|
| 1 | +#' "individuals_by" |
| 2 | +#' |
| 3 | +#' @description individuals_by function creates a force-directed network visualization using the forceNetwork function from the networkD3 package. |
| 4 | +#' |
| 5 | +#' @param individual_data The data frame containing the individual-level data. By default, it assumes a data frame called `individuals`. |
| 6 | +#' @param ind_id The variable/column in the `individual_data` data frame that represents the individual identifier. By default, it assumes a column named "nom". |
| 7 | +#' @param group The variable/column in the `individual_data` data frame that represents the grouping variable. It is used to assign colors to the nodes in the visualization. If not provided, the visualization will not group the nodes. |
| 8 | +#' @param group_other is a string representing the name of the column in the "individual_data" data block that contains additional group information for each individual. |
| 9 | +#' @param group_other_name is a string representing a custom name for the "group_other" column to display in the plot. |
| 10 | +#' @param font_size numeric font size in pixels for the node text labels.By default, it is set to 7. |
| 11 | +#' @param height numeric height for the network graph's frame area in pixels. |
| 12 | +#' @param width numeric width for the network graph's frame area in pixels. |
| 13 | +#' @param colour_scale character string specifying the categorical colour scale for the nodes. See \code{https://github.com/d3/d3/blob/master/API.md#ordinal-scales}. |
| 14 | +#' @param font_family font family for the node text labels. |
| 15 | +#' @param link_distance numeric or character string. Either numberic fixed distance between the links in pixels (actually arbitrary relative to the diagram's size). Or a JavaScript function, possibly to weight by Value. For example: linkDistance = JS("function(d){return d.value * 10}"). |
| 16 | +#' @param link_width numeric or character string. Can be a numeric fixed width in pixels (arbitrary relative to the diagram's size). Or a JavaScript function, possibly to weight by Value. The default is linkWidth = JS("function(d) { return Math.sqrt(d.value); }"). |
| 17 | +#' @param radius_calculation character string. A javascript mathematical expression, to weight the radius by Nodesize. The default value is radiusCalculation = JS("Math.sqrt(d.nodesize)+6"). |
| 18 | +#' @param charge numeric value indicating either the strength of the node repulsion (negative value) or attraction (positive value). |
| 19 | +#' @param link_colour character vector specifying the colour(s) you want the link lines to be. Multiple formats supported (e.g. hexadecimal). |
| 20 | +#' @param opacity numeric value of the proportion opaque you would like the graph elements to be. |
| 21 | +#' @param zoom logical value to enable (TRUE) or disable (FALSE) zooming. |
| 22 | +#' @param arrows logical value to enable directional link arrows. |
| 23 | +#' @param bounded logical value to enable (TRUE) or disable (FALSE) the bounding box limiting the graph's extent. See \code{http://bl.ocks.org/mbostock/1129492}. |
| 24 | +#' @param display_labels is a numeric value representing the number of characters of the label to display on each node. |
| 25 | +#' @param click_action character string with a JavaScript expression to evaluate when a node is clicked. |
| 26 | +#' |
| 27 | +#' @return Returns a network graph object |
| 28 | +#' @export |
| 29 | +#' |
| 30 | +#' @examples # TODO |
| 31 | +individuals_by <- function(individual_data = individuals, ind_id = nom, group = NULL, group_other = NULL, group_other_name = NULL, |
| 32 | + font_size = 7, height = NULL, width = NULL, colour_scale = JS("d3.scaleOrdinal(d3.schemeCategory20);"), |
| 33 | + font_family = "serif", link_distance = 50, link_width = JS("function(d) { return Math.sqrt(d.value); }"), |
| 34 | + radius_calculation = "4*Math.sqrt(d.nodesize)+2", charge = -30, |
| 35 | + link_colour = "#666", opacity = 0.6, zoom = FALSE, arrows = FALSE, |
| 36 | + bounded = FALSE, display_labels = 0, click_action = NULL){ |
| 37 | + |
| 38 | + # if (!is.null(filter_var)){ |
| 39 | + # individual_data <- individual_data %>% |
| 40 | + # filter(.data[[filter_var]] %in% filter_vals) |
| 41 | + # } |
| 42 | + |
| 43 | + # ind data |
| 44 | + ind <- individual_data %>% |
| 45 | + dplyr::mutate(id_ind = gsub("^.*?/","", {{ ind_id }})) |
| 46 | + |
| 47 | + # put this if statement elsewhere - own function |
| 48 | + # replace "other" columns with what the user selected: |
| 49 | + if (group %in% c("pays", "activite_prof")){ |
| 50 | + if (group == "pays") { |
| 51 | + group_other_name = "autre_pays" |
| 52 | + } else if (group %in% c("activite_prof")){ |
| 53 | + group_other_name = "autre" |
| 54 | + } |
| 55 | + ind <- replace_other(data = ind, |
| 56 | + group = group, |
| 57 | + group_other_name = group_other_name, |
| 58 | + RAS = TRUE) |
| 59 | + } |
| 60 | + |
| 61 | + ind <- replace_other(data = ind, |
| 62 | + group = "institutions_associees", |
| 63 | + group_other_name = "autre") |
| 64 | + ind <- replace_other(data = ind, |
| 65 | + group = "initiatives_associees", |
| 66 | + group_other_name = "autre_initiative") |
| 67 | + |
| 68 | + # for prep fun |
| 69 | + ind.init_inst <- ind %>% |
| 70 | + # Create a variable giving the institution |
| 71 | + tidyr::pivot_longer(cols = starts_with("institutions_associees/"), names_to = "id_inst") %>% |
| 72 | + dplyr::mutate(id_inst = gsub("^.*?/", "", id_inst)) %>% |
| 73 | + dplyr::mutate(id_inst = gsub("institutions_associees/","", id_inst)) %>% |
| 74 | + dplyr::filter(value == 1) %>% |
| 75 | + dplyr::select(!value) %>% |
| 76 | + |
| 77 | + # Create a variable giving the initiative |
| 78 | + tidyr::pivot_longer(cols = starts_with("initiatives_associees/"), names_to = "id_init") %>% |
| 79 | + dplyr::mutate(id_init = gsub("^.*?/","", id_init)) %>% |
| 80 | + dplyr::mutate(id_init = gsub("initiatives_associees/","", id_init)) %>% |
| 81 | + dplyr::filter(value == 1) %>% |
| 82 | + dplyr::select(!value)%>% |
| 83 | + |
| 84 | + # combine the initiative and institution column into one |
| 85 | + tidyr::pivot_longer(cols = c(id_inst,id_init), |
| 86 | + names_to = "inst_init_type", values_to = "id_inst_init") %>% |
| 87 | + dplyr::mutate(inst_init_type = case_when( |
| 88 | + inst_init_type == 'id_inst' ~ "Institution", |
| 89 | + inst_init_type == 'id_init' ~ "Initiative")) |
| 90 | + |
| 91 | + # Split the data `ind.init_inst` to just Institutions |
| 92 | + inst.ind <- ind.init_inst %>% |
| 93 | + dplyr::filter(inst_init_type == "Institution") %>% |
| 94 | + dplyr::distinct(id_inst_init) %>% |
| 95 | + dplyr::rename(id_inst = id_inst_init) |
| 96 | + |
| 97 | + # Split the data `ind.init_inst` to just Initatives |
| 98 | + init.ind <- ind.init_inst %>% |
| 99 | + dplyr::filter(inst_init_type == "Initiative") %>% |
| 100 | + dplyr::distinct(id_inst_init) %>% |
| 101 | + dplyr::rename(id_init = id_inst_init) # rename "id_init" by "id_inst_init" |
| 102 | + |
| 103 | + # Creating the node data: bind our three data sets together |
| 104 | + # (individual, inst.ind, init.ind) |
| 105 | + # Add a column to state if it is individual, institution, or initiative data |
| 106 | + nodes_init_by <- bind_rows ( |
| 107 | + "Individu(e)" = rename(ind, id=id_ind), |
| 108 | + "Institution" = rename(inst.ind, id=id_inst), |
| 109 | + "Initiative" = rename(init.ind, id=id_init), |
| 110 | + .id = "type") %>% |
| 111 | + # add a weight |
| 112 | + tidyr::mutate(type_weight = case_when( |
| 113 | + type == 'Individu(e)' ~ 1, |
| 114 | + type == 'Institution' ~ 2, |
| 115 | + type == 'Initiative' ~ 3)) |
| 116 | + |
| 117 | + # Create notes and links data |
| 118 | + # if there is a group variable, then create a "group_type" variable |
| 119 | + if (!is.null(group)){ |
| 120 | + nodes_init_by <- nodes_init_by %>% |
| 121 | + dplyr::mutate(group_type = case_when( |
| 122 | + type == 'Individu(e)' ~ .data[[group]], |
| 123 | + type == 'Institution' ~ "Institution", |
| 124 | + type == 'Initiative' ~ "Initiative")) %>% |
| 125 | + dplyr::mutate(id_index = row_number() - 1) %>% |
| 126 | + dplyr::select(c("id", "id_index", "type_weight", "group_type")) |
| 127 | + } else { |
| 128 | + nodes_init_by <- nodes_init_by %>% |
| 129 | + dplyr::mutate(id_index = row_number()-1) %>% |
| 130 | + dplyr::select(c("id", "id_index", "type", "type_weight")) |
| 131 | + } |
| 132 | + |
| 133 | + # Merge the id_index column into the ind.init_inst data by initiative |
| 134 | + # set this id_index column to be "target" |
| 135 | + links_init_by <- ind.init_inst %>% |
| 136 | + dplyr::left_join(y=rename(select(nodes_init_by, id, id_index), |
| 137 | + target=id_index), |
| 138 | + by = c("id_inst_init"="id")) |
| 139 | + |
| 140 | + # Now merge in the id_index column into the ind.init_inst data by individual |
| 141 | + # set this id_index column to be "source" |
| 142 | + links_init_by <- links_init_by %>% |
| 143 | + dplyr::left_join(y=rename(select(nodes_init_by, id, id_index), |
| 144 | + source=id_index), |
| 145 | + by = c("id_ind"="id")) |
| 146 | + |
| 147 | + # Add a numerical weight to whether it is institution or initiative |
| 148 | + links_init_by <- links_init_by %>% |
| 149 | + dplyr::mutate(inst_init_type_weight = case_when( |
| 150 | + inst_init_type == "Institution" ~ '2', |
| 151 | + inst_init_type == "Initiative" ~ '1')) %>% |
| 152 | + dplyr::select(c(target, source, inst_init_type, inst_init_type_weight)) |
| 153 | + |
| 154 | + if (display_labels) { |
| 155 | + display_labels = 1 |
| 156 | + } else { |
| 157 | + display_labels = 0 |
| 158 | + } |
| 159 | + |
| 160 | + if (is.null(group)){ |
| 161 | + networkD3::forceNetwork(Links = links_init_by, |
| 162 | + Nodes = nodes_init_by, |
| 163 | + Source = "source", |
| 164 | + Target = "target", |
| 165 | + NodeID = "id", |
| 166 | + Nodesize = "type_weight", |
| 167 | + Group = "type", |
| 168 | + legend = TRUE, |
| 169 | + fontSize = font_size, |
| 170 | + height = height, width = width, |
| 171 | + colourScale = colour_scale, fontFamily = font_family, |
| 172 | + linkDistance = link_distance, linkWidth = link_width, |
| 173 | + radiusCalculation = radius_calculation, charge = charge, |
| 174 | + linkColour = link_colour, opacity = opacity, zoom = zoom, |
| 175 | + arrows = arrows, bounded = bounded, opacityNoHover = display_labels, |
| 176 | + clickAction = click_action) |
| 177 | + } |
| 178 | + else { |
| 179 | + networkD3::forceNetwork(Links = links_init_by, |
| 180 | + Nodes = nodes_init_by, |
| 181 | + Source = "source", |
| 182 | + Target = "target", |
| 183 | + NodeID = "id", |
| 184 | + Nodesize = "type_weight", |
| 185 | + Group = "group_type", |
| 186 | + legend = TRUE, |
| 187 | + fontSize = font_size, |
| 188 | + height = height, width = width, |
| 189 | + colourScale = colour_scale, fontFamily = font_family, |
| 190 | + linkDistance = link_distance, linkWidth = link_width, |
| 191 | + radiusCalculation = radius_calculation, charge = charge, |
| 192 | + linkColour = link_colour, opacity = opacity, zoom = zoom, |
| 193 | + arrows = arrows, bounded = bounded, opacityNoHover = display_labels, |
| 194 | + clickAction = click_action) |
| 195 | + } |
| 196 | +} |
| 197 | + |
0 commit comments