From 2d4aba9b79df22e91eddea1f0ef5d34d57b123b4 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Tue, 5 Jun 2018 22:37:41 +0000 Subject: [PATCH 01/36] added xes file reading --- R/server/readData.R | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/R/server/readData.R b/R/server/readData.R index 245f283..e0275d1 100644 --- a/R/server/readData.R +++ b/R/server/readData.R @@ -4,7 +4,7 @@ ############################## # limit what files to accept on input -fileTypes <- c("text/csv","text/comma-separated-values,text/plain",".csv") +fileTypes <- c("text/csv","text/comma-separated-values,text/plain",".csv",".xes") ########################## # Tab Output Definitions # @@ -14,7 +14,7 @@ fileTypes <- c("text/csv","text/comma-separated-values,text/plain",".csv") output$fileSelector <- renderUI({ tags$div( align = "center", - fileInput("inputFile","Please select a .csv file",accept=fileTypes) + fileInput("inputFile","Please select a .csv or .xes file",accept=fileTypes) ) }) @@ -46,10 +46,31 @@ output$dataFilter <- DT::renderDataTable( # return dataframe of occurences parseInputData <- function(inputFile){ - withProgress(message = "Cleaning Data", value = 0,{ - - # read in the table of occurrences - fileRows <- read.csv(inputFile$datapath) + withProgress(message = "Reading and cleaning Data", value = 0,{ + + # Check if this is an xes file + fileType= tools::file_ext(inputFile$datapath) + print(paste('fileType=',fileType)) + if (fileType=='xes') + { + print(paste('in fileType=xes=',fileType)) + # read in the table of occurrences + fileRows=as.data.frame(read_xes(inputFile$datapath)) + print(head(fileRows)) + if (any(match(colnames(fileRows),"timestamp"))) { + + # rename column as tStamp + colnames(fileRows)[colnames(fileRows)=="timestamp"] <- "tStamp" + + # move tStamp to the first column + fileRows=fileRows[c('tStamp', setdiff(names(fileRows), 'tStamp'))] + print(head(fileRows)) + } + else {return(NULL)} + } + else + { # read in the table of occurrences + fileRows <- read.csv(inputFile$datapath) } incProgress(1/3) From ae9325847d5a5852128546e1465b21fe97f87ee7 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 00:14:29 +0000 Subject: [PATCH 02/36] Fixed bug with unconnected nodes causing error --- R/ThreadNet_Core.R | 12 ++++++++---- R/ThreadNet_Misc.R | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index ddb3304..64ae571 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -320,9 +320,10 @@ ThreadOccByPOV <- function(o,THREAD_CF,EVENT_CF){ incProgress(4/n) - # this will store the event map in the GlobalEventMappings and return events with network cluster added for zooming... - # e=clusterEvents(occ, 'OneToOne', 'Network Proximity', THREAD_CF, EVENT_CF,'threads') - e=clusterEvents(occ, '', 'Network Proximity', THREAD_CF, EVENT_CF,'threads') + # return events with network cluster added for zooming... + # print('assign label to ZM_1') + # e$ZM_1 = e$label + e=clusterEvents(occ, '', 'Network Proximity', THREAD_CF, EVENT_CF,'threads') # sort them by threadnum and seqnum e = e[order(e[['threadNum']],e[['seqNum']]),] @@ -689,7 +690,6 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF #' @export clusterEvents <- function(e, NewMapName, cluster_method, thread_CF, event_CF,what_to_return='POV'){ - # make sure to cluster on the correct column (one that exists...) if (cluster_method=="Sequential similarity") { dd = dist_matrix_seq(e) } @@ -708,6 +708,10 @@ clusterEvents <- function(e, NewMapName, cluster_method, thread_CF, event_CF,wha # print(paste('in cluster_events, then, focalCol=',focalCol)) dd = dist_matrix_network(e,focalCol) } + # if there are NA or NaN values, replace then with numbers 10x as big as the largest + dd[is.na(dd)] = max(dd[!is.na(dd)])*10 + dd[is.infinite(dd)] = max(dd[!is.infinite(dd)])*10 + ### cluster the elements clust = hclust( dd, method="ward.D2" ) diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index be906a2..ccd6885 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -8,6 +8,9 @@ ## Make an example data frame for display... +#' @description presents example data when the input data is bad +#' @return DF with some data +#' @export make_example_DF = function(){ correct_occ = read.table(text="tStamp actor action object location '2017-4-7 17:52:04' jimmy tosses ball playground @@ -476,6 +479,9 @@ dual_window_correlation <- function(e,w,s=1,n=2){ # Make a nice dataframe to display # Issue is that DT::renderdatatable cannot display lists correctly. +#' @description Removes columns that do not need to be displayed +#' @param e data frame with POV +#' @export make_nice_event_DT <- function(e){ # Add new column for the occurrences as a character string for display @@ -497,6 +503,7 @@ make_nice_event_DT <- function(e){ # find the biggest column with ZM_, and then get the number that goes with that. # It will not be the same as the column number. +#' @description Used to set upper limit on sliders for zooming #' @param event data frame #' @return biggest zoom level #' @export From 796b02280aa0ea54078526c8563f5874d5455b53 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 00:32:41 +0000 Subject: [PATCH 03/36] Took out debug statements --- Inst/ThreadNet/server/readData.R | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Inst/ThreadNet/server/readData.R b/Inst/ThreadNet/server/readData.R index e0275d1..af6bf3c 100644 --- a/Inst/ThreadNet/server/readData.R +++ b/Inst/ThreadNet/server/readData.R @@ -50,13 +50,11 @@ parseInputData <- function(inputFile){ # Check if this is an xes file fileType= tools::file_ext(inputFile$datapath) - print(paste('fileType=',fileType)) if (fileType=='xes') { - print(paste('in fileType=xes=',fileType)) # read in the table of occurrences fileRows=as.data.frame(read_xes(inputFile$datapath)) - print(head(fileRows)) + if (any(match(colnames(fileRows),"timestamp"))) { # rename column as tStamp @@ -64,7 +62,6 @@ parseInputData <- function(inputFile){ # move tStamp to the first column fileRows=fileRows[c('tStamp', setdiff(names(fileRows), 'tStamp'))] - print(head(fileRows)) } else {return(NULL)} } From 29259d853810d63b3a177cd293a46049ac705d24 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Tue, 5 Jun 2018 21:03:01 -0400 Subject: [PATCH 04/36] Keep tar.gz files in github --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 683f090..5868570 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ # Example code in package build process *-Ex.R # Output files from R CMD build -/*.tar.gz +# /*.tar.gz # Output files from R CMD check /*.Rcheck/ # RStudio files From 02bfb786658f5c696832029749374e39971e34ed Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Tue, 5 Jun 2018 21:10:03 -0400 Subject: [PATCH 05/36] Added package tarball --- ThreadNet.Rproj | 1 + ThreadNet_0.03.tar.gz | Bin 0 -> 47232 bytes 2 files changed, 1 insertion(+) create mode 100644 ThreadNet_0.03.tar.gz diff --git a/ThreadNet.Rproj b/ThreadNet.Rproj index 497f8bf..270314b 100644 --- a/ThreadNet.Rproj +++ b/ThreadNet.Rproj @@ -18,3 +18,4 @@ StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace diff --git a/ThreadNet_0.03.tar.gz b/ThreadNet_0.03.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3524a542d01800e191b96256b56cb9e13611e6d4 GIT binary patch literal 47232 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%0AZ#`{OFa8To+yRGMPu;FCUtb~osg$R+j0n&C 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 Date: Wed, 6 Jun 2018 11:32:31 -0400 Subject: [PATCH 06/36] Changed author/maintainer --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 96ab584..169899c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,9 +2,9 @@ Package: ThreadNet Type: Package Title: ThreadNet Version: 0.03 -Author: Brian T. Pentland, Michigan State University -Maintainer: Brian T. Pentland -Description: ThreadNet is a tool for exploratory sequential data analysis. It converts threads into networks and provides some simple visualizations. +Author: Brian T. Pentland, Michigan State University +Maintainer: Brian T. Pentland +Description: ThreadNet is a Shiny App for exploratory analysis and visualization of sequential data. It converts threads into networks and provides some simple visualizations. License: GPL (>= 2) Encoding: UTF-8 LazyData: true From 80c64ba10984601fadcc7567a707a5c2353e608c Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 11:44:22 -0400 Subject: [PATCH 07/36] cleaned up gitignore --- .gitignore | 2 +- ThreadNet_0.03.tar.gz | Bin 47232 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 ThreadNet_0.03.tar.gz diff --git a/.gitignore b/.gitignore index 5868570..683f090 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ # Example code in package build process *-Ex.R # Output files from R CMD build -# /*.tar.gz +/*.tar.gz # Output files from R CMD check /*.Rcheck/ # RStudio files diff --git a/ThreadNet_0.03.tar.gz b/ThreadNet_0.03.tar.gz deleted file mode 100644 index 3524a542d01800e191b96256b56cb9e13611e6d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47232 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%0AZ#`{OFa8To+yRGMPu;FCUtb~osg$R+j0n&C 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 Date: Wed, 6 Jun 2018 11:48:47 -0400 Subject: [PATCH 08/36] Removed library command from ThreadNet_graphics --- R/ThreadNet_Graphics.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index 71c30ec..b336867 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -8,7 +8,7 @@ # graphic functions used in Shiny App. # some plotly, but some from other packages -library(plotly) + ###### Pie charts for context factors #### # It would be nice to display some other helpful information, perhaps (like the % of possible combinations that occur) From 701ad1e3f2e6ea764f594e6c4a8726a3bc8f25be Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 11:55:29 -0400 Subject: [PATCH 09/36] exported window correlation functions --- R/ThreadNet_Misc.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index ccd6885..bbdcca9 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -325,6 +325,11 @@ get_moving_window <- function(e, s, l ){ # w = window size # s = step (how far to move the window in each step) # n is the ngram size +#' @param e data fraom for POV +#' @param w width of moving window +#' @param s step - how far to move window in each increment (default is 1) +#' @param n number of windows (default is 2) +#' @export window_correlation <- function(e,w,s=1,n=2){ # make data frame From 9f80700e74f6ec581820c441bff3389e741584a9 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 11:55:55 -0400 Subject: [PATCH 10/36] Expoerted window correlations functions --- R/ThreadNet_Misc.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index bbdcca9..fd0d52a 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -407,6 +407,11 @@ window_correlation <- function(e,w,s=1,n=2){ # s = step (how far to move the window in each step) # n is the ngram size # similar as above, except one window on each side of a focal thread. +#' @param e data fraom for POV +#' @param w width of moving window +#' @param s step - how far to move window in each increment (default is 1) +#' @param n number of windows (default is 2) +#' @export dual_window_correlation <- function(e,w,s=1,n=2){ # make data frame From 48458f7c4232e51c635ba8d5274846d29765d4dc Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:00:19 -0400 Subject: [PATCH 11/36] Testing how @name works --- R/ThreadNet_Graphics.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index b336867..8f7ca5b 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -19,7 +19,7 @@ #' and also the number of levels when the factors are combined #' #' @family ThreadNet_Graphics -#' +#' @name CF_multi_pie #' @param oc data frame of occurrences #' @param CF list of contextual factors (columns) to include in the display #' From acf6fe7edec7cb4c4a749af74524f7b33be134da Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:04:21 -0400 Subject: [PATCH 12/36] Moved library commands from UI.R to Global.R --- Inst/ThreadNet/global.R | 6 ++++-- Inst/ThreadNet/ui.R | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index 01f157a..0265b9e 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -11,19 +11,21 @@ library(shiny) library(plotly) +library(ggplot2) library(tidyverse) library(ngram) library(stringr) library(stringdist) -library(ggplot2) +library(igraph) library(networkD3) +library(visNetwork) library(xesreadR) library(colorspace) -library(igraph) library(DT) library(RColorBrewer) library(lubridate) library(knitr) +library(shinyjs) # visualization types for UI dropdowns diff --git a/Inst/ThreadNet/ui.R b/Inst/ThreadNet/ui.R index e70f0eb..63c4d67 100644 --- a/Inst/ThreadNet/ui.R +++ b/Inst/ThreadNet/ui.R @@ -10,10 +10,10 @@ # May 2, 2018 Separation of tab definitions into files # pdf(NULL) # prevent plotly errors -library(shiny) -library(shinyjs) -library(networkD3) -library(visNetwork) +# library(shiny) +# library(shinyjs) +# library(networkD3) +# library(visNetwork) ui <- fluidPage( From 75d860dbc1b02d3a43d41fe93acf1f551c6a7774 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:14:57 -0400 Subject: [PATCH 13/36] Added @name to roxygen2 in event_mappings --- NAMESPACE | 4 ++++ R/Event_Mappings.R | 29 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 7be0832..c57afec 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,7 @@ export(compute_entropy) export(convert_TN_to_TramineR) export(count_ngrams) export(delete_POV) +export(dual_window_correlation) export(dualmovingWindowCorrelation) export(estimate_network_complexity) export(estimate_task_complexity_index) @@ -33,6 +34,8 @@ export(get_POV_THREAD_CF) export(get_POV_name_list) export(get_moving_window) export(get_threadList) +export(make_example_DF) +export(make_nice_event_DT) export(movingWindowCorrelation) export(ng_bar_chart) export(normalNetwork) @@ -44,4 +47,5 @@ export(threadMap) export(threadSizeTable) export(threadTrajectory) export(threads_to_network_original) +export(window_correlation) export(zoom_upper_limit) diff --git a/R/Event_Mappings.R b/R/Event_Mappings.R index 8f90e74..9f79dc5 100644 --- a/R/Event_Mappings.R +++ b/R/Event_Mappings.R @@ -5,11 +5,11 @@ #' map names in memory and forces the creation of a new name. #' #' @family Event_mappings -#' +#' @name check_POV_name #' @param mapname name of map attempting to be created #' @return TRUE or FALSE #' -#' @export check_POV_name +#' @export check_POV_name <- function(mapname){ if (mapname %in% get_POV_name_list()){ @@ -21,6 +21,7 @@ check_POV_name <- function(mapname){ } #' @family Event_mappings +#' @name get_POV_name_list #' @return List of POV names #' #' @export get_POV_name_list @@ -33,6 +34,7 @@ get_POV_name_list <- function(){ } #' @family Event_mappings +#' @name store_POV #' @param EventMapName name of map attempting to be created #' @param e data frame with POV to be stored #' @param thread_CF List of CFs to be stored @@ -40,7 +42,7 @@ get_POV_name_list <- function(){ #' #' @return None, updates global variables #' -#' @export store_POV +#' @export store_POV <- function(EventMapName, e, thread_CF, event_CF){ @@ -62,10 +64,11 @@ store_POV <- function(EventMapName, e, thread_CF, event_CF){ } #' @family Event_mappings +#' @name get_POV #' @param mapname name of POV map #' @return data frame with POV #' -#' @export get_POV +#' @export get_POV <- function(mapname){ idx <- which(mapname==get_POV_name_list() ) @@ -79,10 +82,11 @@ get_POV <- function(mapname){ } #' @family Event_mappings +#' @name get_POV_THREAD_CF #' @param mapname name of POV map #' @return thread CFs for that POV #' -#' @export get_POV_THREAD_CF +#' @export get_POV_THREAD_CF <- function(mapname){ idx <- which(mapname==get_POV_name_list() ) @@ -96,10 +100,11 @@ get_POV_THREAD_CF <- function(mapname){ } #' @family Event_mappings +#' @name get_POV_EVENT_CF #' @param mapname name of POV map #' @return event CFs for that POV #' -#' @export get_POV_EVENT_CF +#' @export get_POV_EVENT_CF <- function(mapname){ idx <- which(mapname==get_POV_name_list() ) @@ -114,11 +119,12 @@ get_POV_EVENT_CF <- function(mapname){ # this one compute the list based on the other two #' @family Event_mappings +#' @name get_POV_COMPARISON_CF #' @param mapname name of POV map #' @param CF_list list of other column names #' @return comparison CFs for that POV #' -#' @export get_POV_COMPARISON_CF +#' @export get_POV_COMPARISON_CF <- function(mapname, CF_list){ idx <- which(mapname==get_POV_name_list() ) @@ -132,10 +138,11 @@ get_POV_COMPARISON_CF <- function(mapname, CF_list){ } #' @family Event_mappings +#' @name delete_POV #' @param mapname name of POV map #' @return None, updates global variables #' -#' @export delete_POV +#' @export delete_POV <- function(mapname){ # get the index for the mapname @@ -152,10 +159,11 @@ delete_POV <- function(mapname){ } #' @family Event_mappings +#' @name export_POV #' @param mapname name of POV map #' @return (writes Rdata file) #' -#' @export export_POV +#' @export export_POV <- function(mapname){ # get the nice variable names @@ -173,10 +181,11 @@ export_POV <- function(mapname){ } #' @family Event_mappings +#' @name export_POV_csv #' @param mapname name of POV map #' @return (writes CSV file) #' -#' @export export_POV_csv +#' @export export_POV_csv <- function(mapname){ output = as.data.frame( get_POV(mapname) ) From 51e0583d36896601bd5b725c3b82b6c936f02992 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:21:15 -0400 Subject: [PATCH 14/36] Added visNetwork package to Imports --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 169899c..7003f41 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,4 +9,4 @@ License: GPL (>= 2) Encoding: UTF-8 LazyData: true RoxygenNote: 6.0.1 -Imports: shiny, plotly, tidyverse, ngram, stringr,stringdist,networkD3, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr +Imports: shiny, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr From 6aebba49289b46c6cf18831e950d5c49e40e9aff Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:27:39 -0400 Subject: [PATCH 15/36] added plotly:: to calls to plotly::layout --- R/ThreadNet_Graphics.R | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index 8f7ca5b..d73fece 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -91,7 +91,7 @@ CF_multi_pie <- function(oc,CF){ } pies = pies %>% - layout(showlegend=FALSE, + plotly::layout(showlegend=FALSE, xaxis = list(showgrid = FALSE,zeroline = FALSE, showticklabels = FALSE), yaxis = list(showgrid = FALSE, zeroline = FALSE,showticklabels = FALSE) # , @@ -197,7 +197,7 @@ CF_multi_pie_event <- function(o, e,CF,r, zm){ } pies = pies %>% - layout(showlegend=FALSE, + plotly::layout(showlegend=FALSE, xaxis = list(showgrid = FALSE,zeroline = FALSE, showticklabels = FALSE), yaxis = list(showgrid = FALSE, zeroline = FALSE,showticklabels = FALSE) # , @@ -250,7 +250,7 @@ threadMap <- function(or, TN, timescale, CF, shape){ hoverinfo = "text+x+y", symbol= "line-ew", symbols=shape, showlegend=FALSE) %>% - layout( + plotly::layout( xaxis = list(title = xaxis), yaxis = list(title = knitr::combine_words(get_THREAD_CF(), sep = ", ")) ) @@ -291,7 +291,7 @@ ng_bar_chart <- function(o,TN, CF, n, mincount){ ngBars = ngdf[ngdf$freq>=mincount,] ngp <- plot_ly( ngBars, x = ~ngrams, y = ~freq, type = "bar",showlegend=FALSE) %>% - layout(xaxis= list(showticklabels = FALSE, title=paste0(n,"-grams of ",CF, " that occur > ",mincount," times"))) + plotly::layout(xaxis= list(showticklabels = FALSE, title=paste0(n,"-grams of ",CF, " that occur > ",mincount," times"))) return(ngp) } @@ -303,8 +303,8 @@ ng_bar_chart_freq <- function(ngdf){ ngdf$ngrams = factor(ngdf$ngrams, levels =unique(ngdf$ngrams)[order(ngdf$freq, decreasing = FALSE)]) ngp <- plot_ly( ngdf, y = ~ngrams, x = ~freq, type = "bar",showlegend=FALSE) %>% - layout(xaxis= list(showticklabels = TRUE, title='Frequency')) %>% - layout(yaxis= list(showticklabels = FALSE, title='')) + plotly::layout(xaxis= list(showticklabels = TRUE, title='Frequency')) %>% + plotly::layout(yaxis= list(showticklabels = FALSE, title='')) return(ngp) } @@ -368,7 +368,7 @@ eventNetwork <- function(et, TN, CF, timesplit){ ), text = n$nodeDF$label, key = n$nodeDF$label, hoverinfo = "text", source = 'A') - p <- layout( + p <- plotly::layout( network, title = title_phrase, shapes = edge_shapes, @@ -661,7 +661,7 @@ threadTrajectory <- function(or){ hoverinfo = "text", showlegend=FALSE) %>% - layout( + plotly::layout( xaxis = list(title='Relative time'), yaxis = list(title='Sequence') )) @@ -679,7 +679,7 @@ movingWindowCorrelation <- function( trace ){ symbol= "line-ew", symbols=15, showlegend=FALSE ) %>% - layout( + plotly::layout( xaxis = list(title='Window number'), yaxis = list(title='Correlation', range = c(0, 1), @@ -704,7 +704,7 @@ dualmovingWindowCorrelation <- function( trace ){ symbol= "line-ew", symbols=15, showlegend=FALSE #,height = 200 ) %>% - layout( + plotly::layout( xaxis = list(title='Window number'), yaxis = list(title='Correlation', range = c(0, 1), From efd060bf523dfe02f45935fc3b38105db5f7dd86 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 12:46:16 -0400 Subject: [PATCH 16/36] Changed roxygen comments in graphics to see if it fixes the warnings... --- R/ThreadNet_Graphics.R | 48 ++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index d73fece..d608159 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -18,7 +18,6 @@ #' When selecting contextual factors that define threads, events and comparisons, this function provide visual feedback about the number of factors levels #' and also the number of levels when the factors are combined #' -#' @family ThreadNet_Graphics #' @name CF_multi_pie #' @param oc data frame of occurrences #' @param CF list of contextual factors (columns) to include in the display @@ -133,6 +132,15 @@ make_df_for_one_pie <- function(o,e,cf,r,zm){ # zoom level as an integer (so you can grab it from the slider) # r = row number or cluster number. Should be the number on the event # z = integer for zoom column +#' @name CF_multi_pie_event +#' @param o data frame of raw occurrences (for the names) +#' @param e data frame with events +#' @param CF list of contextual factors (columns) to include in the display +#' @param r row number of cluster (the number of the event node) +#' @param zm integer for zoom column +#' @return plotyly pie charts (one or more) +#' +#' @export CF_multi_pie_event <- function(o, e,CF,r, zm){ # avoid unpleasant error messages @@ -213,8 +221,8 @@ CF_multi_pie_event <- function(o, e,CF,r, zm){ #' #' Creates a plotly chart of threads in either clock time or event time, depending on the timescale parameter. #' -#' @family ThreadNet_Graphics #' +#' @name threadMap #' @param or Dataframe of threads #' @param TN name of column with thread number #' @param timescale name of column that will be used to plot x-axis of events. It can be the can be the time stamp (for clock time) or the sequence number (for event time) @@ -263,14 +271,12 @@ threadMap <- function(or, TN, timescale, CF, shape){ #' #' Shows the n-grams within a set of threads (but not splitting across threads). This provides a visual indication of how repetitive the threads are. #' -#' @family ThreadNet_Graphics -#' +#' @name ng_bar_chart #' @param o a dataframe of occurrences or events #' @param TN the column that contains the threadNum #' @param CF the contextual factor within which to count the n-grams #' @param n the length of the ngram #' @param mincount the minimum count to display -#' #' @return plotly object #' @export ng_bar_chart <- function(o,TN, CF, n, mincount){ @@ -315,8 +321,7 @@ ng_bar_chart_freq <- function(ngdf){ #' #' Should be replaced with a more expressive layout in plotly #' -#' @family ThreadNet_Graphics -#' +#' @name eventNetwork #' @param et dataframe with the threads to be graphed #' @param TN the column with the threadNumber #' @param CF is the contetual factors (column) @@ -324,7 +329,6 @@ ng_bar_chart_freq <- function(ngdf){ #' #' @return plotly object #' @export - eventNetwork <- function(et, TN, CF, timesplit){ n <- threads_to_network(et, TN, CF, timesplit) @@ -385,10 +389,8 @@ eventNetwork <- function(et, TN, CF, timesplit){ #' NetworkD3 layout for event network #' -#' @family ThreadNet_Graphics -#' +#' @name forceNetworkD3 #' @param n = list with data frames for nodes and edges -#' #' @return networkD3 object #' @export forceNetworkD3 <- function(n){ @@ -412,8 +414,7 @@ forceNetworkD3 <- function(n){ #' Produce a set set of comparison sub-plots in an array. Ideally, we should be able to use any of the plots. So far it is only bar charts. #' This is a prototype that could use rather extensive redesign... #' -#' @family ThreadNet_Graphics -#' +#' @name Comparison_Plots #' @param e dataframe with threads to be plotted #' @param o dataframe with the original data #' @param CF contextul factors @@ -421,7 +422,6 @@ forceNetworkD3 <- function(n){ #' @param nTimePeriods how many time periods to divide the data? #' @param plot_type a type of plotly plot with a function written #' @param role_map_cfs context factors for the role map plot -#' #' @return plotly object, including subplots #' @export Comparison_Plots <- function(e, o, CF, CF_levels, nTimePeriods=1, plot_type,role_map_cfs){ @@ -494,12 +494,10 @@ Comparison_Plots <- function(e, o, CF, CF_levels, nTimePeriods=1, plot_type,rol # Basic Network layout # accepts the data stucture with nodeDF and edgeDF created by threads_to_network and normalNetwork -#' @family ThreadNet_Graphics -#' +#' @name circleVisNetwork #' @param n list with nodeDF and edgeDF dataframes #' @param directed type of network = directed or not #' @param showTitle - show the title or not -#' #' @return visnetwork object #' @export circleVisNetwork <- function( n,directed='directed', showTitle=FALSE ){ @@ -539,12 +537,10 @@ if (directed =='directed') # e is any set of events # vcf is the context factor to graph as network for that set of events # l is the set of labels = factor levels of original data for that VCF -#' @family ThreadNet_Graphics -#' +#' @name normalNetwork #' @param e event data frame #' @param o occurrence data frame #' @param cf context factor for the graph -#' #' @return visnetwork object #' @export normalNetwork <- function(e, o, cf){ @@ -587,8 +583,7 @@ normalNetwork <- function(e, o, cf){ return(list(nodeDF = nodes, edgeDF = as.data.frame(edges) )) } -#' @family ThreadNet_Graphics -#' +#' @name filter_network_edges #' @param n network list of nodeDF and edgeDF #' @param threshold numeric threshold for filtering edges. #' @return n network list of nodeDF and edgeDF @@ -606,12 +601,10 @@ filter_network_edges <- function(n, threshold){ # role map will show "who does what" for any set of events # cfs contains a list of two contextual factors. -#' @family ThreadNet_Graphics -#' +#' @name role_map #' @param e event data frame #' @param o occurrence data frame #' @param cf context factor for the graph -#' #' @return plotly heat map #' @export role_map <- function(e, o, cfs){ @@ -642,8 +635,7 @@ role_map <- function(e, o, cfs){ # this shows relative time versus sequential time # Inspired by Gergen and Danner-Schroeder -#' @family ThreadNet_Graphics -#' +#' @name threadTrajectory #' @param or event data frame #' @return plotly scatter plot #' @export @@ -667,6 +659,7 @@ threadTrajectory <- function(or){ )) } +#' @name movingWindowCorrelation #' @param trace list of (x,y) coordinates #' @return plotly scatter plot #' @export @@ -692,6 +685,7 @@ movingWindowCorrelation <- function( trace ){ showticklabels = TRUE)) ) } +#' @name dualmovingWindowCorrelation #' @param trace list of (x,y) coordinates #' @return plotly scatter plot #' @export From b6f0a0c8b71f67670998045b3e352233e303d65a Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 14:29:07 -0400 Subject: [PATCH 17/36] Lots of little changes to the roxygen documentation --- NAMESPACE | 11 +- R/Event_Mappings.R | 44 +++--- R/ThreadNet.R | 5 +- R/ThreadNet_Batch.R | 138 ------------------- R/ThreadNet_Core.R | 49 ++++--- R/ThreadNet_Graphics.R | 190 +++++++++++++------------- R/ThreadNet_Metrics.R | 25 ++-- R/ThreadNet_Misc.R | 107 +++++++++------ man/CF_multi_pie.Rd | 9 -- man/CF_multi_pie_event.Rd | 25 ++++ man/Comparison_Plots.Rd | 9 -- man/OccToEvents_By_Chunk.Rd | 6 - man/ThreadOccByPOV.Rd | 6 - man/cfnames.Rd | 8 -- man/check_POV_name.Rd | 12 +- man/circleVisNetwork.Rd | 21 +++ man/combineContextFactors.Rd | 8 -- man/compression_index.Rd | 6 - man/compute_entropy.Rd | 6 - man/convert_TN_to_TramineR.Rd | 8 -- man/count_ngrams.Rd | 7 - man/delete_POV.Rd | 17 +++ man/dualmovingWindowCorrelation.Rd | 18 +++ man/estimate_network_complexity.Rd | 6 - man/estimate_task_complexity_index.Rd | 6 - man/eventNetwork.Rd | 32 ----- man/export_POV.Rd | 17 +++ man/export_POV_csv.Rd | 17 +++ man/filter_network_edges.Rd | 19 +++ man/forceNetworkD3.Rd | 14 +- man/get_CF_levels.Rd | 8 -- man/get_POV.Rd | 17 +++ man/get_POV_COMPARISON_CF.Rd | 19 +++ man/get_POV_EVENT_CF.Rd | 17 +++ man/get_POV_THREAD_CF.Rd | 17 +++ man/get_POV_name_list.Rd | 14 ++ man/get_moving_window.Rd | 8 -- man/get_threadList.Rd | 8 -- man/movingWindowCorrelation.Rd | 18 +++ man/ng_bar_chart.Rd | 10 -- man/normalNetwork.Rd | 21 +++ man/numThreads.Rd | 9 -- man/routineness_metric.Rd | 6 - man/store_POV.Rd | 23 ++++ man/threadMap.Rd | 12 +- man/threadSizeTable.Rd | 8 -- man/threadTrajectory.Rd | 18 +++ man/threads_to_network_original.Rd | 6 - 48 files changed, 526 insertions(+), 559 deletions(-) delete mode 100644 R/ThreadNet_Batch.R create mode 100644 man/CF_multi_pie_event.Rd create mode 100644 man/circleVisNetwork.Rd create mode 100644 man/delete_POV.Rd create mode 100644 man/dualmovingWindowCorrelation.Rd delete mode 100644 man/eventNetwork.Rd create mode 100644 man/export_POV.Rd create mode 100644 man/export_POV_csv.Rd create mode 100644 man/filter_network_edges.Rd create mode 100644 man/get_POV.Rd create mode 100644 man/get_POV_COMPARISON_CF.Rd create mode 100644 man/get_POV_EVENT_CF.Rd create mode 100644 man/get_POV_THREAD_CF.Rd create mode 100644 man/get_POV_name_list.Rd create mode 100644 man/movingWindowCorrelation.Rd create mode 100644 man/normalNetwork.Rd create mode 100644 man/store_POV.Rd create mode 100644 man/threadTrajectory.Rd diff --git a/NAMESPACE b/NAMESPACE index c57afec..fa63f37 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ export(ACHR_batch_V1) export(CF_multi_pie) +export(CF_multi_pie_event) export(Comparison_Plots) export(OccToEvents3) export(OccToEvents_By_Chunk) @@ -17,11 +18,12 @@ export(compute_entropy) export(convert_TN_to_TramineR) export(count_ngrams) export(delete_POV) +export(diff_handoffs) +export(diff_tStamp) export(dual_window_correlation) export(dualmovingWindowCorrelation) export(estimate_network_complexity) export(estimate_task_complexity_index) -export(eventNetwork) export(export_POV) export(export_POV_csv) export(filter_network_edges) @@ -36,10 +38,13 @@ export(get_moving_window) export(get_threadList) export(make_example_DF) export(make_nice_event_DT) +export(make_subsets) export(movingWindowCorrelation) +export(newColName) export(ng_bar_chart) export(normalNetwork) export(numThreads) +export(plot_entropy) export(role_map) export(routineness_metric) export(store_POV) @@ -47,5 +52,9 @@ export(threadMap) export(threadSizeTable) export(threadTrajectory) export(threads_to_network_original) +export(threshold_slider_max) +export(threshold_slider_min) +export(threshold_slider_selected) export(window_correlation) +export(zoomColumn) export(zoom_upper_limit) diff --git a/R/Event_Mappings.R b/R/Event_Mappings.R index 9f79dc5..8b5e960 100644 --- a/R/Event_Mappings.R +++ b/R/Event_Mappings.R @@ -1,14 +1,12 @@ ##################################################### # Global_POV is a global variable -#' Checks the name attempting to be create against the list of +#' @title check_POV_name +#' @description Checks the name attempting to be create against the list of #' map names in memory and forces the creation of a new name. -#' -#' @family Event_mappings #' @name check_POV_name #' @param mapname name of map attempting to be created #' @return TRUE or FALSE -#' #' @export check_POV_name <- function(mapname){ @@ -20,10 +18,10 @@ check_POV_name <- function(mapname){ return(existingMap) } -#' @family Event_mappings +#' @title get_POV_name_list +#' @description Get list of POV names for all of the dropdown boxes on the UI #' @name get_POV_name_list #' @return List of POV names -#' #' @export get_POV_name_list get_POV_name_list <- function(){ @@ -33,19 +31,17 @@ get_POV_name_list <- function(){ return(n) } -#' @family Event_mappings +#' @title store_POV +#' @description Stores the POV and context factors #' @name store_POV #' @param EventMapName name of map attempting to be created #' @param e data frame with POV to be stored #' @param thread_CF List of CFs to be stored #' @param event_CF List of CFs to be stored -#' #' @return None, updates global variables -#' #' @export store_POV <- function(EventMapName, e, thread_CF, event_CF){ - # print(paste('in store_POV, EventMapName=',EventMapName)) # print(paste('in store_POV, thread_CF=',thread_CF)) # print(paste('in store_POV, event_CF=',event_CF)) @@ -63,11 +59,11 @@ store_POV <- function(EventMapName, e, thread_CF, event_CF){ } } -#' @family Event_mappings +#' @title get_POV +#' @description Gets the data frame for the POV #' @name get_POV #' @param mapname name of POV map #' @return data frame with POV -#' #' @export get_POV <- function(mapname){ @@ -81,11 +77,11 @@ get_POV <- function(mapname){ } } -#' @family Event_mappings +#' @title get_POV_THREAD_CF +#' @description Gets the CFs that define threads in this POV #' @name get_POV_THREAD_CF #' @param mapname name of POV map #' @return thread CFs for that POV -#' #' @export get_POV_THREAD_CF <- function(mapname){ @@ -99,11 +95,11 @@ get_POV_THREAD_CF <- function(mapname){ } } -#' @family Event_mappings +#' @title get_POV_EVENT_CF +#' @description Gets the CFs that define events in this POV #' @name get_POV_EVENT_CF #' @param mapname name of POV map #' @return event CFs for that POV -#' #' @export get_POV_EVENT_CF <- function(mapname){ @@ -118,12 +114,12 @@ get_POV_EVENT_CF <- function(mapname){ } # this one compute the list based on the other two -#' @family Event_mappings +#' @title get_POV_COMPARISON_CF +#' @description Gets the CFs that can be used for comparisons in this POV #' @name get_POV_COMPARISON_CF #' @param mapname name of POV map #' @param CF_list list of other column names #' @return comparison CFs for that POV -#' #' @export get_POV_COMPARISON_CF <- function(mapname, CF_list){ @@ -137,11 +133,11 @@ get_POV_COMPARISON_CF <- function(mapname, CF_list){ } } -#' @family Event_mappings +#' @title delete_POV +#' @description Deletes all the data assocated with this POV #' @name delete_POV #' @param mapname name of POV map #' @return None, updates global variables -#' #' @export delete_POV <- function(mapname){ @@ -158,11 +154,11 @@ delete_POV <- function(mapname){ } -#' @family Event_mappings +#' @title export_POV +#' @description Exports the data assocated with this POV as Rdata #' @name export_POV #' @param mapname name of POV map #' @return (writes Rdata file) -#' #' @export export_POV <- function(mapname){ @@ -180,11 +176,11 @@ export_POV <- function(mapname){ } -#' @family Event_mappings +#' @title export_POV_csv +#' @description Exports the data assocated with this POV as CSV #' @name export_POV_csv #' @param mapname name of POV map #' @return (writes CSV file) -#' #' @export export_POV_csv <- function(mapname){ diff --git a/R/ThreadNet.R b/R/ThreadNet.R index d05e54f..3fe3546 100644 --- a/R/ThreadNet.R +++ b/R/ThreadNet.R @@ -1,4 +1,5 @@ -#' @family ThreadNet_Core -#' +#' @title ThreadNet_Core +#' @description This function launches the Shiny App called ThreadNet +#' @name ThreadNet_Core #' @export ThreadNet ThreadNet <- function() { shiny::runApp(system.file('ThreadNet', package='ThreadNet')) } diff --git a/R/ThreadNet_Batch.R b/R/ThreadNet_Batch.R deleted file mode 100644 index 863cb5c..0000000 --- a/R/ThreadNet_Batch.R +++ /dev/null @@ -1,138 +0,0 @@ -########################################################################################################## -# THREADNET: Batch processing for larger data sets - -# (c) 2017 Michigan State University. This software may be used according to the terms provided in the -# GNU General Public License (GPL-3.0) https://opensource.org/licenses/GPL-3.0? -# Absolutely no warranty! -########################################################################################################## - - -# Take a large number of patient visits and create a data frame where each row contains -# a set of variables that describes a set of one or more visits. - -# This is just a test -#' Batch processing for larger numbers of threads -#' -#' ACHR stands for Antecedents of Complexity in Healthcare Routines. This is function is set up to compute process parameters on thousands of patient visits. -#' -#' @param inFileName name of file (CSV format) containing the raw thread data. -#' -#' @return data frame ready for further analysis -#' -#' @export -ACHR_batch_V1 <- function(inFileName){ - - -# first read in the csv -rawOcc = fread(inFileName) - - -# HARD-CODED COLUMNS!! Should probably pass in as a parameters -TN = "threadID" -CFs = c("role","workstn","action") -DV= newColName(CFs) - - -# clean up the ocurrences, add week and month columns -occ = cleanOcc(rawOcc,CFs) - -# make threads - this will also make a new column that combines the CFs -threadedOcc <- ThreadOccByPOV(occ,TN,CFs) - -# may want to make threads with and without different CFs to define events, as well - -# pick subsets -- typically just one thread at a time, but could be more -# write a function for this -criteria <-"threadID" -bucket_list <- make_buckets(threadedOcc, criteria) - -# get the size (number of buckets) -N = length(bucket_list) - -# pre-allocate the data.table. Tables are supposed to be faster. -ACHR = data.table(bucket=integer(N), - NEvents = integer(N), - NDiagnoses = integer(N), - NProcedures = integer(N), - visitStartTime = numeric(N), # might need special data type for time - VisitDuration=numeric(N), # might need special data type for time - NetComplexity=double(N), - CompressRatio = double(N), - Clinic = character(N), - PrimaryDiagnosis = character(N), - PayerType = character(N), - Provider = character(N) # might not be available - ) - -# Now add columns for the IVs. There will be three for each IV - -# Add the IV columns -for (cf in CFs){ - - ACHR[, paste0(cf,"_count"):= double(N)] - ACHR[, paste0(cf,"_compression"):= double(N)] - ACHR[, paste0(cf,"_entropy"):= double(N)] - -} - -# loop through the buckets. Result will be data frame with one row per bucket -for (i in 1:N){ - - b = i # as.integer(bucket_list[i]) - - # select the threads that go in this bucket - df = threadedOcc[threadedOcc[[TN]] ==bucket_list[i],] - - # bucket number - ACHR[b,bucket := b] - - # length of the thread (number of rows) - ACHR[b,NEvents := nrow(df)] - - # only do the computations if there are more than two occurrences - if (nrow(df) > 2) { - - # compressibility of DV - ACHR[b,CompressRatio := compression_index(df,DV)] - - # NetComplexity of DV - # First get the network - n = threads_to_network(df,TN, DV) - ACHR[b,NetComplexity := estimate_network_complexity( n )] - - # compute stuff on each context factor - for (cf in CFs){ - - # Count the unique elements in each cf - ACHR[b, paste0(cf,"_count") := length(unique(df[[cf]])) ] - - # get the compression - ACHR[b, paste0(cf,"_compression") := compression_index(df,cf) ] - - # get the entropy - ACHR[b, paste0(cf,"_entropy") := compute_entropy(table(df[[cf]])[table(df[[cf]])>0]) ] - - } -} # kf nrows > 2 - -} # loop thru buckets - -# return the table -return(ACHR) -} - - -# Each bucket is a list of thread numbers that can be used to subset the list of occurrences -make_buckets <- function(o, criteria){ - - return( levels(o[[criteria]]) ) - -} - - -make_box_plots <- function(){ -ggboxplot(ACHR_test[NEvents>100 & Clinic=='DRH'], x = "VisitMonth", y = "NetComplexity", - color = "VisitDay", - ylab = "Complexity", xlab = "Month (DRH)") -} - diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index 64ae571..c581300 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -8,19 +8,15 @@ # These are the basic functions that convert threads to networks, etc. -#' Converts threads to network -#' -#' Converts a sequentially ordered streams of ;events (threads) and creates a unimodal, unidimensional network. +#' @title Converts threads to network +#' @description Converts a sequentially ordered streams of ;events (threads) and creates a unimodal, unidimensional network. #' Sequentially adjacent pairs of events become edges in the resulting network. -#' @family ThreadNet_Core -#' +#' @name threads_to_network_original #' @param et dataframe containing threads #' @param TN name of column in dataframe that contains a unique thread number for each thread #' @param CF name of the column in dataframe that contains the events that will form the nodes of the network #' @param grp grouping variable for coloring the nodes -#' #' @return a list containing two dataframes, one for the nodes (nodeDF) and one for the edges (edgeDF) -#' #' @export threads_to_network_original # here is a version without all the position stuff, which should be separated out, if possible. # Added in the "group" for the network graphics - default group is 'threadNum' because it will always be there @@ -154,15 +150,14 @@ threads_to_network_original <- function(et,TN,CF,grp='threadNum'){ #' Counts ngrams in a set of threads #' #' This function counts n-grams within threads where the length of the thread is greater than n. -#' @family ThreadNet_Core -#' +#' @title +#' @description +#' @name count_ngrams #' @param o dataframe containing threads #' @param TN name of column in dataframe that contains a unique thread number for each thread #' @param CF name of the column in dataframe that contains the events that will form the nodes of the network #' @param n length of ngrams to count -#' #' @return a dataframe with ngram, frequency and proportion in descending order -#' #' @export count_ngrams <- function(o,TN,CF,n){ @@ -189,14 +184,13 @@ count_ngrams <- function(o,TN,CF,n){ #' #' Take the raw occurrences from the input file and sort them by time stamp within #' a set of contextual factors that remain constant for each thread. -#' @family ThreadNet_Core -#' +#' @title +#' @description +#' @name ThreadOccByPOV #' @param o is the dataframe of cleaned ocurrences #' @param THREAD_CF is a list of 1 or more context factors that define the threads (and stay constant during each thread) #' @param EVENT_CF is a list of 1 or more context factors that define events (and change during threads) -#' #' @return dataframe containing the same occurrences sorted from a different point of view -#' #'@export ThreadOccByPOV <- function(o,THREAD_CF,EVENT_CF){ @@ -349,8 +343,9 @@ print('done converting occurrences...') #' #' Thus function provides a place to map occurrences into events, so is is not necessary to interpret individual #' occurrences in isolation. There are many ways to accomplish this mapping. -#' @family ThreadNet_Core -#' +#' @title +#' @description +#' @name OccToEvents_By_Chunk #' @param o a dataframe of occurrences #' @param m = method parameter = one of c('Variable chunks','Uniform chunks') #' @param EventMapName = used to store this mapping for visualization and comparison @@ -361,9 +356,7 @@ print('done converting occurrences...') #' @param thread_CF - context factors used to delineate threads #' @param event_CF - context factors used to define events #' @param compare_CF = context factors used for comparison -- need to be copied over here when the thread is created. -#' #' @return event data frame, with occurrences aggregated into events. -#' #' @export OccToEvents_By_Chunk <- function(o, m, EventMapName, uniform_chunk_size, tThreshold, timescale='mins', chunk_CF, thread_CF, event_CF, compare_CF){ @@ -505,7 +498,9 @@ OccToEvents_By_Chunk <- function(o, m, EventMapName, uniform_chunk_size, tThresh # this one creates events based on frequent ngrams or regular expressions -#' @family ThreadNet_Core +#' @title +#' @description +#' @name OccToEvents3 #' @param o a dataframe of occurrences #' @param EventMapName = used to store this mapping for visualization and comparison #' @param uniform_chunk_size = used to identify breakpoints -- from input slider @@ -519,9 +514,7 @@ OccToEvents_By_Chunk <- function(o, m, EventMapName, uniform_chunk_size, tThresh #' @param CF context factor #' @param rx list of patterns #' @param KeepIrregularEvents = keep or drop events that don't fit patterns -#' #' @return event data frame, with occurrences aggregated into events. -#' #' @export OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF, rx, KeepIrregularEvents){ @@ -675,7 +668,9 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF # e is the event list # EventMapName is an input selected from the list of available mappings # cluster_method is either "Sequential similarity" or "Contextual Similarity" or "Network Structure" -#' @family ThreadNet_Core +#' @title +#' @description +#' @name clusterEvents #' @param e a dataframe of events or occurrences #' @param NewMapName = used to store this mapping for visualization and comparison #' @param cluster_method = method for clustering @@ -684,9 +679,7 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF #' @param compare_CF = context factors used for comparison -- need to be copied over here when the thread is created. #' @param TN ThreadNum #' @param what_to_return POV or Cluster solution - #' @return event data frame with occurrences aggregated into events or cluster solution -#' #' @export clusterEvents <- function(e, NewMapName, cluster_method, thread_CF, event_CF,what_to_return='POV'){ @@ -756,6 +749,12 @@ clusterEvents <- function(e, NewMapName, cluster_method, thread_CF, event_CF,wha {return(newmap)} } +############################################################################# +############################################################################# +## LOCAL FUNCTIONS from here on down +############################################################################# +############################################################################# + # this function pulls computes their similarity of chunks based on sequence # these functions are only used locally dist_matrix_seq <- function(e){ diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index d608159..19283e5 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -13,17 +13,13 @@ ###### Pie charts for context factors #### # It would be nice to display some other helpful information, perhaps (like the % of possible combinations that occur) -#' Creates pie charts for one or more contextual factors -#' -#' When selecting contextual factors that define threads, events and comparisons, this function provide visual feedback about the number of factors levels +#' @title Creates pie charts for one or more contextual factors +#' @description When selecting contextual factors that define threads, events and comparisons, this function provide visual feedback about the number of factors levels #' and also the number of levels when the factors are combined -#' #' @name CF_multi_pie #' @param oc data frame of occurrences #' @param CF list of contextual factors (columns) to include in the display -#' #' @return plotyly pie charts (one or more) -#' #' @export CF_multi_pie <- function(oc,CF){ @@ -132,6 +128,8 @@ make_df_for_one_pie <- function(o,e,cf,r,zm){ # zoom level as an integer (so you can grab it from the slider) # r = row number or cluster number. Should be the number on the event # z = integer for zoom column +#' @title CF_multi_pie_event +#' @description Make multi-pie for click event from force network layout. Generate a small plot of context factor pie charts when you click on a node in the graph #' @name CF_multi_pie_event #' @param o data frame of raw occurrences (for the names) #' @param e data frame with events @@ -139,7 +137,6 @@ make_df_for_one_pie <- function(o,e,cf,r,zm){ #' @param r row number of cluster (the number of the event node) #' @param zm integer for zoom column #' @return plotyly pie charts (one or more) -#' #' @export CF_multi_pie_event <- function(o, e,CF,r, zm){ @@ -217,21 +214,16 @@ CF_multi_pie_event <- function(o, e,CF,r, zm){ ###################################################################### # ThreadMap shows the threads in a horizongal layout -#' Shows threads in a horizontal layout -#' -#' Creates a plotly chart of threads in either clock time or event time, depending on the timescale parameter. -#' -#' +#' @title threadMap shows threads in a horizontal layout +#' @description Creates a plotly chart of threads in either clock time or event time, depending on the timescale parameter. #' @name threadMap #' @param or Dataframe of threads #' @param TN name of column with thread number #' @param timescale name of column that will be used to plot x-axis of events. It can be the can be the time stamp (for clock time) or the sequence number (for event time) #' @param CF name of contextual factor that will determine the colors #' @param shape shape code for the markers on the threadmap -#' #' @return plotly object #' @export -#' threadMap <- function(or, TN, timescale, CF, shape){ @@ -267,10 +259,8 @@ threadMap <- function(or, TN, timescale, CF, shape){ ################################################ -#' Create an ngram bar chart -#' -#' Shows the n-grams within a set of threads (but not splitting across threads). This provides a visual indication of how repetitive the threads are. -#' +#' @title Create an ngram bar chart +#' @description Shows the n-grams within a set of threads (but not splitting across threads). This provides a visual indication of how repetitive the threads are. #' @name ng_bar_chart #' @param o a dataframe of occurrences or events #' @param TN the column that contains the threadNum @@ -317,78 +307,78 @@ ng_bar_chart_freq <- function(ngdf){ ############################################################################# -#' Circular network layout for event network (USES visnetwork) -#' -#' Should be replaced with a more expressive layout in plotly -#' -#' @name eventNetwork -#' @param et dataframe with the threads to be graphed -#' @param TN the column with the threadNumber -#' @param CF is the contetual factors (column) -#' @param timesplit time measure -#' -#' @return plotly object -#' @export -eventNetwork <- function(et, TN, CF, timesplit){ - - n <- threads_to_network(et, TN, CF, timesplit) - - - title_phrase = paste("Estimated complexity index =",estimate_network_complexity(n)) - - edge_shapes <- list() - for(i in 1:length(n$edgeDF$from)) { - E <- n$edgeDF[i,] - - edge_shape = list( - type = "line", - line = list(color = "#030303", width = 0), - x0 = E[['from_x']], - x1 = E[['to_x']], - y0 = E[['from_y']], - y1 = E[['to_y']], - xref = "x", - yref = "y" - ) - - edge_shapes[[i]] <- edge_shape - } - - x <- list( - title = 'Average Time' - ) - - y <- list( - title = 'Frequency' - ) - color_pal = colorRampPalette(brewer.pal(11,'Spectral')) - size_pal = (n$nodeDF$y_pos-min(n$nodeDF$y_pos))/(max(n$nodeDF$y_pos)-min(n$nodeDF$y_pos))*15+10 - network <- plot_ly(x = ~n$nodeDF$x_pos, y = ~n$nodeDF$y_pos, - width = 0, - mode = "markers", - marker = list(size= size_pal, - color=color_pal(100)[as.numeric(cut(n$nodeDF$x_pos, breaks=100))] - - ), - text = n$nodeDF$label, key = n$nodeDF$label, hoverinfo = "text", source = 'A') - - p <- plotly::layout( - network, - title = title_phrase, - shapes = edge_shapes, - xaxis = x, - yaxis = y - ) - return(p) +# @title Circular network layout for event network (USES visnetwork) +# +# Should be replaced with a more expressive layout in plotly +# +# @name eventNetwork +# @param et dataframe with the threads to be graphed +# @param TN the column with the threadNumber +# @param CF is the contetual factors (column) +# @param timesplit time measure +# @return plotly object +# @export +# eventNetwork <- function(et, TN, CF, timesplit){ +# +# n <- threads_to_network(et, TN, CF, timesplit) +# +# +# title_phrase = paste("Estimated complexity index =",estimate_network_complexity(n)) +# +# edge_shapes <- list() +# for(i in 1:length(n$edgeDF$from)) { +# E <- n$edgeDF[i,] +# +# edge_shape = list( +# type = "line", +# line = list(color = "#030303", width = 0), +# x0 = E[['from_x']], +# x1 = E[['to_x']], +# y0 = E[['from_y']], +# y1 = E[['to_y']], +# xref = "x", +# yref = "y" +# ) +# +# edge_shapes[[i]] <- edge_shape +# } +# +# x <- list( +# title = 'Average Time' +# ) +# +# y <- list( +# title = 'Frequency' +# ) +# color_pal = colorRampPalette(brewer.pal(11,'Spectral')) +# size_pal = (n$nodeDF$y_pos-min(n$nodeDF$y_pos))/(max(n$nodeDF$y_pos)-min(n$nodeDF$y_pos))*15+10 +# network <- plot_ly(x = ~n$nodeDF$x_pos, y = ~n$nodeDF$y_pos, +# width = 0, +# mode = "markers", +# marker = list(size= size_pal, +# color=color_pal(100)[as.numeric(cut(n$nodeDF$x_pos, breaks=100))] +# +# ), +# text = n$nodeDF$label, key = n$nodeDF$label, hoverinfo = "text", source = 'A') +# +# p <- plotly::layout( +# network, +# title = title_phrase, +# shapes = edge_shapes, +# xaxis = x, +# yaxis = y +# ) +# return(p) +# +# } -} ################################################################ ## Here is the networkD3 version of the same thing. # it has a bunch of extra code because of the groups... # needs to be re-written to separate computation of the network from the layout... -#' NetworkD3 layout for event network -#' +#' @title forceNetworkD3 is an Interactive layout for event network +#' @description This produces a force layout network using networkD3 #' @name forceNetworkD3 #' @param n = list with data frames for nodes and edges #' @return networkD3 object @@ -400,7 +390,6 @@ forceNetworkD3 <- function(n){ n$edgeDF['from'] = n$edgeDF['from']-1 n$edgeDF['to'] = n$edgeDF['to']-1 - return( forceNetwork(Links = n$edgeDF, Nodes = n$nodeDF, Source = "from", Target = "to", Value = "Value", NodeID = "label", Group = "Group", opacity = 1, zoom = T,arrows=TRUE, bounded = FALSE, @@ -409,11 +398,9 @@ forceNetworkD3 <- function(n){ ###################################################################################### -#' Comparison plots -#' -#' Produce a set set of comparison sub-plots in an array. Ideally, we should be able to use any of the plots. So far it is only bar charts. +#' @title Comparison plots +#' @description Produce a set set of comparison sub-plots in an array. Ideally, we should be able to use any of the plots. So far it is only bar charts. #' This is a prototype that could use rather extensive redesign... -#' #' @name Comparison_Plots #' @param e dataframe with threads to be plotted #' @param o dataframe with the original data @@ -494,6 +481,8 @@ Comparison_Plots <- function(e, o, CF, CF_levels, nTimePeriods=1, plot_type,rol # Basic Network layout # accepts the data stucture with nodeDF and edgeDF created by threads_to_network and normalNetwork +#' @title circleVisNetwork +#' @description Produces a circle network layout using visNetwork #' @name circleVisNetwork #' @param n list with nodeDF and edgeDF dataframes #' @param directed type of network = directed or not @@ -537,6 +526,8 @@ if (directed =='directed') # e is any set of events # vcf is the context factor to graph as network for that set of events # l is the set of labels = factor levels of original data for that VCF +#' @title normalNetwork +#' @description Produced a network of co-occurrences for any given CF and displays it in a visNetwork circle layout #' @name normalNetwork #' @param e event data frame #' @param o occurrence data frame @@ -569,8 +560,6 @@ normalNetwork <- function(e, o, cf){ diag(a) = 0 a=a/max(a) - - # print(a) g=graph_from_adjacency_matrix(a, mode='undirected', weighted=TRUE) @@ -583,6 +572,8 @@ normalNetwork <- function(e, o, cf){ return(list(nodeDF = nodes, edgeDF = as.data.frame(edges) )) } +#' @title filter_network_edges +#' @description Filters out network edges with weight below the threshold #' @name filter_network_edges #' @param n network list of nodeDF and edgeDF #' @param threshold numeric threshold for filtering edges. @@ -599,8 +590,8 @@ filter_network_edges <- function(n, threshold){ return(n) } -# role map will show "who does what" for any set of events -# cfs contains a list of two contextual factors. +#' @name role_map +#' @description A role map (like a heat map) that will show "who does what" for any set of events #' @name role_map #' @param e event data frame #' @param o occurrence data frame @@ -633,8 +624,10 @@ role_map <- function(e, o, cfs){ } -# this shows relative time versus sequential time -# Inspired by Gergen and Danner-Schroeder + +#'@title threadTrajectory +#' @description Create a plotly diagram showing relative time versus sequential time. +#' Inspired by Gergen and Danner-Schroeder #' @name threadTrajectory #' @param or event data frame #' @return plotly scatter plot @@ -659,6 +652,9 @@ threadTrajectory <- function(or){ )) } +#' @title movingWindowCorrelation +#' @description Creates plotly diagram showing correlation of moving windows across time +#' In this version, the moving window can overlap with itself as it slides #' @name movingWindowCorrelation #' @param trace list of (x,y) coordinates #' @return plotly scatter plot @@ -685,6 +681,10 @@ movingWindowCorrelation <- function( trace ){ showticklabels = TRUE)) ) } + +#' @title dualmovingWindowCorrelation +#' @description Creates plotly diagram showing correlation of moving windows across time +#' This version computes the correlation of TWO adjacent windows that never overlap #' @name dualmovingWindowCorrelation #' @param trace list of (x,y) coordinates #' @return plotly scatter plot diff --git a/R/ThreadNet_Metrics.R b/R/ThreadNet_Metrics.R index b067184..2977572 100644 --- a/R/ThreadNet_Metrics.R +++ b/R/ThreadNet_Metrics.R @@ -14,10 +14,8 @@ #' This function takes a network descripts (nodes and edges, as generaged by the functino threads_to_network, and estimates the number of paths. #' as described in Haerem, Pentland and Miller (2015). The estimate correlates with the McCabe's (1975) cyclometric complexity. #' -#' @family ThreadNet_Metrics -#' +#' @name estimate_network_complexity #' @param net Object with dataframe for nodes and edges -#' #' @return number #' @export estimate_network_complexity <- function(net){ return(estimate_task_complexity_index( nrow(net$nodeDF), nrow(net$edgeDF)) ) } @@ -27,11 +25,9 @@ estimate_network_complexity <- function(net){ return(estimate_task_complexity_in #' #' Same as estimate_network_complexity, but takes different parameters #' -#' @family ThreadNet_Metrics -#' +#' @name estimate_task_complexity_index #' @param v number of vertices (or nodes) #' @param e number of edges -#' #' @return number #' @export estimate_task_complexity_index <- function(v,e){ @@ -61,16 +57,13 @@ estimate_task_complexity_index <- function(v,e){ #' Computes the fraction of observed behavior that conforms to an observed pattern. #' Current version uses ngrams, but it would be good to use spmf pattern mining to avoid including duplicate patterns (e.g., a-b-c and b-c-d) #' -#' @family ThreadNet_Metrics -#' +#' @name routineness_metric #' @param o data frame with occurresnces or events #' @param TN name of column with threadNumbers #' @param CF name of column with contextual factor #' @param n size of ngram #' @param m how many of the most frequent ngrams to include. When m > 1, there is a risk of duplication. -#' #' @return number, index of routineness. -#' #' @export routineness_metric <- function(o,TN,CF,n,m){ @@ -90,11 +83,9 @@ routineness_metric <- function(o,TN,CF,n,m){ #' Compressibility is an index of complexity -- more compressible means less complex. This function computes the ratio of compressed data #' to the original data. Should be between zero and one. Uses built-in functions for in=memory compression #' -#' @family ThreadNet_Metrics -#' +#' @name compression_index #' @param df a data frame containing occurrences or events #' @param CF a column or contextual factor in that data frame -#' #' @return number containing compressibility index, 0 < i < 1 #' @export compression_index <- function(df,CF){ return( @@ -110,10 +101,8 @@ compression_index <- function(df,CF){ return( #' #' Each column in the raw data represents a contextual factor. This function computes the entropy of each factor that is selected for use in the #' analysis. -#' @family ThreadNet_Metrics -#' +#' @name compute_entropy #' @param freq is the frequency distribution of the levels in the factor -#' #' @return number #' @export compute_entropy <- function(freq){ @@ -125,6 +114,10 @@ compute_entropy <- function(freq){ # code to plot entropy as a function of zoom_level # need to get the zoom levels -- grep out the 'Z_' column names... +#' @name plot_entropy +#' @param e data from of events with zoom levels +#' @return R plot +#' @export plot_entropy <- function(e){ plot(unlist(lapply(grep('ZM_',colnames(e)),function(i){compute_entropy(table(e[[i]]))}))) } diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index fd0d52a..d93cdbf 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -8,7 +8,7 @@ ## Make an example data frame for display... -#' @description presents example data when the input data is bad +#' @name make_example_DF #' @return DF with some data #' @export make_example_DF = function(){ @@ -26,10 +26,9 @@ make_example_DF = function(){ #' #' Threads must have unique thred numbers for this function to work #' -#' @family ThreadNet_Misc +#' @name numThreads #' @param o data frame with occurrences or events #' @param TN column with thread number -#' #' @return number of threads #' @export numThreads = function(o,TN) {length(unique(o[[TN]]))} @@ -50,12 +49,21 @@ timeRangePhrase = function(tr){ # this function is used to split up the threads into n ~equal buckets +#' @name make_subsets +#' @param d data frame with occurrences or events +#' @param n number of buckets +#' @return list of smaller data frames +#' @export make_subsets <- function(d,n){ return(split(d, ceiling(seq_along(d)/(length(d)/n)))) } # This function takes a slider value and returns a valid column name for zooming # if the argument is null, then use ZM_1 +#' @name zoomColumn +#' @param z integer for the zoom level +#' @return column name for that zoom level ('ZM_n) +#' @export zoomColumn <- function(z){ # print(paste("In zoomColumn z=",z)) @@ -75,25 +83,21 @@ zoomColumn <- function(z){ # grab all of the columns except the first, which has the time stamp # tStamp in the first column #' cfnames provides names of all the contextual factors (except the time stamp) -#' @family ThreadNet_Misc +#' @name cfnames #' @param o data frame with threads -#' #' @return list of column names #' @export -#' cfnames <- function(o){ colnames(o)[2:length(colnames(o))]} ## this is used to populate the UI for comparison of categories within a CF #' get_CF_levels returns the levels of a contextual factor -#' @family ThreadNet_Misc +#' @name get_CF_levels #' @param o data frame with threads #' @param cf a contextual factors (column) -#' #' @return list of unique factor levels #' @export get_CF_levels <- function(o,cf){ - return(levels(o[,cf])) } @@ -103,16 +107,14 @@ get_CF_levels <- function(o,cf){ #' #' For example, actor+action #' -#' @family ThreadNet_Misc +#' @name combineContextFactors #' @param o data frame with threads #' @param CF contextual factors to be combined. #' @param newCol name of the new combined conextual factor -#' #' @return data frame with the new column #' @export combineContextFactors <- function(o,CF,newCol){ - # Use the old column if there is one if (!(newCol %in% names(o))) { @@ -132,19 +134,34 @@ combineContextFactors <- function(o,CF,newCol){ } # just keep this simple +#' @name newColName +#' @param CF_list list of context factors to define events +#' @return column name +#' @export newColName <- function(CF_list){ return(paste0(CF_list,collapse="_")) } # These were used on the occ-to-event tab to configure the slider +#' @name threshold_slider_min +#' @param o dataframe of occurrences +#' @return slider min +#' @export threshold_slider_min <- function(o){ return(floor(min(o$timeGap))) } +#' @name threshold_slider_max +#' @param o dataframe of occurrences +#' @return slider max +#' @export threshold_slider_max <- function(o){ return(ceiling(max(o$timeGap))) } - +#' @name threshold_slider_selected +#' @param o dataframe of occurrences +#' @return selected value = min +#' @export threshold_slider_selected <- function(o){ return(min(o$timeGap)) } @@ -153,6 +170,10 @@ threshold_slider_selected <- function(o){ #### count the handoffs, but reverse coded -- zero = all different +#' @name diff_handoffs +#' @param o dataframe of occurrences +#' @return o dataframe of occurrences with handoff count filled in +#' @export diff_handoffs <- function(o){ # initialize the previous row @@ -175,6 +196,10 @@ row_diff_handoff <- function(this_row){ #### Time gaps -- just pass in the column of time stamps +#' @name diff_tStamp +#' @param o dataframe of occurrences +#' @return o dataframe of occurrences with handoff count filled in +#' @export diff_tStamp <- function(ts){ # initialize the first row @@ -190,7 +215,7 @@ row_diff_tStamp <- function(this_row){ # just add up the differences. - d <-max(0,difftime(this_row, previous_row, units="secs")) + d <-max(0,difftime(this_row, previous_row, units='auto')) # store the previous row previous_row <<-this_row @@ -207,11 +232,9 @@ row_diff_tStamp <- function(this_row){ #' This function should work on either ocurrences or events. #' it returns length and duration of each thread.It requires tStamp field to compute duration. #' -#' @family ThreadNet_Misc -#' +#' @name threadSizeTable #' @param o data frame with threads #' @param TN column comtaining the threadNumber -#' #' @return data frame with table of thread lengths #' @export threadSizeTable <- function(o,TN){ @@ -245,10 +268,9 @@ threadSizeTable <- function(o,TN){ #' #' converts the csv format used in ThreadNet to the format used by TraMiner. Should provide a way to save this, as well. #' -#' @family ThreadNet_Misc +#' @name convert_TN_to_TramineR #' @param df threads (occurrences or events) #' @param CF Contextual factor that will be used to define the state sequences in TraMineR -#' #' @return Dataframe in TraMineR format (state sequeces in horizontal rows) #' @export convert_TN_to_TramineR <- function(df, CF){ @@ -283,12 +305,10 @@ convert_TN_to_TramineR <- function(df, CF){ # these functions support the moving window #' get_threadList returns a list of all thread numbers #' -#' @family ThreadNet_Misc -#' +#' @name get_threadList #' @param e data frame with threaded events #' @param TN Column with threadNumber #' @param SN Column with sequence numbers -#' #' @return list of thread numbers #' @export get_threadList <- function(e,TN,SN){ @@ -299,12 +319,10 @@ get_threadList <- function(e,TN,SN){ #' get_moving_window returns a set of threads for a moving window #' -#' @family ThreadNet_Misc -#' +#' @name get_moving_window #' @param e data frame with threads (needs to have threadNum and seqNum) #' @param s size of window #' @param l location of window -#' #' @return data from with just the threads in the window #' @export get_moving_window <- function(e, s, l ){ @@ -325,6 +343,7 @@ get_moving_window <- function(e, s, l ){ # w = window size # s = step (how far to move the window in each step) # n is the ngram size +#' @name window_correlation #' @param e data fraom for POV #' @param w width of moving window #' @param s step - how far to move window in each increment (default is 1) @@ -407,6 +426,7 @@ window_correlation <- function(e,w,s=1,n=2){ # s = step (how far to move the window in each step) # n is the ngram size # similar as above, except one window on each side of a focal thread. +#' @name dual_window_correlation #' @param e data fraom for POV #' @param w width of moving window #' @param s step - how far to move window in each increment (default is 1) @@ -489,6 +509,7 @@ dual_window_correlation <- function(e,w,s=1,n=2){ # Make a nice dataframe to display # Issue is that DT::renderdatatable cannot display lists correctly. +#' @name make_nice_event_DT #' @description Removes columns that do not need to be displayed #' @param e data frame with POV #' @export @@ -513,6 +534,7 @@ make_nice_event_DT <- function(e){ # find the biggest column with ZM_, and then get the number that goes with that. # It will not be the same as the column number. +#' @name zoom_upper_limit #' @description Used to set upper limit on sliders for zooming #' @param event data frame #' @return biggest zoom level @@ -531,25 +553,22 @@ zoom_upper_limit <- function(e){ ###################################################### # Just putting this code here to play with for now. # this function finds the common events in two subsets of thread data -common_events <- function(ss1, ss2, TN, CF, n){ - - # get the list of ngrams for each subset of threads - e1 = count_ngrams(ss1, TN, CF, n)[1] - e2 = count_ngrams(ss2, TN, CF, n)[1] - - # return the intersection - return(intersect(as.matrix(e1), as.matrix(e2))) - -} - -rr_grams <- function(o,TN, CF, N, R) { - # N - max length of ngram - # R = threshold for repetition - - - - -} +# common_events <- function(ss1, ss2, TN, CF, n){ +# +# # get the list of ngrams for each subset of threads +# e1 = count_ngrams(ss1, TN, CF, n)[1] +# e2 = count_ngrams(ss2, TN, CF, n)[1] +# +# # return the intersection +# return(intersect(as.matrix(e1), as.matrix(e2))) +# +# } +# +# rr_grams <- function(o,TN, CF, N, R) { +# # N - max length of ngram +# # R = threshold for repetition +# +# } # Ideas for regex work # https://stackoverflow.com/questions/35704369/identify-repetitive-pattern-in-numeric-vector-in-r-with-fuzzy-search diff --git a/man/CF_multi_pie.Rd b/man/CF_multi_pie.Rd index 014e461..ba96e2b 100644 --- a/man/CF_multi_pie.Rd +++ b/man/CF_multi_pie.Rd @@ -18,12 +18,3 @@ plotyly pie charts (one or more) When selecting contextual factors that define threads, events and comparisons, this function provide visual feedback about the number of factors levels and also the number of levels when the factors are combined } -\seealso{ -Other ThreadNet_Graphics: \code{\link{Comparison_Plots}}, - \code{\link{circleVisNetwork}}, - \code{\link{eventNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{forceNetworkD3}}, \code{\link{ng_bar_chart}}, - \code{\link{normalNetwork}}, \code{\link{role_map}}, - \code{\link{threadMap}}, \code{\link{threadTrajectory}} -} diff --git a/man/CF_multi_pie_event.Rd b/man/CF_multi_pie_event.Rd new file mode 100644 index 0000000..ba802b6 --- /dev/null +++ b/man/CF_multi_pie_event.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{CF_multi_pie_event} +\alias{CF_multi_pie_event} +\title{CF_multi_pie_event} +\usage{ +CF_multi_pie_event(o, e, CF, r, zm) +} +\arguments{ +\item{o}{data frame of raw occurrences (for the names)} + +\item{e}{data frame with events} + +\item{CF}{list of contextual factors (columns) to include in the display} + +\item{r}{row number of cluster (the number of the event node)} + +\item{zm}{integer for zoom column} +} +\value{ +plotyly pie charts (one or more) +} +\description{ +Make multi-pie for click event from force network layout. Generate a small plot of context factor pie charts when you click on a node in the graph +} diff --git a/man/Comparison_Plots.Rd b/man/Comparison_Plots.Rd index 9b74b57..ed5e4bc 100644 --- a/man/Comparison_Plots.Rd +++ b/man/Comparison_Plots.Rd @@ -29,12 +29,3 @@ plotly object, including subplots Produce a set set of comparison sub-plots in an array. Ideally, we should be able to use any of the plots. So far it is only bar charts. This is a prototype that could use rather extensive redesign... } -\seealso{ -Other ThreadNet_Graphics: \code{\link{CF_multi_pie}}, - \code{\link{circleVisNetwork}}, - \code{\link{eventNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{forceNetworkD3}}, \code{\link{ng_bar_chart}}, - \code{\link{normalNetwork}}, \code{\link{role_map}}, - \code{\link{threadMap}}, \code{\link{threadTrajectory}} -} diff --git a/man/OccToEvents_By_Chunk.Rd b/man/OccToEvents_By_Chunk.Rd index 64e251a..f7e9989 100644 --- a/man/OccToEvents_By_Chunk.Rd +++ b/man/OccToEvents_By_Chunk.Rd @@ -35,9 +35,3 @@ event data frame, with occurrences aggregated into events. Thus function provides a place to map occurrences into events, so is is not necessary to interpret individual occurrences in isolation. There are many ways to accomplish this mapping. } -\seealso{ -Other ThreadNet_Core: \code{\link{OccToEvents3}}, - \code{\link{ThreadOccByPOV}}, - \code{\link{clusterEvents}}, \code{\link{count_ngrams}}, - \code{\link{threads_to_network_original}} -} diff --git a/man/ThreadOccByPOV.Rd b/man/ThreadOccByPOV.Rd index e92040c..2551820 100644 --- a/man/ThreadOccByPOV.Rd +++ b/man/ThreadOccByPOV.Rd @@ -20,9 +20,3 @@ dataframe containing the same occurrences sorted from a different point of view Take the raw occurrences from the input file and sort them by time stamp within a set of contextual factors that remain constant for each thread. } -\seealso{ -Other ThreadNet_Core: \code{\link{OccToEvents3}}, - \code{\link{OccToEvents_By_Chunk}}, - \code{\link{clusterEvents}}, \code{\link{count_ngrams}}, - \code{\link{threads_to_network_original}} -} diff --git a/man/cfnames.Rd b/man/cfnames.Rd index 51492d1..42234dd 100644 --- a/man/cfnames.Rd +++ b/man/cfnames.Rd @@ -15,11 +15,3 @@ list of column names \description{ cfnames provides names of all the contextual factors (except the time stamp) } -\seealso{ -Other ThreadNet_Misc: \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, \code{\link{numThreads}}, - \code{\link{threadSizeTable}} -} diff --git a/man/check_POV_name.Rd b/man/check_POV_name.Rd index b9b71bf..011f232 100644 --- a/man/check_POV_name.Rd +++ b/man/check_POV_name.Rd @@ -2,8 +2,7 @@ % Please edit documentation in R/Event_Mappings.R \name{check_POV_name} \alias{check_POV_name} -\title{Checks the name attempting to be create against the list of -map names in memory and forces the creation of a new name.} +\title{check_POV_name} \usage{ check_POV_name(mapname) } @@ -17,12 +16,3 @@ TRUE or FALSE Checks the name attempting to be create against the list of map names in memory and forces the creation of a new name. } -\seealso{ -Other Event_mappings: \code{\link{delete_POV}}, - \code{\link{export_POV_csv}}, \code{\link{export_POV}}, - \code{\link{get_POV_COMPARISON_CF}}, - \code{\link{get_POV_EVENT_CF}}, - \code{\link{get_POV_THREAD_CF}}, - \code{\link{get_POV_name_list}}, \code{\link{get_POV}}, - \code{\link{store_POV}} -} diff --git a/man/circleVisNetwork.Rd b/man/circleVisNetwork.Rd new file mode 100644 index 0000000..32cce84 --- /dev/null +++ b/man/circleVisNetwork.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{circleVisNetwork} +\alias{circleVisNetwork} +\title{circleVisNetwork} +\usage{ +circleVisNetwork(n, directed = "directed", showTitle = FALSE) +} +\arguments{ +\item{n}{list with nodeDF and edgeDF dataframes} + +\item{directed}{type of network = directed or not} + +\item{showTitle}{- show the title or not} +} +\value{ +visnetwork object +} +\description{ +Produces a circle network layout using visNetwork +} diff --git a/man/combineContextFactors.Rd b/man/combineContextFactors.Rd index e6ddfa7..9555dc0 100644 --- a/man/combineContextFactors.Rd +++ b/man/combineContextFactors.Rd @@ -19,11 +19,3 @@ data frame with the new column \description{ For example, actor+action } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, \code{\link{numThreads}}, - \code{\link{threadSizeTable}} -} diff --git a/man/compression_index.Rd b/man/compression_index.Rd index f137121..9e7b4dd 100644 --- a/man/compression_index.Rd +++ b/man/compression_index.Rd @@ -18,9 +18,3 @@ number containing compressibility index, 0 < i < 1 Compressibility is an index of complexity -- more compressible means less complex. This function computes the ratio of compressed data to the original data. Should be between zero and one. Uses built-in functions for in=memory compression } -\seealso{ -Other ThreadNet_Metrics: \code{\link{compute_entropy}}, - \code{\link{estimate_network_complexity}}, - \code{\link{estimate_task_complexity_index}}, - \code{\link{routineness_metric}} -} diff --git a/man/compute_entropy.Rd b/man/compute_entropy.Rd index e02ed31..34cf9af 100644 --- a/man/compute_entropy.Rd +++ b/man/compute_entropy.Rd @@ -16,9 +16,3 @@ number Each column in the raw data represents a contextual factor. This function computes the entropy of each factor that is selected for use in the analysis. } -\seealso{ -Other ThreadNet_Metrics: \code{\link{compression_index}}, - \code{\link{estimate_network_complexity}}, - \code{\link{estimate_task_complexity_index}}, - \code{\link{routineness_metric}} -} diff --git a/man/convert_TN_to_TramineR.Rd b/man/convert_TN_to_TramineR.Rd index 2f5a1b7..13caf01 100644 --- a/man/convert_TN_to_TramineR.Rd +++ b/man/convert_TN_to_TramineR.Rd @@ -17,11 +17,3 @@ Dataframe in TraMineR format (state sequeces in horizontal rows) \description{ converts the csv format used in ThreadNet to the format used by TraMiner. Should provide a way to save this, as well. } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, \code{\link{numThreads}}, - \code{\link{threadSizeTable}} -} diff --git a/man/count_ngrams.Rd b/man/count_ngrams.Rd index 9cd7e01..dea7e2b 100644 --- a/man/count_ngrams.Rd +++ b/man/count_ngrams.Rd @@ -21,10 +21,3 @@ a dataframe with ngram, frequency and proportion in descending order \description{ This function counts n-grams within threads where the length of the thread is greater than n. } -\seealso{ -Other ThreadNet_Core: \code{\link{OccToEvents3}}, - \code{\link{OccToEvents_By_Chunk}}, - \code{\link{ThreadOccByPOV}}, - \code{\link{clusterEvents}}, - \code{\link{threads_to_network_original}} -} diff --git a/man/delete_POV.Rd b/man/delete_POV.Rd new file mode 100644 index 0000000..c5f0d26 --- /dev/null +++ b/man/delete_POV.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{delete_POV} +\alias{delete_POV} +\title{delete_POV} +\usage{ +delete_POV(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +None, updates global variables +} +\description{ +Deletes all the data assocated with this POV +} diff --git a/man/dualmovingWindowCorrelation.Rd b/man/dualmovingWindowCorrelation.Rd new file mode 100644 index 0000000..6a29709 --- /dev/null +++ b/man/dualmovingWindowCorrelation.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{dualmovingWindowCorrelation} +\alias{dualmovingWindowCorrelation} +\title{dualmovingWindowCorrelation} +\usage{ +dualmovingWindowCorrelation(trace) +} +\arguments{ +\item{trace}{list of (x,y) coordinates} +} +\value{ +plotly scatter plot +} +\description{ +Creates plotly diagram showing correlation of moving windows across time +This version computes the correlation of TWO adjacent windows that never overlap +} diff --git a/man/estimate_network_complexity.Rd b/man/estimate_network_complexity.Rd index 5c98af7..729f315 100644 --- a/man/estimate_network_complexity.Rd +++ b/man/estimate_network_complexity.Rd @@ -16,9 +16,3 @@ number This function takes a network descripts (nodes and edges, as generaged by the functino threads_to_network, and estimates the number of paths. as described in Haerem, Pentland and Miller (2015). The estimate correlates with the McCabe's (1975) cyclometric complexity. } -\seealso{ -Other ThreadNet_Metrics: \code{\link{compression_index}}, - \code{\link{compute_entropy}}, - \code{\link{estimate_task_complexity_index}}, - \code{\link{routineness_metric}} -} diff --git a/man/estimate_task_complexity_index.Rd b/man/estimate_task_complexity_index.Rd index f3f90ba..baeb1e9 100644 --- a/man/estimate_task_complexity_index.Rd +++ b/man/estimate_task_complexity_index.Rd @@ -17,9 +17,3 @@ number \description{ Same as estimate_network_complexity, but takes different parameters } -\seealso{ -Other ThreadNet_Metrics: \code{\link{compression_index}}, - \code{\link{compute_entropy}}, - \code{\link{estimate_network_complexity}}, - \code{\link{routineness_metric}} -} diff --git a/man/eventNetwork.Rd b/man/eventNetwork.Rd deleted file mode 100644 index 244e73f..0000000 --- a/man/eventNetwork.Rd +++ /dev/null @@ -1,32 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ThreadNet_Graphics.R -\name{eventNetwork} -\alias{eventNetwork} -\title{Circular network layout for event network (USES visnetwork)} -\usage{ -eventNetwork(et, TN, CF, timesplit) -} -\arguments{ -\item{et}{dataframe with the threads to be graphed} - -\item{TN}{the column with the threadNumber} - -\item{CF}{is the contetual factors (column)} - -\item{timesplit}{time measure} -} -\value{ -plotly object -} -\description{ -Should be replaced with a more expressive layout in plotly -} -\seealso{ -Other ThreadNet_Graphics: \code{\link{CF_multi_pie}}, - \code{\link{Comparison_Plots}}, - \code{\link{circleVisNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{forceNetworkD3}}, \code{\link{ng_bar_chart}}, - \code{\link{normalNetwork}}, \code{\link{role_map}}, - \code{\link{threadMap}}, \code{\link{threadTrajectory}} -} diff --git a/man/export_POV.Rd b/man/export_POV.Rd new file mode 100644 index 0000000..cf1acf0 --- /dev/null +++ b/man/export_POV.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{export_POV} +\alias{export_POV} +\title{export_POV} +\usage{ +export_POV(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +(writes Rdata file) +} +\description{ +Exports the data assocated with this POV as Rdata +} diff --git a/man/export_POV_csv.Rd b/man/export_POV_csv.Rd new file mode 100644 index 0000000..df71ee5 --- /dev/null +++ b/man/export_POV_csv.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{export_POV_csv} +\alias{export_POV_csv} +\title{export_POV_csv} +\usage{ +export_POV_csv(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +(writes CSV file) +} +\description{ +Exports the data assocated with this POV as CSV +} diff --git a/man/filter_network_edges.Rd b/man/filter_network_edges.Rd new file mode 100644 index 0000000..006def7 --- /dev/null +++ b/man/filter_network_edges.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{filter_network_edges} +\alias{filter_network_edges} +\title{filter_network_edges} +\usage{ +filter_network_edges(n, threshold) +} +\arguments{ +\item{n}{network list of nodeDF and edgeDF} + +\item{threshold}{numeric threshold for filtering edges.} +} +\value{ +n network list of nodeDF and edgeDF +} +\description{ +Filters out network edges with weight below the threshold +} diff --git a/man/forceNetworkD3.Rd b/man/forceNetworkD3.Rd index b2e7227..7bb0bea 100644 --- a/man/forceNetworkD3.Rd +++ b/man/forceNetworkD3.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Graphics.R \name{forceNetworkD3} \alias{forceNetworkD3} -\title{NetworkD3 layout for event network} +\title{forceNetworkD3 is an Interactive layout for event network} \usage{ forceNetworkD3(n) } @@ -13,15 +13,5 @@ forceNetworkD3(n) networkD3 object } \description{ -NetworkD3 layout for event network -} -\seealso{ -Other ThreadNet_Graphics: \code{\link{CF_multi_pie}}, - \code{\link{Comparison_Plots}}, - \code{\link{circleVisNetwork}}, - \code{\link{eventNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{ng_bar_chart}}, \code{\link{normalNetwork}}, - \code{\link{role_map}}, \code{\link{threadMap}}, - \code{\link{threadTrajectory}} +This produces a force layout network using networkD3 } diff --git a/man/get_CF_levels.Rd b/man/get_CF_levels.Rd index 28c5aec..a443946 100644 --- a/man/get_CF_levels.Rd +++ b/man/get_CF_levels.Rd @@ -17,11 +17,3 @@ list of unique factor levels \description{ get_CF_levels returns the levels of a contextual factor } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, \code{\link{numThreads}}, - \code{\link{threadSizeTable}} -} diff --git a/man/get_POV.Rd b/man/get_POV.Rd new file mode 100644 index 0000000..0de1bcf --- /dev/null +++ b/man/get_POV.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{get_POV} +\alias{get_POV} +\title{get_POV} +\usage{ +get_POV(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +data frame with POV +} +\description{ +Gets the data frame for the POV +} diff --git a/man/get_POV_COMPARISON_CF.Rd b/man/get_POV_COMPARISON_CF.Rd new file mode 100644 index 0000000..35c875f --- /dev/null +++ b/man/get_POV_COMPARISON_CF.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{get_POV_COMPARISON_CF} +\alias{get_POV_COMPARISON_CF} +\title{get_POV_COMPARISON_CF} +\usage{ +get_POV_COMPARISON_CF(mapname, CF_list) +} +\arguments{ +\item{mapname}{name of POV map} + +\item{CF_list}{list of other column names} +} +\value{ +comparison CFs for that POV +} +\description{ +Gets the CFs that can be used for comparisons in this POV +} diff --git a/man/get_POV_EVENT_CF.Rd b/man/get_POV_EVENT_CF.Rd new file mode 100644 index 0000000..3466873 --- /dev/null +++ b/man/get_POV_EVENT_CF.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{get_POV_EVENT_CF} +\alias{get_POV_EVENT_CF} +\title{get_POV_EVENT_CF} +\usage{ +get_POV_EVENT_CF(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +event CFs for that POV +} +\description{ +Gets the CFs that define events in this POV +} diff --git a/man/get_POV_THREAD_CF.Rd b/man/get_POV_THREAD_CF.Rd new file mode 100644 index 0000000..998416b --- /dev/null +++ b/man/get_POV_THREAD_CF.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{get_POV_THREAD_CF} +\alias{get_POV_THREAD_CF} +\title{get_POV_THREAD_CF} +\usage{ +get_POV_THREAD_CF(mapname) +} +\arguments{ +\item{mapname}{name of POV map} +} +\value{ +thread CFs for that POV +} +\description{ +Gets the CFs that define threads in this POV +} diff --git a/man/get_POV_name_list.Rd b/man/get_POV_name_list.Rd new file mode 100644 index 0000000..63591a1 --- /dev/null +++ b/man/get_POV_name_list.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{get_POV_name_list} +\alias{get_POV_name_list} +\title{get_POV_name_list} +\usage{ +get_POV_name_list() +} +\value{ +List of POV names +} +\description{ +Get list of POV names for all of the dropdown boxes on the UI +} diff --git a/man/get_moving_window.Rd b/man/get_moving_window.Rd index fe45396..30531ac 100644 --- a/man/get_moving_window.Rd +++ b/man/get_moving_window.Rd @@ -19,11 +19,3 @@ data from with just the threads in the window \description{ get_moving_window returns a set of threads for a moving window } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_threadList}}, \code{\link{numThreads}}, - \code{\link{threadSizeTable}} -} diff --git a/man/get_threadList.Rd b/man/get_threadList.Rd index 7c49f30..9b99a4c 100644 --- a/man/get_threadList.Rd +++ b/man/get_threadList.Rd @@ -19,11 +19,3 @@ list of thread numbers \description{ get_threadList returns a list of all thread numbers } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{numThreads}}, \code{\link{threadSizeTable}} -} diff --git a/man/movingWindowCorrelation.Rd b/man/movingWindowCorrelation.Rd new file mode 100644 index 0000000..7eef9cd --- /dev/null +++ b/man/movingWindowCorrelation.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{movingWindowCorrelation} +\alias{movingWindowCorrelation} +\title{movingWindowCorrelation} +\usage{ +movingWindowCorrelation(trace) +} +\arguments{ +\item{trace}{list of (x,y) coordinates} +} +\value{ +plotly scatter plot +} +\description{ +Creates plotly diagram showing correlation of moving windows across time +In this version, the moving window can overlap with itself as it slides +} diff --git a/man/ng_bar_chart.Rd b/man/ng_bar_chart.Rd index 851a3af..bf9772c 100644 --- a/man/ng_bar_chart.Rd +++ b/man/ng_bar_chart.Rd @@ -23,13 +23,3 @@ plotly object \description{ Shows the n-grams within a set of threads (but not splitting across threads). This provides a visual indication of how repetitive the threads are. } -\seealso{ -Other ThreadNet_Graphics: \code{\link{CF_multi_pie}}, - \code{\link{Comparison_Plots}}, - \code{\link{circleVisNetwork}}, - \code{\link{eventNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{forceNetworkD3}}, - \code{\link{normalNetwork}}, \code{\link{role_map}}, - \code{\link{threadMap}}, \code{\link{threadTrajectory}} -} diff --git a/man/normalNetwork.Rd b/man/normalNetwork.Rd new file mode 100644 index 0000000..1d54e02 --- /dev/null +++ b/man/normalNetwork.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{normalNetwork} +\alias{normalNetwork} +\title{normalNetwork} +\usage{ +normalNetwork(e, o, cf) +} +\arguments{ +\item{e}{event data frame} + +\item{o}{occurrence data frame} + +\item{cf}{context factor for the graph} +} +\value{ +visnetwork object +} +\description{ +Produced a network of co-occurrences for any given CF and displays it in a visNetwork circle layout +} diff --git a/man/numThreads.Rd b/man/numThreads.Rd index 03d6126..9b2398c 100644 --- a/man/numThreads.Rd +++ b/man/numThreads.Rd @@ -17,12 +17,3 @@ number of threads \description{ Threads must have unique thred numbers for this function to work } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, - \code{\link{threadSizeTable}} -} diff --git a/man/routineness_metric.Rd b/man/routineness_metric.Rd index 332176e..11d0af5 100644 --- a/man/routineness_metric.Rd +++ b/man/routineness_metric.Rd @@ -24,9 +24,3 @@ number, index of routineness. Computes the fraction of observed behavior that conforms to an observed pattern. Current version uses ngrams, but it would be good to use spmf pattern mining to avoid including duplicate patterns (e.g., a-b-c and b-c-d) } -\seealso{ -Other ThreadNet_Metrics: \code{\link{compression_index}}, - \code{\link{compute_entropy}}, - \code{\link{estimate_network_complexity}}, - \code{\link{estimate_task_complexity_index}} -} diff --git a/man/store_POV.Rd b/man/store_POV.Rd new file mode 100644 index 0000000..049b56f --- /dev/null +++ b/man/store_POV.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Event_Mappings.R +\name{store_POV} +\alias{store_POV} +\title{store_POV} +\usage{ +store_POV(EventMapName, e, thread_CF, event_CF) +} +\arguments{ +\item{EventMapName}{name of map attempting to be created} + +\item{e}{data frame with POV to be stored} + +\item{thread_CF}{List of CFs to be stored} + +\item{event_CF}{List of CFs to be stored} +} +\value{ +None, updates global variables +} +\description{ +Stores the POV and context factors +} diff --git a/man/threadMap.Rd b/man/threadMap.Rd index 046b370..0d82a31 100644 --- a/man/threadMap.Rd +++ b/man/threadMap.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Graphics.R \name{threadMap} \alias{threadMap} -\title{Shows threads in a horizontal layout} +\title{threadMap shows threads in a horizontal layout} \usage{ threadMap(or, TN, timescale, CF, shape) } @@ -23,13 +23,3 @@ plotly object \description{ Creates a plotly chart of threads in either clock time or event time, depending on the timescale parameter. } -\seealso{ -Other ThreadNet_Graphics: \code{\link{CF_multi_pie}}, - \code{\link{Comparison_Plots}}, - \code{\link{circleVisNetwork}}, - \code{\link{eventNetwork}}, - \code{\link{filter_network_edges}}, - \code{\link{forceNetworkD3}}, \code{\link{ng_bar_chart}}, - \code{\link{normalNetwork}}, \code{\link{role_map}}, - \code{\link{threadTrajectory}} -} diff --git a/man/threadSizeTable.Rd b/man/threadSizeTable.Rd index 7e231cf..556d102 100644 --- a/man/threadSizeTable.Rd +++ b/man/threadSizeTable.Rd @@ -18,11 +18,3 @@ data frame with table of thread lengths This function should work on either ocurrences or events. it returns length and duration of each thread.It requires tStamp field to compute duration. } -\seealso{ -Other ThreadNet_Misc: \code{\link{cfnames}}, - \code{\link{combineContextFactors}}, - \code{\link{convert_TN_to_TramineR}}, - \code{\link{get_CF_levels}}, - \code{\link{get_moving_window}}, - \code{\link{get_threadList}}, \code{\link{numThreads}} -} diff --git a/man/threadTrajectory.Rd b/man/threadTrajectory.Rd new file mode 100644 index 0000000..7db6480 --- /dev/null +++ b/man/threadTrajectory.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{threadTrajectory} +\alias{threadTrajectory} +\title{threadTrajectory} +\usage{ +threadTrajectory(or) +} +\arguments{ +\item{or}{event data frame} +} +\value{ +plotly scatter plot +} +\description{ +Create a plotly diagram showing relative time versus sequential time. +Inspired by Gergen and Danner-Schroeder +} diff --git a/man/threads_to_network_original.Rd b/man/threads_to_network_original.Rd index 90821be..f022283 100644 --- a/man/threads_to_network_original.Rd +++ b/man/threads_to_network_original.Rd @@ -22,9 +22,3 @@ a list containing two dataframes, one for the nodes (nodeDF) and one for the edg Converts a sequentially ordered streams of ;events (threads) and creates a unimodal, unidimensional network. Sequentially adjacent pairs of events become edges in the resulting network. } -\seealso{ -Other ThreadNet_Core: \code{\link{OccToEvents3}}, - \code{\link{OccToEvents_By_Chunk}}, - \code{\link{ThreadOccByPOV}}, - \code{\link{clusterEvents}}, \code{\link{count_ngrams}} -} From 4754a9aee2b38144e9b5741e5d3c188e63603f94 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 14:34:26 -0400 Subject: [PATCH 18/36] changes to roxygen docs --- R/ThreadNet_Core.R | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index c581300..d3b7849 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -146,12 +146,9 @@ threads_to_network_original <- function(et,TN,CF,grp='threadNum'){ # return(list(nodeDF = nodes, edgeDF = edges)) # } -# Counting ngrams is essential to several ThreadNet functions -#' Counts ngrams in a set of threads -#' -#' This function counts n-grams within threads where the length of the thread is greater than n. -#' @title -#' @description + +#' @title Counts ngrams in a set of threads +#' @description Counting ngrams is essential to several ThreadNet functions. This function counts n-grams within threads where the length of the thread is greater than n. #' @name count_ngrams #' @param o dataframe containing threads #' @param TN name of column in dataframe that contains a unique thread number for each thread @@ -179,19 +176,15 @@ count_ngrams <- function(o,TN,CF,n){ ################################################################# -# -#' Make new threads from a new POV -#' -#' Take the raw occurrences from the input file and sort them by time stamp within +#' @title Converts occurrences into events, make threads from a new POV +#' @description Take the raw occurrences from the input file and sort them by time stamp within #' a set of contextual factors that remain constant for each thread. -#' @title -#' @description #' @name ThreadOccByPOV #' @param o is the dataframe of cleaned ocurrences #' @param THREAD_CF is a list of 1 or more context factors that define the threads (and stay constant during each thread) #' @param EVENT_CF is a list of 1 or more context factors that define events (and change during threads) #' @return dataframe containing the same occurrences sorted from a different point of view -#'@export +#' @export ThreadOccByPOV <- function(o,THREAD_CF,EVENT_CF){ timescale = get_timeScale() @@ -339,12 +332,11 @@ print('done converting occurrences...') ############################################################################################################## -#' Maps occurrences into events #' -#' Thus function provides a place to map occurrences into events, so is is not necessary to interpret individual -#' occurrences in isolation. There are many ways to accomplish this mapping. -#' @title -#' @description +#' +#' @title Maps occurrences into events by chunks. +#' @description Thus function provides a way to map occurrences into events, so is is not necessary to interpret individual +#' occurrences in isolation. Provides three ways to accomplish this mapping. #' @name OccToEvents_By_Chunk #' @param o a dataframe of occurrences #' @param m = method parameter = one of c('Variable chunks','Uniform chunks') @@ -497,9 +489,9 @@ OccToEvents_By_Chunk <- function(o, m, EventMapName, uniform_chunk_size, tThresh } -# this one creates events based on frequent ngrams or regular expressions -#' @title -#' @description +# +#' @title OccToEvents3 +#' @description Creates events based on frequent ngrams or regular expressions #' @name OccToEvents3 #' @param o a dataframe of occurrences #' @param EventMapName = used to store this mapping for visualization and comparison @@ -664,12 +656,8 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF } -# new function for new tab -# e is the event list -# EventMapName is an input selected from the list of available mappings -# cluster_method is either "Sequential similarity" or "Contextual Similarity" or "Network Structure" -#' @title -#' @description +#' @title Clusters occurrences or eents +#' @description cluster_method is either "Sequential similarity" or "Contextual Similarity" or "Network Structure" #' @name clusterEvents #' @param e a dataframe of events or occurrences #' @param NewMapName = used to store this mapping for visualization and comparison From 2b23e7a4216391ab2970ed655d589974861bdde4 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 14:49:51 -0400 Subject: [PATCH 19/36] More roxygen details... --- NAMESPACE | 1 - R/ThreadNet_Misc.R | 92 +++++++++++++++++++------------------ man/ACHR_batch_V1.Rd | 17 ------- man/OccToEvents3.Rd | 42 +++++++++++++++++ man/OccToEvents_By_Chunk.Rd | 6 +-- man/ThreadNet_Core.Rd | 12 +++++ man/ThreadOccByPOV.Rd | 2 +- man/clusterEvents.Rd | 32 +++++++++++++ man/count_ngrams.Rd | 2 +- 9 files changed, 139 insertions(+), 67 deletions(-) delete mode 100644 man/ACHR_batch_V1.Rd create mode 100644 man/OccToEvents3.Rd create mode 100644 man/ThreadNet_Core.Rd create mode 100644 man/clusterEvents.Rd diff --git a/NAMESPACE b/NAMESPACE index fa63f37..450c7e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,5 @@ # Generated by roxygen2: do not edit by hand -export(ACHR_batch_V1) export(CF_multi_pie) export(CF_multi_pie_event) export(Comparison_Plots) diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index d93cdbf..229ed34 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -7,7 +7,8 @@ ########################################################################################################## -## Make an example data frame for display... +#' @title make_example_DF +#' @description Make an example data frame for display... #' @name make_example_DF #' @return DF with some data #' @export @@ -22,10 +23,9 @@ make_example_DF = function(){ -#' numThreads counts how many threads in the data set -#' -#' Threads must have unique thred numbers for this function to work -#' + +#' @title numThreads counts how many threads in the data set +#' @description Threads must have unique thred numbers for this function to work #' @name numThreads #' @param o data frame with occurrences or events #' @param TN column with thread number @@ -49,6 +49,8 @@ timeRangePhrase = function(tr){ # this function is used to split up the threads into n ~equal buckets +#' @title make_subsets +#' @description this function is used to split up the threads into n ~equal buckets #' @name make_subsets #' @param d data frame with occurrences or events #' @param n number of buckets @@ -58,8 +60,9 @@ make_subsets <- function(d,n){ return(split(d, ceiling(seq_along(d)/(length(d)/n)))) } -# This function takes a slider value and returns a valid column name for zooming -# if the argument is null, then use ZM_1 +#' @title zoomColumn +#' @description This function takes a slider value and returns a valid column name for zooming +#' if the argument is null, then use ZM_1 #' @name zoomColumn #' @param z integer for the zoom level #' @return column name for that zoom level ('ZM_n) @@ -78,11 +81,9 @@ zoomColumn <- function(z){ } ######### Functions that return column names ####### - -# names of the columns for contextual factors -# grab all of the columns except the first, which has the time stamp -# tStamp in the first column -#' cfnames provides names of all the contextual factors (except the time stamp) +#' @title cfnames provides names of all the contextual factors (except the time stamp) +#' @description grab all of the columns except the first, which has the time stamp +#' tStamp in the first colummn #' @name cfnames #' @param o data frame with threads #' @return list of column names @@ -90,8 +91,8 @@ zoomColumn <- function(z){ cfnames <- function(o){ colnames(o)[2:length(colnames(o))]} -## this is used to populate the UI for comparison of categories within a CF -#' get_CF_levels returns the levels of a contextual factor +#' @title get_CF_levels returns the levels of a contextual factor +#' @description this is used to populate the UI for comparison of categories within a CF #' @name get_CF_levels #' @param o data frame with threads #' @param cf a contextual factors (column) @@ -102,11 +103,8 @@ get_CF_levels <- function(o,cf){ } ########################################################################################################## -# this function adds a new column to the occurrenes table based on a combination of context factors CF) -#' Creates a new column that combines some set of other columns -#' -#' For example, actor+action -#' +#' @titleCreates a new column that combines some set of other columns +#' @description this function adds a new column to the occurrenes table based on a combination of context factors CF). For example, actor+action #' @name combineContextFactors #' @param o data frame with threads #' @param CF contextual factors to be combined. @@ -133,7 +131,9 @@ combineContextFactors <- function(o,CF,newCol){ return(o) } -# just keep this simple + +#' @title newColName +#' @description returns new combined column name from a list of CFs #' @name newColName #' @param CF_list list of context factors to define events #' @return column name @@ -143,6 +143,8 @@ newColName <- function(CF_list){ # These were used on the occ-to-event tab to configure the slider +#' @title threshold_slider_min +#' @description These were used on the occ-to-event tab to configure the slider #' @name threshold_slider_min #' @param o dataframe of occurrences #' @return slider min @@ -151,6 +153,8 @@ threshold_slider_min <- function(o){ return(floor(min(o$timeGap))) } +#' @title threshold_slider_max +#' @description These were used on the occ-to-event tab to configure the slider #' @name threshold_slider_max #' @param o dataframe of occurrences #' @return slider max @@ -158,6 +162,9 @@ threshold_slider_min <- function(o){ threshold_slider_max <- function(o){ return(ceiling(max(o$timeGap))) } + +#' @title threshold_slider_selected +#' @description These were used on the occ-to-event tab to configure the slider #' @name threshold_slider_selected #' @param o dataframe of occurrences #' @return selected value = min @@ -170,6 +177,8 @@ threshold_slider_selected <- function(o){ #### count the handoffs, but reverse coded -- zero = all different +#' @title diff_handoffs +#' @description count the handoffs, but reverse coded -- zero = all different #' @name diff_handoffs #' @param o dataframe of occurrences #' @return o dataframe of occurrences with handoff count filled in @@ -196,6 +205,8 @@ row_diff_handoff <- function(this_row){ #### Time gaps -- just pass in the column of time stamps +#' @title diff_tStamp +#' @description Time gaps -- just pass in the column of time stamps - uses auto units #' @name diff_tStamp #' @param o dataframe of occurrences #' @return o dataframe of occurrences with handoff count filled in @@ -227,11 +238,9 @@ row_diff_tStamp <- function(this_row){ -#' threadSizeTable provides a distribution of the length of threads -#' -#' This function should work on either ocurrences or events. +#' @title threadSizeTable provides a distribution of the length of threads +#' @description This function should work on either ocurrences or events. #' it returns length and duration of each thread.It requires tStamp field to compute duration. -#' #' @name threadSizeTable #' @param o data frame with threads #' @param TN column comtaining the threadNumber @@ -264,10 +273,8 @@ threadSizeTable <- function(o,TN){ ######################################################### -#' convert_TN_to_TramineR -#' -#' converts the csv format used in ThreadNet to the format used by TraMiner. Should provide a way to save this, as well. -#' +#' @title convert_TN_to_TramineR +#' @description converts the csv format used in ThreadNet to the format used by TraMiner. Should provide a way to save this, as well. #' @name convert_TN_to_TramineR #' @param df threads (occurrences or events) #' @param CF Contextual factor that will be used to define the state sequences in TraMineR @@ -303,8 +310,8 @@ convert_TN_to_TramineR <- function(df, CF){ } # these functions support the moving window -#' get_threadList returns a list of all thread numbers -#' +#' @titleget_threadList +#' @description get_threadList returns a list of all thread numbers #' @name get_threadList #' @param e data frame with threaded events #' @param TN Column with threadNumber @@ -317,8 +324,8 @@ get_threadList <- function(e,TN,SN){ return(e[e[[SN]]==1,TN]) } -#' get_moving_window returns a set of threads for a moving window -#' +#' @title get_moving_window +#' @description get_moving_window returns a set of threads for a moving window #' @name get_moving_window #' @param e data frame with threads (needs to have threadNum and seqNum) #' @param s size of window @@ -339,10 +346,9 @@ get_moving_window <- function(e, s, l ){ } -# e is the data -# w = window size -# s = step (how far to move the window in each step) -# n is the ngram size + +#' @title window_correlation +#' @description Correlation between moving windows #' @name window_correlation #' @param e data fraom for POV #' @param w width of moving window @@ -421,11 +427,8 @@ window_correlation <- function(e,w,s=1,n=2){ } -# e is the data -# w = window size -# s = step (how far to move the window in each step) -# n is the ngram size -# similar as above, except one window on each side of a focal thread. +#' @title dual_window_correlation +#' @description similar to moving window, except one window on each side of a focal thread. #' @name dual_window_correlation #' @param e data fraom for POV #' @param w width of moving window @@ -508,7 +511,8 @@ dual_window_correlation <- function(e,w,s=1,n=2){ } # Make a nice dataframe to display -# Issue is that DT::renderdatatable cannot display lists correctly. +#' @title make_nice_event_DT +#' @description Issue is that DT::renderdatatable cannot display lists correctly, so cut then out. #' @name make_nice_event_DT #' @description Removes columns that do not need to be displayed #' @param e data frame with POV @@ -532,8 +536,8 @@ make_nice_event_DT <- function(e){ return(e) } -# find the biggest column with ZM_, and then get the number that goes with that. -# It will not be the same as the column number. +#' @title zoom_upper_limit +#' @description find the biggest column with ZM_, and then get the number that goes with that. #' @name zoom_upper_limit #' @description Used to set upper limit on sliders for zooming #' @param event data frame diff --git a/man/ACHR_batch_V1.Rd b/man/ACHR_batch_V1.Rd deleted file mode 100644 index 6a358ae..0000000 --- a/man/ACHR_batch_V1.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ThreadNet_Batch.R -\name{ACHR_batch_V1} -\alias{ACHR_batch_V1} -\title{Batch processing for larger numbers of threads} -\usage{ -ACHR_batch_V1(inFileName) -} -\arguments{ -\item{inFileName}{name of file (CSV format) containing the raw thread data.} -} -\value{ -data frame ready for further analysis -} -\description{ -ACHR stands for Antecedents of Complexity in Healthcare Routines. This is function is set up to compute process parameters on thousands of patient visits. -} diff --git a/man/OccToEvents3.Rd b/man/OccToEvents3.Rd new file mode 100644 index 0000000..01ed57b --- /dev/null +++ b/man/OccToEvents3.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Core.R +\name{OccToEvents3} +\alias{OccToEvents3} +\title{OccToEvents3} +\usage{ +OccToEvents3(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF, TN, CF, rx, + KeepIrregularEvents) +} +\arguments{ +\item{o}{a dataframe of occurrences} + +\item{EventMapName}{= used to store this mapping for visualization and comparison} + +\item{THREAD_CF}{- context factors used to delineate threads} + +\item{EVENT_CF}{- context factors used to define events} + +\item{compare_CF}{= context factors used for comparison -- need to be copied over here when the thread is created.} + +\item{TN}{ThreadNum} + +\item{CF}{context factor} + +\item{rx}{list of patterns} + +\item{KeepIrregularEvents}{= keep or drop events that don't fit patterns} + +\item{uniform_chunk_size}{= used to identify breakpoints -- from input slider} + +\item{tThreshold}{= used to identify breakpoints -- from input slider} + +\item{timescale}{hours, min or sec} + +\item{chunk_CF}{- context factors used to delineate chunks} +} +\value{ +event data frame, with occurrences aggregated into events. +} +\description{ +Creates events based on frequent ngrams or regular expressions +} diff --git a/man/OccToEvents_By_Chunk.Rd b/man/OccToEvents_By_Chunk.Rd index f7e9989..5a436ee 100644 --- a/man/OccToEvents_By_Chunk.Rd +++ b/man/OccToEvents_By_Chunk.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Core.R \name{OccToEvents_By_Chunk} \alias{OccToEvents_By_Chunk} -\title{Maps occurrences into events} +\title{Maps occurrences into events by chunks.} \usage{ OccToEvents_By_Chunk(o, m, EventMapName, uniform_chunk_size, tThreshold, timescale = "mins", chunk_CF, thread_CF, event_CF, compare_CF) @@ -32,6 +32,6 @@ OccToEvents_By_Chunk(o, m, EventMapName, uniform_chunk_size, tThreshold, event data frame, with occurrences aggregated into events. } \description{ -Thus function provides a place to map occurrences into events, so is is not necessary to interpret individual -occurrences in isolation. There are many ways to accomplish this mapping. +Thus function provides a way to map occurrences into events, so is is not necessary to interpret individual +occurrences in isolation. Provides three ways to accomplish this mapping. } diff --git a/man/ThreadNet_Core.Rd b/man/ThreadNet_Core.Rd new file mode 100644 index 0000000..3f3cab9 --- /dev/null +++ b/man/ThreadNet_Core.Rd @@ -0,0 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet.R +\name{ThreadNet_Core} +\alias{ThreadNet_Core} +\alias{ThreadNet} +\title{ThreadNet_Core} +\usage{ +ThreadNet() +} +\description{ +This function launches the Shiny App called ThreadNet +} diff --git a/man/ThreadOccByPOV.Rd b/man/ThreadOccByPOV.Rd index 2551820..ac67972 100644 --- a/man/ThreadOccByPOV.Rd +++ b/man/ThreadOccByPOV.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Core.R \name{ThreadOccByPOV} \alias{ThreadOccByPOV} -\title{Make new threads from a new POV} +\title{Converts occurrences into events, make threads from a new POV} \usage{ ThreadOccByPOV(o, THREAD_CF, EVENT_CF) } diff --git a/man/clusterEvents.Rd b/man/clusterEvents.Rd new file mode 100644 index 0000000..10de33b --- /dev/null +++ b/man/clusterEvents.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Core.R +\name{clusterEvents} +\alias{clusterEvents} +\title{Clusters occurrences or eents} +\usage{ +clusterEvents(e, NewMapName, cluster_method, thread_CF, event_CF, + what_to_return = "POV") +} +\arguments{ +\item{e}{a dataframe of events or occurrences} + +\item{NewMapName}{= used to store this mapping for visualization and comparison} + +\item{cluster_method}{= method for clustering} + +\item{thread_CF}{- context factors used to delineate threads} + +\item{event_CF}{- context factors used to define events} + +\item{what_to_return}{POV or Cluster solution} + +\item{compare_CF}{= context factors used for comparison -- need to be copied over here when the thread is created.} + +\item{TN}{ThreadNum} +} +\value{ +event data frame with occurrences aggregated into events or cluster solution +} +\description{ +cluster_method is either "Sequential similarity" or "Contextual Similarity" or "Network Structure" +} diff --git a/man/count_ngrams.Rd b/man/count_ngrams.Rd index dea7e2b..2a33182 100644 --- a/man/count_ngrams.Rd +++ b/man/count_ngrams.Rd @@ -19,5 +19,5 @@ count_ngrams(o, TN, CF, n) a dataframe with ngram, frequency and proportion in descending order } \description{ -This function counts n-grams within threads where the length of the thread is greater than n. +Counting ngrams is essential to several ThreadNet functions. This function counts n-grams within threads where the length of the thread is greater than n. } From 8ee4874d3aa342eaa7bf2113a47894af5fa0a0c3 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 15:28:01 -0400 Subject: [PATCH 20/36] more package details --- DESCRIPTION | 2 +- Inst/ThreadNet/global.R | 3 +++ NAMESPACE | 16 +++++++++++ R/ThreadNet_Core.R | 11 ++------ R/ThreadNet_Graphics.R | 6 ++--- R/ThreadNet_Metrics.R | 39 ++++++++++----------------- R/ThreadNet_Misc.R | 10 +++---- man/OccToEvents3.Rd | 8 ------ man/cfnames.Rd | 3 ++- man/clusterEvents.Rd | 4 --- man/combineContextFactors.Rd | 4 +-- man/diff_handoffs.Rd | 17 ++++++++++++ man/diff_tStamp.Rd | 17 ++++++++++++ man/dual_window_correlation.Rd | 20 ++++++++++++++ man/estimate_task_complexity_index.Rd | 2 +- man/get_CF_levels.Rd | 2 +- man/get_moving_window.Rd | 2 +- man/get_threadList.Rd | 2 +- man/make_example_DF.Rd | 14 ++++++++++ man/make_nice_event_DT.Rd | 16 +++++++++++ man/make_subsets.Rd | 19 +++++++++++++ man/newColName.Rd | 17 ++++++++++++ man/plot_entropy.Rd | 17 ++++++++++++ man/role_map.Rd | 21 +++++++++++++++ man/threshold_slider_max.Rd | 17 ++++++++++++ man/threshold_slider_min.Rd | 17 ++++++++++++ man/threshold_slider_selected.Rd | 17 ++++++++++++ man/window_correlation.Rd | 20 ++++++++++++++ man/zoomColumn.Rd | 18 +++++++++++++ man/zoom_upper_limit.Rd | 19 +++++++++++++ 30 files changed, 318 insertions(+), 62 deletions(-) create mode 100644 man/diff_handoffs.Rd create mode 100644 man/diff_tStamp.Rd create mode 100644 man/dual_window_correlation.Rd create mode 100644 man/make_example_DF.Rd create mode 100644 man/make_nice_event_DT.Rd create mode 100644 man/make_subsets.Rd create mode 100644 man/newColName.Rd create mode 100644 man/plot_entropy.Rd create mode 100644 man/role_map.Rd create mode 100644 man/threshold_slider_max.Rd create mode 100644 man/threshold_slider_min.Rd create mode 100644 man/threshold_slider_selected.Rd create mode 100644 man/window_correlation.Rd create mode 100644 man/zoomColumn.Rd create mode 100644 man/zoom_upper_limit.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 7003f41..62ee8a8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,4 +9,4 @@ License: GPL (>= 2) Encoding: UTF-8 LazyData: true RoxygenNote: 6.0.1 -Imports: shiny, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr +Imports: shiny, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr , methods diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index 0265b9e..f53d269 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -8,7 +8,10 @@ # 15 October Point of view code has solidified somewhat # 20 October Starting on NetworkD3 +# June 6 finishing up Package + +# add the dependent packages library(shiny) library(plotly) library(ggplot2) diff --git a/NAMESPACE b/NAMESPACE index 450c7e7..93d62bd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -57,3 +57,19 @@ export(threshold_slider_selected) export(window_correlation) export(zoomColumn) export(zoom_upper_limit) +importFrom(grDevices,as.dist) +importFrom(grDevices,colorRampPalette) +importFrom(grDevices,cor) +importFrom(grDevices,cutree) +importFrom(grDevices,dist) +importFrom(grDevices,filter) +importFrom(grDevices,graphics) +importFrom(grDevices,hclust) +importFrom(grDevices,methods) +importFrom(grDevices,new) +importFrom(grDevices,plot) +importFrom(grDevices,read.table) +importFrom(grDevices,setNames) +importFrom(grDevices,stats) +importFrom(grDevices,utils) +importFrom(grDevices,write.csv) diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index d3b7849..72840bf 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -332,8 +332,6 @@ print('done converting occurrences...') ############################################################################################################## -#' -#' #' @title Maps occurrences into events by chunks. #' @description Thus function provides a way to map occurrences into events, so is is not necessary to interpret individual #' occurrences in isolation. Provides three ways to accomplish this mapping. @@ -489,16 +487,12 @@ OccToEvents_By_Chunk <- function(o, m, EventMapName, uniform_chunk_size, tThresh } -# +################################################################################ #' @title OccToEvents3 #' @description Creates events based on frequent ngrams or regular expressions #' @name OccToEvents3 #' @param o a dataframe of occurrences #' @param EventMapName = used to store this mapping for visualization and comparison -#' @param uniform_chunk_size = used to identify breakpoints -- from input slider -#' @param tThreshold = used to identify breakpoints -- from input slider -#' @param timescale hours, min or sec -#' @param chunk_CF - context factors used to delineate chunks #' @param THREAD_CF - context factors used to delineate threads #' @param EVENT_CF - context factors used to define events #' @param compare_CF = context factors used for comparison -- need to be copied over here when the thread is created. @@ -656,6 +650,7 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF } +###################################################################################### #' @title Clusters occurrences or eents #' @description cluster_method is either "Sequential similarity" or "Contextual Similarity" or "Network Structure" #' @name clusterEvents @@ -664,8 +659,6 @@ OccToEvents3 <- function(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF,TN, CF #' @param cluster_method = method for clustering #' @param thread_CF - context factors used to delineate threads #' @param event_CF - context factors used to define events -#' @param compare_CF = context factors used for comparison -- need to be copied over here when the thread is created. -#' @param TN ThreadNum #' @param what_to_return POV or Cluster solution #' @return event data frame with occurrences aggregated into events or cluster solution #' @export diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index 19283e5..c2f5eca 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -590,12 +590,12 @@ filter_network_edges <- function(n, threshold){ return(n) } -#' @name role_map +#' @title role_map #' @description A role map (like a heat map) that will show "who does what" for any set of events #' @name role_map #' @param e event data frame #' @param o occurrence data frame -#' @param cf context factor for the graph +#' @param cfs context factors for the graph #' @return plotly heat map #' @export role_map <- function(e, o, cfs){ @@ -625,7 +625,7 @@ role_map <- function(e, o, cfs){ -#'@title threadTrajectory +#' @title threadTrajectory #' @description Create a plotly diagram showing relative time versus sequential time. #' Inspired by Gergen and Danner-Schroeder #' @name threadTrajectory diff --git a/R/ThreadNet_Metrics.R b/R/ThreadNet_Metrics.R index 2977572..5a92e6d 100644 --- a/R/ThreadNet_Metrics.R +++ b/R/ThreadNet_Metrics.R @@ -8,23 +8,18 @@ # Functions for metrics: entropy, complexity, routine-ness, etc. -# takes the output from the function that maps threads to networks -#' Estimates the number of paths in a directed graph -#' -#' This function takes a network descripts (nodes and edges, as generaged by the functino threads_to_network, and estimates the number of paths. +#' @title Estimates the number of paths in a directed graph +#' @description This function takes a network descripts (nodes and edges, as generaged by the functino threads_to_network, and estimates the number of paths. #' as described in Haerem, Pentland and Miller (2015). The estimate correlates with the McCabe's (1975) cyclometric complexity. -#' #' @name estimate_network_complexity #' @param net Object with dataframe for nodes and edges #' @return number #' @export estimate_network_complexity <- function(net){ return(estimate_task_complexity_index( nrow(net$nodeDF), nrow(net$edgeDF)) ) } -# this version takes vertices and edges -#' Estimates the number of paths in a directed graph -#' -#' Same as estimate_network_complexity, but takes different parameters -#' + +#' @title Estimates the number of paths in a directed graph +#' @description Same as estimate_network_complexity, but takes this version takes vertices and edges as parameters #' @name estimate_task_complexity_index #' @param v number of vertices (or nodes) #' @param e number of edges @@ -52,11 +47,9 @@ estimate_task_complexity_index <- function(v,e){ ################################################################# -#' Computes a metric of routineness based on frequency of ngrams -#' -#' Computes the fraction of observed behavior that conforms to an observed pattern. +#' @title Computes a metric of routineness based on frequency of ngrams +#' @description Computes the fraction of observed behavior that conforms to an observed pattern. #' Current version uses ngrams, but it would be good to use spmf pattern mining to avoid including duplicate patterns (e.g., a-b-c and b-c-d) -#' #' @name routineness_metric #' @param o data frame with occurresnces or events #' @param TN name of column with threadNumbers @@ -78,11 +71,9 @@ routineness_metric <- function(o,TN,CF,n,m){ ############################################################################# -#' Computes the compressibility of the data in one column of a data frame -#' -#' Compressibility is an index of complexity -- more compressible means less complex. This function computes the ratio of compressed data +#' @title Computes the compressibility of the data in one column of a data frame +#' @description Compressibility is an index of complexity -- more compressible means less complex. This function computes the ratio of compressed data #' to the original data. Should be between zero and one. Uses built-in functions for in=memory compression -#' #' @name compression_index #' @param df a data frame containing occurrences or events #' @param CF a column or contextual factor in that data frame @@ -95,11 +86,8 @@ compression_index <- function(df,CF){ return( ####################################################################### #compute entropy for a set of observations in a column from a data frame -# freq is typically going to the $freq column from ngram table, or -# the frequency of each level in the CFs, as counted by table() -#' Compute the entropy of a contextual factor -#' -#' Each column in the raw data represents a contextual factor. This function computes the entropy of each factor that is selected for use in the +#' @title Compute the entropy of a contextual factor +#' @description Each column in the raw data represents a contextual factor. This function computes the entropy of each factor that is selected for use in the #' analysis. #' @name compute_entropy #' @param freq is the frequency distribution of the levels in the factor @@ -113,10 +101,11 @@ compute_entropy <- function(freq){ } # code to plot entropy as a function of zoom_level -# need to get the zoom levels -- grep out the 'Z_' column names... +#' @title plot entropy as a function of zoom_level +#' @description Gets the zoom levels, grep out the 'Z_' column names... #' @name plot_entropy #' @param e data from of events with zoom levels -#' @return R plot +#' @return regular R plot #' @export plot_entropy <- function(e){ plot(unlist(lapply(grep('ZM_',colnames(e)),function(i){compute_entropy(table(e[[i]]))}))) diff --git a/R/ThreadNet_Misc.R b/R/ThreadNet_Misc.R index 229ed34..6eb7ed5 100644 --- a/R/ThreadNet_Misc.R +++ b/R/ThreadNet_Misc.R @@ -103,7 +103,7 @@ get_CF_levels <- function(o,cf){ } ########################################################################################################## -#' @titleCreates a new column that combines some set of other columns +#' @title combineContextFactors creates a new column that combines some set of other columns #' @description this function adds a new column to the occurrenes table based on a combination of context factors CF). For example, actor+action #' @name combineContextFactors #' @param o data frame with threads @@ -208,8 +208,8 @@ row_diff_handoff <- function(this_row){ #' @title diff_tStamp #' @description Time gaps -- just pass in the column of time stamps - uses auto units #' @name diff_tStamp -#' @param o dataframe of occurrences -#' @return o dataframe of occurrences with handoff count filled in +#' @param ts column of time stamps +#' @return Column of differences between timestamps #' @export diff_tStamp <- function(ts){ @@ -310,7 +310,7 @@ convert_TN_to_TramineR <- function(df, CF){ } # these functions support the moving window -#' @titleget_threadList +#' @title get_threadList #' @description get_threadList returns a list of all thread numbers #' @name get_threadList #' @param e data frame with threaded events @@ -540,7 +540,7 @@ make_nice_event_DT <- function(e){ #' @description find the biggest column with ZM_, and then get the number that goes with that. #' @name zoom_upper_limit #' @description Used to set upper limit on sliders for zooming -#' @param event data frame +#' @param e data frame #' @return biggest zoom level #' @export zoom_upper_limit <- function(e){ diff --git a/man/OccToEvents3.Rd b/man/OccToEvents3.Rd index 01ed57b..1a2d16a 100644 --- a/man/OccToEvents3.Rd +++ b/man/OccToEvents3.Rd @@ -25,14 +25,6 @@ OccToEvents3(o, EventMapName, THREAD_CF, EVENT_CF, compare_CF, TN, CF, rx, \item{rx}{list of patterns} \item{KeepIrregularEvents}{= keep or drop events that don't fit patterns} - -\item{uniform_chunk_size}{= used to identify breakpoints -- from input slider} - -\item{tThreshold}{= used to identify breakpoints -- from input slider} - -\item{timescale}{hours, min or sec} - -\item{chunk_CF}{- context factors used to delineate chunks} } \value{ event data frame, with occurrences aggregated into events. diff --git a/man/cfnames.Rd b/man/cfnames.Rd index 42234dd..2b8e5ee 100644 --- a/man/cfnames.Rd +++ b/man/cfnames.Rd @@ -13,5 +13,6 @@ cfnames(o) list of column names } \description{ -cfnames provides names of all the contextual factors (except the time stamp) +grab all of the columns except the first, which has the time stamp +tStamp in the first colummn } diff --git a/man/clusterEvents.Rd b/man/clusterEvents.Rd index 10de33b..6d6e780 100644 --- a/man/clusterEvents.Rd +++ b/man/clusterEvents.Rd @@ -19,10 +19,6 @@ clusterEvents(e, NewMapName, cluster_method, thread_CF, event_CF, \item{event_CF}{- context factors used to define events} \item{what_to_return}{POV or Cluster solution} - -\item{compare_CF}{= context factors used for comparison -- need to be copied over here when the thread is created.} - -\item{TN}{ThreadNum} } \value{ event data frame with occurrences aggregated into events or cluster solution diff --git a/man/combineContextFactors.Rd b/man/combineContextFactors.Rd index 9555dc0..df5b433 100644 --- a/man/combineContextFactors.Rd +++ b/man/combineContextFactors.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Misc.R \name{combineContextFactors} \alias{combineContextFactors} -\title{Creates a new column that combines some set of other columns} +\title{combineContextFactors creates a new column that combines some set of other columns} \usage{ combineContextFactors(o, CF, newCol) } @@ -17,5 +17,5 @@ combineContextFactors(o, CF, newCol) data frame with the new column } \description{ -For example, actor+action +this function adds a new column to the occurrenes table based on a combination of context factors CF). For example, actor+action } diff --git a/man/diff_handoffs.Rd b/man/diff_handoffs.Rd new file mode 100644 index 0000000..8d6a088 --- /dev/null +++ b/man/diff_handoffs.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{diff_handoffs} +\alias{diff_handoffs} +\title{diff_handoffs} +\usage{ +diff_handoffs(o) +} +\arguments{ +\item{o}{dataframe of occurrences} +} +\value{ +o dataframe of occurrences with handoff count filled in +} +\description{ +count the handoffs, but reverse coded -- zero = all different +} diff --git a/man/diff_tStamp.Rd b/man/diff_tStamp.Rd new file mode 100644 index 0000000..6c6b41b --- /dev/null +++ b/man/diff_tStamp.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{diff_tStamp} +\alias{diff_tStamp} +\title{diff_tStamp} +\usage{ +diff_tStamp(ts) +} +\arguments{ +\item{ts}{column of time stamps} +} +\value{ +Column of differences between timestamps +} +\description{ +Time gaps -- just pass in the column of time stamps - uses auto units +} diff --git a/man/dual_window_correlation.Rd b/man/dual_window_correlation.Rd new file mode 100644 index 0000000..e2e4cc2 --- /dev/null +++ b/man/dual_window_correlation.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{dual_window_correlation} +\alias{dual_window_correlation} +\title{dual_window_correlation} +\usage{ +dual_window_correlation(e, w, s = 1, n = 2) +} +\arguments{ +\item{e}{data fraom for POV} + +\item{w}{width of moving window} + +\item{s}{step - how far to move window in each increment (default is 1)} + +\item{n}{number of windows (default is 2)} +} +\description{ +similar to moving window, except one window on each side of a focal thread. +} diff --git a/man/estimate_task_complexity_index.Rd b/man/estimate_task_complexity_index.Rd index baeb1e9..f2dc14e 100644 --- a/man/estimate_task_complexity_index.Rd +++ b/man/estimate_task_complexity_index.Rd @@ -15,5 +15,5 @@ estimate_task_complexity_index(v, e) number } \description{ -Same as estimate_network_complexity, but takes different parameters +Same as estimate_network_complexity, but takes this version takes vertices and edges as parameters } diff --git a/man/get_CF_levels.Rd b/man/get_CF_levels.Rd index a443946..9555f96 100644 --- a/man/get_CF_levels.Rd +++ b/man/get_CF_levels.Rd @@ -15,5 +15,5 @@ get_CF_levels(o, cf) list of unique factor levels } \description{ -get_CF_levels returns the levels of a contextual factor +this is used to populate the UI for comparison of categories within a CF } diff --git a/man/get_moving_window.Rd b/man/get_moving_window.Rd index 30531ac..b86683b 100644 --- a/man/get_moving_window.Rd +++ b/man/get_moving_window.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Misc.R \name{get_moving_window} \alias{get_moving_window} -\title{get_moving_window returns a set of threads for a moving window} +\title{get_moving_window} \usage{ get_moving_window(e, s, l) } diff --git a/man/get_threadList.Rd b/man/get_threadList.Rd index 9b99a4c..a3c7bc7 100644 --- a/man/get_threadList.Rd +++ b/man/get_threadList.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/ThreadNet_Misc.R \name{get_threadList} \alias{get_threadList} -\title{get_threadList returns a list of all thread numbers} +\title{get_threadList} \usage{ get_threadList(e, TN, SN) } diff --git a/man/make_example_DF.Rd b/man/make_example_DF.Rd new file mode 100644 index 0000000..abbc7e9 --- /dev/null +++ b/man/make_example_DF.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{make_example_DF} +\alias{make_example_DF} +\title{make_example_DF} +\usage{ +make_example_DF() +} +\value{ +DF with some data +} +\description{ +Make an example data frame for display... +} diff --git a/man/make_nice_event_DT.Rd b/man/make_nice_event_DT.Rd new file mode 100644 index 0000000..b459551 --- /dev/null +++ b/man/make_nice_event_DT.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{make_nice_event_DT} +\alias{make_nice_event_DT} +\title{make_nice_event_DT} +\usage{ +make_nice_event_DT(e) +} +\arguments{ +\item{e}{data frame with POV} +} +\description{ +Issue is that DT::renderdatatable cannot display lists correctly, so cut then out. + +Removes columns that do not need to be displayed +} diff --git a/man/make_subsets.Rd b/man/make_subsets.Rd new file mode 100644 index 0000000..11a478c --- /dev/null +++ b/man/make_subsets.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{make_subsets} +\alias{make_subsets} +\title{make_subsets} +\usage{ +make_subsets(d, n) +} +\arguments{ +\item{d}{data frame with occurrences or events} + +\item{n}{number of buckets} +} +\value{ +list of smaller data frames +} +\description{ +this function is used to split up the threads into n ~equal buckets +} diff --git a/man/newColName.Rd b/man/newColName.Rd new file mode 100644 index 0000000..3f765ac --- /dev/null +++ b/man/newColName.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{newColName} +\alias{newColName} +\title{newColName} +\usage{ +newColName(CF_list) +} +\arguments{ +\item{CF_list}{list of context factors to define events} +} +\value{ +column name +} +\description{ +returns new combined column name from a list of CFs +} diff --git a/man/plot_entropy.Rd b/man/plot_entropy.Rd new file mode 100644 index 0000000..e75d3fa --- /dev/null +++ b/man/plot_entropy.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Metrics.R +\name{plot_entropy} +\alias{plot_entropy} +\title{plot entropy as a function of zoom_level} +\usage{ +plot_entropy(e) +} +\arguments{ +\item{e}{data from of events with zoom levels} +} +\value{ +regular R plot +} +\description{ +Gets the zoom levels, grep out the 'Z_' column names... +} diff --git a/man/role_map.Rd b/man/role_map.Rd new file mode 100644 index 0000000..b9e28b4 --- /dev/null +++ b/man/role_map.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Graphics.R +\name{role_map} +\alias{role_map} +\title{role_map} +\usage{ +role_map(e, o, cfs) +} +\arguments{ +\item{e}{event data frame} + +\item{o}{occurrence data frame} + +\item{cfs}{context factors for the graph} +} +\value{ +plotly heat map +} +\description{ +A role map (like a heat map) that will show "who does what" for any set of events +} diff --git a/man/threshold_slider_max.Rd b/man/threshold_slider_max.Rd new file mode 100644 index 0000000..43f9d5e --- /dev/null +++ b/man/threshold_slider_max.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{threshold_slider_max} +\alias{threshold_slider_max} +\title{threshold_slider_max} +\usage{ +threshold_slider_max(o) +} +\arguments{ +\item{o}{dataframe of occurrences} +} +\value{ +slider max +} +\description{ +These were used on the occ-to-event tab to configure the slider +} diff --git a/man/threshold_slider_min.Rd b/man/threshold_slider_min.Rd new file mode 100644 index 0000000..a96f139 --- /dev/null +++ b/man/threshold_slider_min.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{threshold_slider_min} +\alias{threshold_slider_min} +\title{threshold_slider_min} +\usage{ +threshold_slider_min(o) +} +\arguments{ +\item{o}{dataframe of occurrences} +} +\value{ +slider min +} +\description{ +These were used on the occ-to-event tab to configure the slider +} diff --git a/man/threshold_slider_selected.Rd b/man/threshold_slider_selected.Rd new file mode 100644 index 0000000..3ade129 --- /dev/null +++ b/man/threshold_slider_selected.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{threshold_slider_selected} +\alias{threshold_slider_selected} +\title{threshold_slider_selected} +\usage{ +threshold_slider_selected(o) +} +\arguments{ +\item{o}{dataframe of occurrences} +} +\value{ +selected value = min +} +\description{ +These were used on the occ-to-event tab to configure the slider +} diff --git a/man/window_correlation.Rd b/man/window_correlation.Rd new file mode 100644 index 0000000..309e422 --- /dev/null +++ b/man/window_correlation.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{window_correlation} +\alias{window_correlation} +\title{window_correlation} +\usage{ +window_correlation(e, w, s = 1, n = 2) +} +\arguments{ +\item{e}{data fraom for POV} + +\item{w}{width of moving window} + +\item{s}{step - how far to move window in each increment (default is 1)} + +\item{n}{number of windows (default is 2)} +} +\description{ +Correlation between moving windows +} diff --git a/man/zoomColumn.Rd b/man/zoomColumn.Rd new file mode 100644 index 0000000..f6472ef --- /dev/null +++ b/man/zoomColumn.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{zoomColumn} +\alias{zoomColumn} +\title{zoomColumn} +\usage{ +zoomColumn(z) +} +\arguments{ +\item{z}{integer for the zoom level} +} +\value{ +column name for that zoom level ('ZM_n) +} +\description{ +This function takes a slider value and returns a valid column name for zooming +if the argument is null, then use ZM_1 +} diff --git a/man/zoom_upper_limit.Rd b/man/zoom_upper_limit.Rd new file mode 100644 index 0000000..1b822b9 --- /dev/null +++ b/man/zoom_upper_limit.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Misc.R +\name{zoom_upper_limit} +\alias{zoom_upper_limit} +\title{zoom_upper_limit} +\usage{ +zoom_upper_limit(e) +} +\arguments{ +\item{e}{data frame} +} +\value{ +biggest zoom level +} +\description{ +find the biggest column with ZM_, and then get the number that goes with that. + +Used to set upper limit on sliders for zooming +} From b2d4d26d888b97ec321a7ba5b466351e7c079cf1 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 15:53:57 -0400 Subject: [PATCH 21/36] Some final changes for the package --- Inst/ThreadNet/global.R | 6 ++++++ NAMESPACE | 26 +++++++++++--------------- R/ThreadNet_Core.R | 4 ++++ R/ThreadNet_Graphics.R | 4 ++++ 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index f53d269..561da95 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -10,6 +10,12 @@ # 20 October Starting on NetworkD3 # June 6 finishing up Package +# explicitly add packages/functions to the NAMESPACE +#' @importFrom grDevices colorRampPalette +#' @importFrom graphics plot +#' @importFrom methods new +#' @importFrom stats as.dist cor cutree dist filter hclust setNames +#' @importFrom utils read.table write.csv # add the dependent packages library(shiny) diff --git a/NAMESPACE b/NAMESPACE index 93d62bd..f0f4bf8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -57,19 +57,15 @@ export(threshold_slider_selected) export(window_correlation) export(zoomColumn) export(zoom_upper_limit) -importFrom(grDevices,as.dist) importFrom(grDevices,colorRampPalette) -importFrom(grDevices,cor) -importFrom(grDevices,cutree) -importFrom(grDevices,dist) -importFrom(grDevices,filter) -importFrom(grDevices,graphics) -importFrom(grDevices,hclust) -importFrom(grDevices,methods) -importFrom(grDevices,new) -importFrom(grDevices,plot) -importFrom(grDevices,read.table) -importFrom(grDevices,setNames) -importFrom(grDevices,stats) -importFrom(grDevices,utils) -importFrom(grDevices,write.csv) +importFrom(graphics,plot) +importFrom(methods,new) +importFrom(stats,as.dist) +importFrom(stats,cor) +importFrom(stats,cutree) +importFrom(stats,dist) +importFrom(stats,filter) +importFrom(stats,hclust) +importFrom(stats,setNames) +importFrom(utils,read.table) +importFrom(utils,write.csv) diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index 72840bf..ce9e730 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -5,6 +5,10 @@ # GNU General Public License (GPL-3.0) https://opensource.org/licenses/GPL-3.0? # Absolutely no warranty! ########################################################################################################## +#' @importFrom methods new +#' @importFrom stats as.dist cor cutree dist filter hclust setNames +#' @importFrom utils read.table write.csv + # These are the basic functions that convert threads to networks, etc. diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index c2f5eca..836255b 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -8,6 +8,10 @@ # graphic functions used in Shiny App. # some plotly, but some from other packages +# explicitly add packages/functions to the NAMESPACE +#' @importFrom grDevices colorRampPalette +#' @importFrom graphics plot +#' @importFrom methods new ###### Pie charts for context factors #### From a557792ae93178bb7fad54b909eb98a4b951bbc7 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 19:55:17 +0000 Subject: [PATCH 22/36] Missed ShinyJS --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 62ee8a8..ebf5aca 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,4 +9,4 @@ License: GPL (>= 2) Encoding: UTF-8 LazyData: true RoxygenNote: 6.0.1 -Imports: shiny, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr , methods +Imports: shiny, shinyjs, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr , methods From 9bdce402b9b3bcca650fee552497ffb7813555d7 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 20:26:52 +0000 Subject: [PATCH 23/36] Trying to fix horrific new bug in ThreadToEvents_original (!). Amazing. --- Inst/ThreadNet/global.R | 9 ++------- R/ThreadNet_Core.R | 12 +++++++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index 561da95..477ff4e 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -10,15 +10,10 @@ # 20 October Starting on NetworkD3 # June 6 finishing up Package -# explicitly add packages/functions to the NAMESPACE -#' @importFrom grDevices colorRampPalette -#' @importFrom graphics plot -#' @importFrom methods new -#' @importFrom stats as.dist cor cutree dist filter hclust setNames -#' @importFrom utils read.table write.csv # add the dependent packages library(shiny) +library(shinyjs) library(plotly) library(ggplot2) library(tidyverse) @@ -34,7 +29,7 @@ library(DT) library(RColorBrewer) library(lubridate) library(knitr) -library(shinyjs) + # visualization types for UI dropdowns diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index ce9e730..a777ec1 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -5,9 +5,6 @@ # GNU General Public License (GPL-3.0) https://opensource.org/licenses/GPL-3.0? # Absolutely no warranty! ########################################################################################################## -#' @importFrom methods new -#' @importFrom stats as.dist cor cutree dist filter hclust setNames -#' @importFrom utils read.table write.csv # These are the basic functions that convert threads to networks, etc. @@ -84,7 +81,8 @@ threads_to_network_original <- function(et,TN,CF,grp='threadNum'){ label = ngdf$freq, Value =ngdf$freq) %>% filter(!from==to) - # print(paste("Edges:",edges)) + print(paste("T2N nodes:",nodes)) + print(paste("T2N edges:",edges)) return(list(nodeDF = nodes, edgeDF = edges)) } @@ -771,7 +769,11 @@ dist_matrix_network <- function(e,CF){ # first get the nodes and edges n=threads_to_network_original(e,'threadNum',CF) - # print(paste('in dist_matrix_network, n=', n)) + print(paste('in dist_matrix_network, n=', n)) + print(n$nodeDF[['label']]) + print(n$edgeDF) + + # now get the shortest paths between all nodes in the graph d=distances(graph_from_data_frame(n$edgeDF), From 5114e23323f97245475047e3a19c2dad8471b24a Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Wed, 6 Jun 2018 21:14:33 +0000 Subject: [PATCH 24/36] Fixed bugs with missing functions --- DESCRIPTION | 2 +- NAMESPACE | 13 ++----- R/ThreadNet_Core.R | 79 ++++++++++++++++++++++++++------------- R/ThreadNet_Graphics.R | 1 - man/frequent_ngrams.Rd | 27 +++++++++++++ man/support_level.Rd | 19 ++++++++++ man/thread_text_vector.Rd | 23 ++++++++++++ 7 files changed, 127 insertions(+), 37 deletions(-) create mode 100644 man/frequent_ngrams.Rd create mode 100644 man/support_level.Rd create mode 100644 man/thread_text_vector.Rd diff --git a/DESCRIPTION b/DESCRIPTION index ebf5aca..b54aa72 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,4 +9,4 @@ License: GPL (>= 2) Encoding: UTF-8 LazyData: true RoxygenNote: 6.0.1 -Imports: shiny, shinyjs, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr , methods +Imports: shiny, shinyjs, plotly, tidyverse, ngram, stringr,stringdist, networkD3, visNetwork, colorspace, igraph, RColorBrewer, xesreadR, DT, lubridate, knitr diff --git a/NAMESPACE b/NAMESPACE index f0f4bf8..5ac8de6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -27,6 +27,7 @@ export(export_POV) export(export_POV_csv) export(filter_network_edges) export(forceNetworkD3) +export(frequent_ngrams) export(get_CF_levels) export(get_POV) export(get_POV_COMPARISON_CF) @@ -47,9 +48,11 @@ export(plot_entropy) export(role_map) export(routineness_metric) export(store_POV) +export(support_level) export(threadMap) export(threadSizeTable) export(threadTrajectory) +export(thread_text_vector) export(threads_to_network_original) export(threshold_slider_max) export(threshold_slider_min) @@ -59,13 +62,3 @@ export(zoomColumn) export(zoom_upper_limit) importFrom(grDevices,colorRampPalette) importFrom(graphics,plot) -importFrom(methods,new) -importFrom(stats,as.dist) -importFrom(stats,cor) -importFrom(stats,cutree) -importFrom(stats,dist) -importFrom(stats,filter) -importFrom(stats,hclust) -importFrom(stats,setNames) -importFrom(utils,read.table) -importFrom(utils,write.csv) diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index a777ec1..55607df 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -81,8 +81,8 @@ threads_to_network_original <- function(et,TN,CF,grp='threadNum'){ label = ngdf$freq, Value =ngdf$freq) %>% filter(!from==to) - print(paste("T2N nodes:",nodes)) - print(paste("T2N edges:",edges)) + # print(paste("T2N nodes:",nodes)) + # print(paste("T2N edges:",edges)) return(list(nodeDF = nodes, edgeDF = edges)) } @@ -732,6 +732,36 @@ clusterEvents <- function(e, NewMapName, cluster_method, thread_CF, event_CF,wha {return(newmap)} } + +# this is used for the regex pages to show the threads. +# similar code is used in count_ngrams and to make networks, but with different delimiters +# and with a minimum sequence length (ngram size), but this can be filtered after this function3 +#' @title thread_text_vector +#' @description Create a vector of threads +#' @name thread_text_vector +#' @param o a dataframe of events or occurrences +#' @param TN = threadNum +#' @param CF = CF or columm to include +#' @param delimiter usually comma or blank +#' @return vector of threads as delimited character strings +#' @export + thread_text_vector <- function(o, TN, CF, delimiter){ + + # Initialize text vector + tv = vector(mode="character") + + # Loop through the unique thread numbers + j=0 + for (i in unique(o[[TN]])){ + txt =o[o[[TN]]==i,CF] + + j=j+1 + tv[j] = str_replace_all(concatenate(o[o[[TN]]==i,CF] ),' ',delimiter) + } + return(tv) + +} + ############################################################################# ############################################################################# ## LOCAL FUNCTIONS from here on down @@ -769,10 +799,9 @@ dist_matrix_network <- function(e,CF){ # first get the nodes and edges n=threads_to_network_original(e,'threadNum',CF) - print(paste('in dist_matrix_network, n=', n)) - print(n$nodeDF[['label']]) - print(n$edgeDF) - + # print(paste('in dist_matrix_network, n=', n)) + # print(n$nodeDF[['label']]) + # print(n$edgeDF) # now get the shortest paths between all nodes in the graph @@ -932,25 +961,7 @@ one_vcf_matrix <- function(e, vcf){ } -# this is used for the regex pages to show the threads. -# similar code is used in count_ngrams and to make networks, but with different delimiters -# and with a minimum sequence length (ngram size), but this can be filtered after this function3 -thread_text_vector <- function(o, TN, CF, delimiter){ - # Initialize text vector - tv = vector(mode="character") - - # Loop through the unique thread numbers - j=0 - for (i in unique(o[[TN]])){ - txt =o[o[[TN]]==i,CF] - - j=j+1 - tv[j] = str_replace_all(concatenate(o[o[[TN]]==i,CF] ),' ',delimiter) - } - return(tv) - -} # use this to replace patterns for regex and ngrams # tv is the text vector for the set of threads @@ -983,6 +994,17 @@ replace_regex_list <- function(tv, rx ){ # combined set of frequent ngrams # add parameter to make maximal a choice +#' @title frequent_ngrams +#' @description combined set of frequent ngrams within a range o lengths +#' @name frequent_ngrams +#' @param e event data +#' @param TN threadNum +#' @param CF context factor (column) to look at +#' @param minN miniumum ngram length +#' @param maxN maximum ngram length +#' @param onlyMaximal Filters out ngrams that are included in longer ngrams. Default is true. +#' @return dataframe of ngrams +#' @export frequent_ngrams <- function(e, TN, CF, minN, maxN, onlyMaximal=TRUE){ # initialize the output @@ -1024,6 +1046,13 @@ maximal_ngrams <- function(ng){ # tv = text vectors for the threads # ng = frequent ngrams data frame # returns ng data frame with support level added +#' @title support_level +#' @description Counts what fraction of the threads a particular ngram appears in +#' @name support_level +#' @param tv text vector of threads +#' @param ng ngram to be located in the threads +#' @return percentage of threads containing the ngram +#' @export support_level <- function(tv, ng) { # change the commas back to spaces @@ -1045,7 +1074,7 @@ support_level <- function(tv, ng) { return(ng) } -# compute the generativity = in-degree and out-degree + generativity_level<- function(tv, ng){ # for each ngram, look at the next longer size diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index 836255b..e70240a 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -11,7 +11,6 @@ # explicitly add packages/functions to the NAMESPACE #' @importFrom grDevices colorRampPalette #' @importFrom graphics plot -#' @importFrom methods new ###### Pie charts for context factors #### diff --git a/man/frequent_ngrams.Rd b/man/frequent_ngrams.Rd new file mode 100644 index 0000000..4529dd6 --- /dev/null +++ b/man/frequent_ngrams.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Core.R +\name{frequent_ngrams} +\alias{frequent_ngrams} +\title{frequent_ngrams} +\usage{ +frequent_ngrams(e, TN, CF, minN, maxN, onlyMaximal = TRUE) +} +\arguments{ +\item{e}{event data} + +\item{TN}{threadNum} + +\item{CF}{context factor (column) to look at} + +\item{minN}{miniumum ngram length} + +\item{maxN}{maximum ngram length} + +\item{onlyMaximal}{Filters out ngrams that are included in longer ngrams. Default is true.} +} +\value{ +dataframe of ngrams +} +\description{ +combined set of frequent ngrams within a range o lengths +} diff --git a/man/support_level.Rd b/man/support_level.Rd new file mode 100644 index 0000000..e59ef95 --- /dev/null +++ b/man/support_level.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Core.R +\name{support_level} +\alias{support_level} +\title{support_level} +\usage{ +support_level(tv, ng) +} +\arguments{ +\item{tv}{text vector of threads} + +\item{ng}{ngram to be located in the threads} +} +\value{ +percentage of threads containing the ngram +} +\description{ +Counts what fraction of the threads a particular ngram appears in +} diff --git a/man/thread_text_vector.Rd b/man/thread_text_vector.Rd new file mode 100644 index 0000000..d1a47d1 --- /dev/null +++ b/man/thread_text_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ThreadNet_Core.R +\name{thread_text_vector} +\alias{thread_text_vector} +\title{thread_text_vector} +\usage{ +thread_text_vector(o, TN, CF, delimiter) +} +\arguments{ +\item{o}{a dataframe of events or occurrences} + +\item{TN}{= threadNum} + +\item{CF}{= CF or columm to include} + +\item{delimiter}{usually comma or blank} +} +\value{ +vector of threads as delimited character strings +} +\description{ +Create a vector of threads +} From 4305ba9e74d21ee27808c9bb4b9b6f29f273b3e5 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 7 Jun 2018 00:40:02 +0000 Subject: [PATCH 25/36] Added lirary(ThreadNet) to the Shiny App global app --- Inst/ThreadNet/global.R | 1 + 1 file changed, 1 insertion(+) diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index 477ff4e..fb100ae 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -29,6 +29,7 @@ library(DT) library(RColorBrewer) library(lubridate) library(knitr) +library(ThreadNet) From 5c027043e1cc40ee9344f18b26127f14f404f874 Mon Sep 17 00:00:00 2001 From: Mahmood Shafeie Zargar Date: Thu, 7 Jun 2018 11:29:35 +0200 Subject: [PATCH 26/36] Silencing package loads --- Inst/ThreadNet/global.R | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Inst/ThreadNet/global.R b/Inst/ThreadNet/global.R index fb100ae..221d5fd 100644 --- a/Inst/ThreadNet/global.R +++ b/Inst/ThreadNet/global.R @@ -12,26 +12,26 @@ # add the dependent packages -library(shiny) -library(shinyjs) -library(plotly) -library(ggplot2) -library(tidyverse) -library(ngram) -library(stringr) -library(stringdist) -library(igraph) -library(networkD3) -library(visNetwork) -library(xesreadR) -library(colorspace) -library(DT) -library(RColorBrewer) -library(lubridate) -library(knitr) -library(ThreadNet) - - +suppressPackageStartupMessages({ + library(shiny) + library(shinyjs) + library(plotly) + library(ggplot2) + library(tidyverse) + library(ngram) + library(stringr) + library(stringdist) + library(igraph) + library(networkD3) + library(visNetwork) + library(xesreadR) + library(colorspace) + library(DT) + library(RColorBrewer) + library(lubridate) + library(knitr) + library(ThreadNet) +}) # visualization types for UI dropdowns visualizations <- c( From c742a92041665c9631aaff8b2ee04d4b09c90fea Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 7 Jun 2018 16:49:26 +0000 Subject: [PATCH 27/36] Addded parameters to plotly_empty to suppress warnings. Also updated the acknowledgements to include link to the theme song --- R/ThreadNet_Graphics.R | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index e70240a..4a8a531 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -30,10 +30,10 @@ CF_multi_pie <- function(oc,CF){ # print(paste('in CF_multiPie, CF=',CF)) # avoid unpleasant error messages - if (length(CF)==0) {return(plotly_empty())} + if (length(CF)==0) {return(plotly_empty(type='scatter',mode='markers'))} # make sure the necessary columns are present - if (!all(CF %in% colnames(oc))) {return(plotly_empty())} + if (!all(CF %in% colnames(oc))) {return(plotly_empty(type='scatter',mode='markers'))} # first add the combined column if there is more than one if (length(CF) >1){ @@ -57,7 +57,7 @@ CF_multi_pie <- function(oc,CF){ # Now loop for each CF, computing entropy and adding on the next "trace" to the plot - # start with blank plot object + # start with blank plot object. Assign type=scatter to suppress warnings. pies = plot_ly() max_combos = 1 for (i in 1:nPlots) { @@ -105,11 +105,6 @@ CF_multi_pie <- function(oc,CF){ # CF = list of context factor names used to define events # r is the row name for the event being examined # -# KNOWN ISSUES: -# * Need to pass in the factor levels as labels for the pie slices -# * Need to compute the values differently for a node in the dendrogram or in a zoomed graph -# * Probably need to pass in the vector of values -# # Call this for one CF at a time # o is the raw occurrences. This is where we get the labels. # e is the events. This is where we get the frequencies @@ -144,9 +139,9 @@ make_df_for_one_pie <- function(o,e,cf,r,zm){ CF_multi_pie_event <- function(o, e,CF,r, zm){ # avoid unpleasant error messages - if (length(CF)==0) {return(plotly_empty())} + if (length(CF)==0) {return(plotly_empty(type='scatter',mode='markers'))} # print(paste('in CF_multi_pie_event, r=',r)) - if (is.na(as.numeric(r))) {return(plotly_empty())} + if (is.na(as.numeric(r))) {return(plotly_empty(type='scatter',mode='markers'))} # get number of plots nPlots = length(CF) @@ -169,8 +164,10 @@ CF_multi_pie_event <- function(o, e,CF,r, zm){ n=length # Now loop for each CF, computing entropy and adding on the next "trace" to the plot - # start with blank plot object + # start with blank plot object. Assign type = scatter to suppress warnings. +# pies = plot_ly(type='scatter') pies = plot_ly() + max_combos = 1 for (i in 1:nPlots) { @@ -604,7 +601,7 @@ filter_network_edges <- function(n, threshold){ role_map <- function(e, o, cfs){ if (!length(cfs)==2) - return(plot_ly()) + return(plot_ly(type='scatter')) # Get the context factors vcf_1 = paste0('V_',cfs[1]) From a2a300827161fa7a73dca4562dd36521e6fad16a Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 7 Jun 2018 17:16:59 +0000 Subject: [PATCH 28/36] Updated acknowledgements --- Inst/ThreadNet/ui/acknowledgements.R | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Inst/ThreadNet/ui/acknowledgements.R b/Inst/ThreadNet/ui/acknowledgements.R index 42411c9..3671e5e 100644 --- a/Inst/ThreadNet/ui/acknowledgements.R +++ b/Inst/ThreadNet/ui/acknowledgements.R @@ -1,14 +1,27 @@ tabPanel( "Acknowledgements", - tags$h4("Support"), - tags$a(href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=1734237","NSF SES-1734237"), + tags$h4("Author"), + tags$p("Brian Pentland, Michigan State University"), + + tags$h4("Contact"), + tags$mailto("ThreadNetHelp@gmail.com"), + + tags$h4("Financial Support"), + tags$a(href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=1734237","NSF SES-1734237",target="_blank"), tags$p("Antecedents of Complexity in Healthcare Routines"), + tags$h4("Code Gurus"), - tags$p("Yu Lucy Han, Ezra Brooks, Patrick Bills, Danielle Barnes, Morgan Patterson, Douglas Krum"), + tags$p("Danielle Barnes, Patrick Bills, Ezra Brooks, Yu Lucy Han, Morgan Patterson, Douglas Krum, Mahmood Shafeie Zargar"), + tags$h4("Collaborators"), tags$p("Katharina Dittrich, Martha Feldman, Ken Frank, Thorvald Haerem, Inkyu Kim, Waldemar Kremser, Christian Mahringer, Alice Pentland, Jan Recker, Sudhanshu Srivastava, Julie Ryan Wolf, George Wyner "), - tags$h4("Related Publications"), - tags$a(href="http://routines.broad.msu.edu/resources/","http://routines.broad.msu.edu/resources/" ), + + tags$h4("Documentation and Related Publications"), + tags$a(href="http://routines.broad.msu.edu/resources/","http://routines.broad.msu.edu/resources/",target="_blank" ), + tags$h4("ThreadNet 2 (MatLab version)"), - tags$a(href="http://routines.broad.msu.edu/ThreadNet/","http://routines.broad.msu.edu/ThreadNet/" ) + tags$a(href="http://routines.broad.msu.edu/ThreadNet/","http://routines.broad.msu.edu/ThreadNet/",target="_blank" ), + + tags$h4("Official Song"), + tags$a(href="Doctor Decade: Tell Us a Story","https://www.youtube.com/watch?v=tSnoHWBYA8U",target="_blank" ) ) From f216463f34cb599d1ccc71160b508da245d7e29e Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 7 Jun 2018 17:37:20 +0000 Subject: [PATCH 29/36] Small changes to suppress errors --- Inst/ThreadNet/ui/acknowledgements.R | 4 ++-- R/ThreadNet_Graphics.R | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Inst/ThreadNet/ui/acknowledgements.R b/Inst/ThreadNet/ui/acknowledgements.R index 3671e5e..72f151e 100644 --- a/Inst/ThreadNet/ui/acknowledgements.R +++ b/Inst/ThreadNet/ui/acknowledgements.R @@ -4,7 +4,7 @@ tabPanel( tags$p("Brian Pentland, Michigan State University"), tags$h4("Contact"), - tags$mailto("ThreadNetHelp@gmail.com"), + tags$a(href="mailto:ThreadNetHelp@gmail.com","ThreadNetHelp@gmail.com"), tags$h4("Financial Support"), tags$a(href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=1734237","NSF SES-1734237",target="_blank"), @@ -23,5 +23,5 @@ tabPanel( tags$a(href="http://routines.broad.msu.edu/ThreadNet/","http://routines.broad.msu.edu/ThreadNet/",target="_blank" ), tags$h4("Official Song"), - tags$a(href="Doctor Decade: Tell Us a Story","https://www.youtube.com/watch?v=tSnoHWBYA8U",target="_blank" ) + tags$a(href="https://www.youtube.com/watch?v=tSnoHWBYA8U", "Doctor Decade: Tell Us a Story", target="_blank" ) ) diff --git a/R/ThreadNet_Graphics.R b/R/ThreadNet_Graphics.R index 4a8a531..adfdbd1 100644 --- a/R/ThreadNet_Graphics.R +++ b/R/ThreadNet_Graphics.R @@ -455,7 +455,7 @@ Comparison_Plots <- function(e, o, CF, CF_levels, nTimePeriods=1, plot_type,rol else if (plot_type=='Threads (event time)') {plot_list[[plotName]] = threadMap(dfp, "threadNum", "seqNum", 1, 15 ) } else - {plot_list[[plotName]] = plotly_empty()} + {plot_list[[plotName]] = plotly_empty(type='scatter',mode='marker')} } }} From d7eec11dce4b366ca759a86fec7923659917b7ed Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Sat, 9 Jun 2018 19:34:43 -0400 Subject: [PATCH 30/36] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b56927..2978178 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,11 @@ ## Overview ThreadNet is a tool for visualization of repetitive sequences, such as organizational routines. It emphasizes the role of sequential and temporal context. It is being created for NSF (SES-1734237) Antecedents of Complexity in Healthcare Routine, a collaborative project between Michigan State University and the University of Rochester Medical Center. Co-PIs: Brian Pentland and Kenneth Frank (MSU), Julie Ryan Wolf and Alice Pentland (URMC). The original version of ThreadNet was implemented in MatLab. +## Documentation and sample data +You can access installation instructions, documentation and sample data here: http://routines.broad.msu.edu/ThreadNet + ## File format -ThreadNet reads data in simple .CSV format. We will soon support the IEEE standard .XES format for process event log data. +ThreadNet reads data in simple .CSV format and .XES format (IEEE standard for process event log data). ### First column must be either "tStamp" or "sequence" When using timestamped data, the first column must be called "tStamp". The timestamps should be in default R format: "yyyy-mm-dd hh:mm:ss" From accdf90a98374faeb8774bfa671913de16efe3bf Mon Sep 17 00:00:00 2001 From: Ezra Brooks Date: Mon, 18 Jun 2018 08:28:01 -0400 Subject: [PATCH 31/36] add function to allow shinyServer to host app directly from package --- R/runExample.R | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 R/runExample.R diff --git a/R/runExample.R b/R/runExample.R new file mode 100644 index 0000000..5220c1d --- /dev/null +++ b/R/runExample.R @@ -0,0 +1,9 @@ +#' @export +runExample <- function() { + appDir <- system.file("ThreadNet", package = "ThreadNet") + if (appDir == "") { + stop("Could not find shiny app directory. Try re-installing `ThreadNet`.", call. = FALSE) + } + + shiny::runApp(appDir, display.mode = "normal") +} From 2dde7d6be7f810ef481ece571377ecf9323a3800 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 23 Aug 2018 21:36:41 +0000 Subject: [PATCH 32/36] First attempt at saving the network --- Inst/ThreadNet/server.R | 6 ++++++ Inst/ThreadNet/server/visualize.R | 33 ++++++++++++++++++++++++++----- R/Event_Mappings.R | 21 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Inst/ThreadNet/server.R b/Inst/ThreadNet/server.R index 5df2925..e6fd321 100644 --- a/Inst/ThreadNet/server.R +++ b/Inst/ThreadNet/server.R @@ -320,6 +320,12 @@ server <- shinyServer(function(input, output, session) { showNotification(paste(input$ManageEventMapInputID, " exported as .csv file"), type='message', duration=10) }) + # This is on visualize tab, but logically it fits better here + observeEvent(input$save_edge_list_button,{ + export_network(input$ManageEventMapInputID, viz_net() ) + showNotification("Graph exported as edge list", type='message', duration=10) + }) + # Another opportunity to make subsets... observeEvent(input$SelectSubsetButton, if (check_POV_name(input$SelectSubsetMapName)){ diff --git a/Inst/ThreadNet/server/visualize.R b/Inst/ThreadNet/server/visualize.R index e11c0d7..7b60b45 100644 --- a/Inst/ThreadNet/server/visualize.R +++ b/Inst/ThreadNet/server/visualize.R @@ -3,6 +3,7 @@ #### Main Tab Output Functions #### # Controls for the whole set of tabs + output$Visualize_Tab_Controls_1 <- renderUI({ selectizeInput( "VisualizeEventMapInputID", @@ -81,6 +82,7 @@ output$WholeSequenceThreadMap_RelativeTime <- renderPlotly({ threadMap(threadedE # use this to select how to color the nodes in force layout output$Circle_Network_Tab_Controls <- renderUI({ tags$div( + actionButton("save_edge_list_button","Save this network view"), sliderInput("circleEdgeTheshold","Display edges above", 0,1,0,step = 0.01,ticks = FALSE ), radioButtons( "Label_or_Zoom_1", @@ -88,24 +90,45 @@ output$Circle_Network_Tab_Controls <- renderUI({ choices = c('Labels','Zooming'), selected = 'Labels', inline = TRUE) + ) }) - -output$circleVisNetwork <- renderVisNetwork({ +# Create the network to be exported and also displayed +viz_net <<- reactive({ req(input$circleEdgeTheshold) - # first convert the threads to the network + # first convert the threads to the network if (input$Label_or_Zoom_1 == 'Labels') { n <- threads_to_network_original(threadedEventsViz(), "threadNum", 'label') } else { n <- threads_to_network_original(threadedEventsViz(), "threadNum", get_Zoom_VIZ()) } # filter out the edges if desired - n <- filter_network_edges(n,input$circleEdgeTheshold) - circleVisNetwork(n, 'directed', TRUE) + n <- filter_network_edges(n,input$circleEdgeTheshold) + n }) +output$circleVisNetwork <- renderVisNetwork({ + req(input$circleEdgeTheshold) + + circleVisNetwork(viz_net(), 'directed', TRUE) +}) + +# output$circleVisNetwork <- renderVisNetwork({ +# req(input$circleEdgeTheshold) +# +# # first convert the threads to the network +# if (input$Label_or_Zoom_1 == 'Labels') +# { n <- threads_to_network_original(threadedEventsViz(), "threadNum", 'label') } +# else +# { n <- threads_to_network_original(threadedEventsViz(), "threadNum", get_Zoom_VIZ()) } +# +# # filter out the edges if desired +# n <- filter_network_edges(n,input$circleEdgeTheshold) +# circleVisNetwork(n, 'directed', TRUE) +# }) + #### Other Networks sub-tab #### # use this to select how to color the nodes in force layout diff --git a/R/Event_Mappings.R b/R/Event_Mappings.R index 8b5e960..4781c2b 100644 --- a/R/Event_Mappings.R +++ b/R/Event_Mappings.R @@ -191,3 +191,24 @@ export_POV_csv <- function(mapname){ write.csv(output, file=file.choose(), quote = TRUE, row.names = FALSE) } + +#' @title export_network +#' @description Exports the edge list for the graph that is displayed +#' @name export_network +#' @param mapname name of POV map to use as name of the file +#' @param n +#' @return (saves network into file) +#' @export +export_network <- function(mapname, CurrentNetwork ){ + + # get the nice variable names + nicename = paste0("CurrentNetwork_from_",mapname) + + # get the edge list for the network + # edge_list <- n.edgeDF + + # save the data + save( CurrentNetwork , file = paste0(nicename,".Rdata")) + +} + From 366a3960e7e7a3358f8e64c84bb9ed56b9a3d0e0 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Fri, 24 Aug 2018 13:51:22 +0000 Subject: [PATCH 33/36] Changed to use downloadButton --- Inst/ThreadNet/server.R | 18 ++++++++++++++++-- Inst/ThreadNet/server/visualize.R | 3 ++- R/Event_Mappings.R | 3 +++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Inst/ThreadNet/server.R b/Inst/ThreadNet/server.R index e6fd321..2951e70 100644 --- a/Inst/ThreadNet/server.R +++ b/Inst/ThreadNet/server.R @@ -320,10 +320,24 @@ server <- shinyServer(function(input, output, session) { showNotification(paste(input$ManageEventMapInputID, " exported as .csv file"), type='message', duration=10) }) + CurrentNetwork <- reactiveValues() + observe({ + if(!is.null(viz_net())) + isolate( + CurrentNetwork <<- viz_net() + ) + }) + + output$downloadNetwork <- downloadHandler( + filename = paste("CurrentNetwork_POV.Rdata"), + # content = function(file) { save( eval(assign('CurrentNetwork',viz_net())) , file = file) } + content = function(file) { save( CurrentNetwork , file = file) } + ) + # This is on visualize tab, but logically it fits better here observeEvent(input$save_edge_list_button,{ - export_network(input$ManageEventMapInputID, viz_net() ) - showNotification("Graph exported as edge list", type='message', duration=10) + export_network(input$VisualizeEventMapInputID, viz_net() ) + showNotification("Exported to CurrentNetwork_POV.Rdata", type='message', duration=10) }) # Another opportunity to make subsets... diff --git a/Inst/ThreadNet/server/visualize.R b/Inst/ThreadNet/server/visualize.R index 7b60b45..e93418d 100644 --- a/Inst/ThreadNet/server/visualize.R +++ b/Inst/ThreadNet/server/visualize.R @@ -82,7 +82,7 @@ output$WholeSequenceThreadMap_RelativeTime <- renderPlotly({ threadMap(threadedE # use this to select how to color the nodes in force layout output$Circle_Network_Tab_Controls <- renderUI({ tags$div( - actionButton("save_edge_list_button","Save this network view"), + downloadButton('downloadNetwork', 'Export this Network', class="dlButton"), sliderInput("circleEdgeTheshold","Display edges above", 0,1,0,step = 0.01,ticks = FALSE ), radioButtons( "Label_or_Zoom_1", @@ -109,6 +109,7 @@ viz_net <<- reactive({ n }) + output$circleVisNetwork <- renderVisNetwork({ req(input$circleEdgeTheshold) diff --git a/R/Event_Mappings.R b/R/Event_Mappings.R index 4781c2b..1a5b0f8 100644 --- a/R/Event_Mappings.R +++ b/R/Event_Mappings.R @@ -203,12 +203,15 @@ export_network <- function(mapname, CurrentNetwork ){ # get the nice variable names nicename = paste0("CurrentNetwork_from_",mapname) + nicename = "CurrentNetwork_POV" # get the edge list for the network # edge_list <- n.edgeDF + print('saving network: CurrentNetwork_POV.Rdata') # save the data save( CurrentNetwork , file = paste0(nicename,".Rdata")) + print(' network saved') } From 9a79a452651a4065ccd45af1b3a6080d5c31bf48 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Mon, 24 Jun 2019 20:43:11 +0000 Subject: [PATCH 34/36] Added in estimated number of paths for circle network --- Inst/ThreadNet/server/visualize.R | 9 +++++++++ Inst/ThreadNet/ui/visualize.R | 2 ++ R/ThreadNet_Metrics.R | 17 ++++++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Inst/ThreadNet/server/visualize.R b/Inst/ThreadNet/server/visualize.R index e93418d..d67c604 100644 --- a/Inst/ThreadNet/server/visualize.R +++ b/Inst/ThreadNet/server/visualize.R @@ -94,6 +94,15 @@ output$Circle_Network_Tab_Controls <- renderUI({ ) }) +output$Circle_Network_Path_Estimate <- renderText({ + paste0('Estimated paths = ', + estimate_network_complexity(viz_net()) ) +}) + +output$Network_Nodes_Edges <- renderText({ + paste0( print_network_nodes_edges(viz_net()) ) +}) + # Create the network to be exported and also displayed viz_net <<- reactive({ req(input$circleEdgeTheshold) diff --git a/Inst/ThreadNet/ui/visualize.R b/Inst/ThreadNet/ui/visualize.R index a2adf11..ad0c4e3 100644 --- a/Inst/ThreadNet/ui/visualize.R +++ b/Inst/ThreadNet/ui/visualize.R @@ -40,6 +40,8 @@ tabPanel(value = "visualize", tabPanel( "Event network (circle)", uiOutput("Circle_Network_Tab_Controls"), + textOutput("Circle_Network_Path_Estimate"), + textOutput("Network_Nodes_Edges"), visNetworkOutput("circleVisNetwork", width = "100%", height = "1200px") ), tabPanel( diff --git a/R/ThreadNet_Metrics.R b/R/ThreadNet_Metrics.R index 5a92e6d..ee16a5b 100644 --- a/R/ThreadNet_Metrics.R +++ b/R/ThreadNet_Metrics.R @@ -1,4 +1,4 @@ -########################################################################################################## + ########################################################################################################## # THREADNET: Metrics # This software may be used according to the terms provided in the @@ -17,6 +17,9 @@ #' @export estimate_network_complexity <- function(net){ return(estimate_task_complexity_index( nrow(net$nodeDF), nrow(net$edgeDF)) ) } +# returns a string with the number of nodes and edges in the network +print_network_nodes_edges <- function(net){ return(paste0('Number of nodes = ', nrow(net$nodeDF),' Number of edges = ', nrow(net$edgeDF) ) ) } + #' @title Estimates the number of paths in a directed graph #' @description Same as estimate_network_complexity, but takes this version takes vertices and edges as parameters @@ -32,17 +35,17 @@ estimate_task_complexity_index <- function(v,e){ # v = number of vertices # tested for range of 10 < v < 100 # e = number of edges - print("edges") - print(e) - print("vertices") - print(v) + # print("edges") + # print(e) + # print("vertices") + # print(v) # # OUTPUT ARG: # cidx correlates with Log10(simple paths) with r>= 0.8 # from ORM paper analysis, constant is 0.12. # For boundary condition of 2 nodes and 1 edge, complexity index=0, constant = 0.08 - return( 0.08 + 0.08*e - 0.08*v ) + return( 10^( 0.08 + 0.08*e - 0.08*v) ) } @@ -84,7 +87,7 @@ compression_index <- function(df,CF){ return( length(paste0(as.character(df[[CF]]))) ) } -####################################################################### +#######################################################################es #compute entropy for a set of observations in a column from a data frame #' @title Compute the entropy of a contextual factor #' @description Each column in the raw data represents a contextual factor. This function computes the entropy of each factor that is selected for use in the From 4553576cc1a12d3171735fe40d7e4eebef6e07fe Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Thu, 18 Jul 2019 22:03:30 +0000 Subject: [PATCH 35/36] Stopped filtering selfies for the networks. --- Inst/ThreadNet/server/visualize.R | 2 +- R/ThreadNet_Core.R | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Inst/ThreadNet/server/visualize.R b/Inst/ThreadNet/server/visualize.R index d67c604..0a012ed 100644 --- a/Inst/ThreadNet/server/visualize.R +++ b/Inst/ThreadNet/server/visualize.R @@ -96,7 +96,7 @@ output$Circle_Network_Tab_Controls <- renderUI({ output$Circle_Network_Path_Estimate <- renderText({ paste0('Estimated paths = ', - estimate_network_complexity(viz_net()) ) + round(estimate_network_complexity(viz_net()) ),1) }) output$Network_Nodes_Edges <- renderText({ diff --git a/R/ThreadNet_Core.R b/R/ThreadNet_Core.R index 55607df..6fc381e 100644 --- a/R/ThreadNet_Core.R +++ b/R/ThreadNet_Core.R @@ -75,14 +75,16 @@ threads_to_network_original <- function(et,TN,CF,grp='threadNum'){ to[i] = match(to_labels[i], nodes$label) } + # Stopped filtering out selfies July 20, 2019 for Kerstin Sailer bug report edges = data.frame( from, to, label = ngdf$freq, - Value =ngdf$freq) %>% filter(!from==to) + Value =ngdf$freq) # %>% filter(!from==to) # print(paste("T2N nodes:",nodes)) - # print(paste("T2N edges:",edges)) + # print(paste("ngdf = :",ngdf)) + # print(paste("edges= :",edges)) return(list(nodeDF = nodes, edgeDF = edges)) } @@ -163,10 +165,13 @@ count_ngrams <- function(o,TN,CF,n){ # Need a vector of strings, one for each thread, delimited by spaces # the function long_enough filters out the threads that are shorter than n # use space for the delimiter here - text_vector = long_enough( thread_text_vector(o,TN,CF,' '), n, ' ') + text_vector = long_enough( thread_text_vector(o,TN,CF,' '), n, ' ') + # text_vector = thread_text_vector(o,TN,CF,' ') + + + # print(paste0("thread=", o[1,TN] ,", text_vector")) + # print(text_vector) - # print("text_vector") - # print(text_vector) ng = get.phrasetable(ngram(text_vector,n)) From cf7ffc3dc649fa1092be10dd48e9757344beb4e6 Mon Sep 17 00:00:00 2001 From: Brian Pentland Date: Fri, 26 Jul 2019 11:11:06 -0400 Subject: [PATCH 36/36] fixed bug in visualize network --- R/ThreadNet_Metrics.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/ThreadNet_Metrics.R b/R/ThreadNet_Metrics.R index ee16a5b..1f5b1c6 100644 --- a/R/ThreadNet_Metrics.R +++ b/R/ThreadNet_Metrics.R @@ -18,6 +18,12 @@ estimate_network_complexity <- function(net){ return(estimate_task_complexity_index( nrow(net$nodeDF), nrow(net$edgeDF)) ) } # returns a string with the number of nodes and edges in the network +#' @title returns a string with the number of nodes and edges in the network +#' @description returns a string with the number of nodes and edges +#' @name print_network_nodes_edges +#' @param net Object with dataframe for nodes and edges +#' @return string +#' @export print_network_nodes_edges <- function(net){ return(paste0('Number of nodes = ', nrow(net$nodeDF),' Number of edges = ', nrow(net$edgeDF) ) ) }