diff --git a/DESCRIPTION b/DESCRIPTION index ed7e6ead..513f8eb7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: manynet Title: Many Ways to Make, Modify, Mark, and Map Myriad Networks -Version: 0.4.1 -Date: 2024-01-24 +Version: 0.4.2 +Date: 2024-03-12 Description: A set of tools for making, modifying, marking, and mapping many different types of networks. All functions operate with matrices, edge lists, and 'igraph', 'network', and 'tidygraph' objects, and on one-mode, two-mode (bipartite), and sometimes three-mode networks. @@ -15,7 +15,7 @@ License: MIT + file LICENSE Language: en-GB Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.1 Imports: dplyr (>= 1.1.0), ggplot2, diff --git a/NAMESPACE b/NAMESPACE index 0bd0d9f5..017381fb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -338,6 +338,7 @@ export(join_ties) export(labs) export(layout_tbl_graph_alluvial) export(layout_tbl_graph_concentric) +export(layout_tbl_graph_configuration) export(layout_tbl_graph_hierarchy) export(layout_tbl_graph_ladder) export(layout_tbl_graph_lineage) diff --git a/NEWS.md b/NEWS.md index 9ddd3c4d..b26d113f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,23 @@ +# manynet 0.4.2 + +2024-03-12 + +## Making + +- Closed #57 by updating `play_diffusions()` to revert future plan on exit +- Fixed bug with how `generate_random()` works for two-mode networks with specified number of ties + +## Mapping + +- Closed #6 by updating how "lineage" layout works and places nodes on Y axis +- Closed #39 by making `autographr()` more flexible and efficient in setting variables to aesthetics +- Updated themes to be compatible with newer and older versions of `{ggplot2}` +- Added "configuration" layout for small triad/quad networks + +## Modifying + +- Updated `to_reciprocated.matrix()` to consistently work with matrices + # manynet 0.4.1 2023-12-24 diff --git a/R/make_generate.R b/R/make_generate.R index ed848683..698b43a9 100644 --- a/R/make_generate.R +++ b/R/make_generate.R @@ -79,7 +79,7 @@ generate_random <- function(n, p = 0.5, directed = FALSE, with_attr = TRUE) { if(!as.integer(p)==p) stop("`p` must be an integer if above 1.") g <- igraph::sample_bipartite(n[1], n[2], m = p, - type = "gmp", + type = "gnm", directed = directed, mode = "out") } else { diff --git a/R/make_play.R b/R/make_play.R index 1e8e9f6e..333d3e04 100644 --- a/R/make_play.R +++ b/R/make_play.R @@ -257,8 +257,11 @@ play_diffusions <- function(.data, verbose = FALSE) { thisRequires("future") thisRequires("furrr") + oplan <- future::plan(strategy) + on.exit(future::plan(oplan), add = TRUE) + if(missing(steps)) steps <- network_nodes(.data) - future::plan(strategy) + out <- furrr::future_map_dfr(1:times, function(j){ data.frame(sim = j, play_diffusion(.data, diff --git a/R/manip_reformat.R b/R/manip_reformat.R index 2324a86d..d618be6a 100644 --- a/R/manip_reformat.R +++ b/R/manip_reformat.R @@ -212,7 +212,7 @@ to_reciprocated.tbl_graph <- function(.data) { #' @export to_reciprocated.matrix <- function(.data) { - as_matrix(to_reciprocated(as_igraph(.data))) + .data + t(.data) } #' @export diff --git a/R/map_autographr.R b/R/map_autographr.R index 01c1ac72..36b59ffa 100644 --- a/R/map_autographr.R +++ b/R/map_autographr.R @@ -59,7 +59,7 @@ #' It is easiest if this is added as an edge or tie attribute #' to the graph before plotting. #' Edges can also be colored by declaring a color instead. -#' @param edge_size Edge variable to be used for sizing the edges. +#' @param edge_size Tie variable to be used for sizing the edges. #' This can be any continuous variable on the nodes of the network. #' Since this function expects this to be an existing variable, #' it is recommended to calculate all edge-related statistics prior @@ -75,40 +75,38 @@ NULL #' @describeIn autographing Graphs a network with sensible defaults #' @examples -#' #autographr(ison_adolescents) -#' #ison_adolescents %>% -#' # mutate(year = rep(c(1985, 1990, 1995, 2000), times = 2)) %>% -#' #autographr(layout = "lineage", rank = "year") -#' #autographr(ison_algebra, layout = "circle", -#' # node_size = 8, node_color = "orange", node_shape = "square", -#' # edge_color = "blue", edge_size = 2) +#' autographr(ison_adolescents) +#' autographr(ison_algebra, layout = "circle", +#' node_size = 8, node_color = "orange", node_shape = "square", +#' edge_color = "blue", edge_size = 2) +#' autographr(ison_southern_women, layout = "concentric", +#' node_color = "type", membership = "type") +#' autographr(play_diffusion(ison_karateka)) #' #autographr(ison_algebra, edge_color = "type", -#' # node_size = migraph::node_betweenness(ison_algebra)*100) +#' # node_size = migraph::node_betweenness(ison_algebra)*100) +#' #ison_adolescents %>% +#' # mutate(year = rep(c(1985, 1990, 1995, 2000), times = 2)) %>% +#' # autographr(layout = "lineage", rank = "year") #' #ison_adolescents %>% #' # mutate(cut = node_is_cutpoint(ison_adolescents)) %>% #' #autographr(node_color = "cut", node_shape = "cut") #' #autographr(ison_lotr, node_color = Race, -#' # node_size = migraph::node_degree(ison_lotr)*2, -#' # edge_color = "darkgreen", -#' # edge_size = migraph::tie_degree(ison_lotr)) +#' # node_size = migraph::node_degree(ison_lotr)*2, +#' # edge_color = "darkgreen", +#' # edge_size = migraph::tie_degree(ison_lotr)) #' #autographr(ison_karateka, node_group = allegiance, -#' # edge_size = migraph::tie_closeness(ison_karateka)) -#' #autographr(ison_southern_women, layout = "concentric", -#' # node_color = "type", membership = "type") +#' # edge_size = migraph::tie_closeness(ison_karateka)) #' #autographr(ison_southern_women, layout = "hierarchy", center = "events") #' #autographr(ison_lotr, layout = "multilevel", -#' # node_color = "Race", level = "Race") -#' #autographr(play_diffusion(ison_karateka)) +#' # node_color = "Race", level = "Race") #' @export autographr <- function(.data, layout, labels = TRUE, node_color, node_shape, node_size, node_group, edge_color, edge_size, ...) { g <- as_tidygraph(.data) if (missing(layout)) { - if (length(g) == 3) { - layout <- "triad" - } else if (length(g) == 4) { - layout <- "quad" + if (length(g) <= 4) { + layout <- "configuration" } else if (is_twomode(g)) { layout <- "hierarchy" } else layout <- "stress" @@ -217,14 +215,18 @@ autographs <- function(netlist, waves, #' # mutate_ties(year = sample(1995:1998, 10, replace = TRUE)) %>% #' # to_waves(attribute = "year", cumulative = TRUE) %>% #' # autographd() -#' #ison_adolescents %>% -#' # mutate(shape = rep(c("circle", "square"), times = 4), -#' # color = rep(c("blue", "red"), times = 4), -#' # size = sample(4:16, 8, replace = TRUE)) %>% -#' # mutate_ties(year = sample(1995:1998, 10, replace = TRUE)) %>% +#' #ison_adolescents %>% +#' # mutate(gender = rep(c("male", "female"), times = 4), +#' # hair = rep(c("black", "brown"), times = 4), +#' # age = sample(11:16, 8, replace = TRUE)) %>% +#' # mutate_ties(year = sample(1995:1998, 10, replace = TRUE), +#' # links = sample(c("friends", "not_friends"), 10, replace = TRUE), +#' # weekly_meetings = sample(c(3, 5, 7), 10, replace = TRUE)) %>% #' # to_waves(attribute = "year") %>% -#' # autographd(layout = "circle", node_shape = "shape", -#' # node_color = "color", node_size = "size") +#' # autographd(layout = "concentric", membership = "gender", +#' # node_shape = "gender", node_color = "hair", +#' # node_size = "age", edge_color = "links", +#' # edge_size = "weekly_meetings") #' #autographd(play_diffusion(ison_adolescents, seeds = 5, recovery = 0.1)) #' @export autographd <- function(tlist, layout, labels = TRUE, @@ -257,9 +259,7 @@ autographd <- function(tlist, layout, labels = TRUE, edge_size <- as.character(substitute(edge_size)) } # Check if diffusion model - if (inherits(tlist, "diff_model")) { - tlist <- to_waves(tlist) - } + if (inherits(tlist, "diff_model")) tlist <- to_waves(tlist) # Check if object is a list of lists if (!is.list(tlist[[1]])) { stop("Please declare a migraph-compatible network listed according @@ -281,9 +281,12 @@ autographd <- function(tlist, layout, labels = TRUE, frame = ifelse(is.null(names(tlist)), i, names(tlist)[i]))) # Check if all names are present in all lists if (length(unique(unname(lapply(tlist, length)))) != 1) { - tlist <- to_waves(as_tidygraph(do.call("rbind", edges_lst)), - attribute = "frame") - } + if (any(c(node_shape, node_color, node_size) %in% names(node_attribute(tlist[[1]])))) { + node_info <- dplyr::distinct(do.call(rbind, lapply(1:length(tlist), function(i) + tlist[[i]] %>% activate("nodes") %>% data.frame()))) # keep node info for latter + } else node_info <- NULL + tlist <- to_waves(as_tidygraph(do.call("rbind", edges_lst)), attribute = "frame") + } else node_info <- NULL # Add separate layouts for each time point lay <- lapply(1:length(tlist), function(i) ggraph::create_layout(tlist[[i]], layout, ...)) @@ -294,7 +297,7 @@ autographd <- function(tlist, layout, labels = TRUE, frame = ifelse(is.null(names(tlist)), i, names(tlist)[i])) }) # Create an edge list for each time point - edges_lst <- time_edges_lst(tlist, edges_lst, nodes_lst, edge_color) + edges_lst <- time_edges_lst(tlist, edges_lst, nodes_lst) # Get edge IDs for all edges all_edges <- do.call("rbind", lapply(tlist, igraph::get.edgelist)) all_edges <- all_edges[!duplicated(all_edges), ] @@ -304,6 +307,9 @@ autographd <- function(tlist, layout, labels = TRUE, # Bind nodes and edges list edges_out <- do.call("rbind", edges_lst) nodes_out <- do.call("rbind", nodes_lst) + if (!is.null(node_info)) { + nodes_out <- dplyr::left_join(nodes_out, node_info[!duplicated(node_info$name),], by = "name") + } # Delete nodes for each frame if isolate if (isFALSE(keep_isolates)) { nodes_out <- remove_isolates(edges_out, nodes_out) @@ -378,12 +384,6 @@ reduce_categories <- function(g, node_group) { } } } - # if (layout == "stress" & .is_diamond(g)) { - # turn <- matrix(c(cos(0.71), -sin(0.71), sin(0.71), cos(0.71)), 2, 2) - # coord <- matrix(cbind(lo[,1], lo[,2]), ncol = 2) %*% turn - # lo[,1] <- coord[,1] - # lo[,2] <- coord[,2] - # } p <- ggraph::ggraph(lo) + ggplot2::theme_void() if (labels & is_labelled(g)) { if (layout == "circle") { @@ -402,7 +402,7 @@ reduce_categories <- function(g, node_group) { hj <- ifelse(lo[,1] >= 0, -0.2, 1.2) vj <- ifelse(lo[,2] >= 0, -0.2, 1.2) } - p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), + p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), repel = TRUE, size = 3, hjust = hj, angle = angles) + ggplot2::coord_cartesian(xlim=c(-1.2,1.2), ylim=c(-1.2,1.2)) } else if (layout == "concentric") { @@ -416,31 +416,24 @@ reduce_categories <- function(g, node_group) { hj <- ifelse(lo[,1] >= 0, -0.2, 1.2) vj <- ifelse(lo[,2] >= 0, -0.2, 1.2) } - p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), - size = 3, hjust = hj, - vjust = vj, check_overlap = TRUE) + + p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), vjust = vj, + size = 3, hjust = hj, repel = TRUE) + ggplot2::coord_cartesian(xlim=c(-1.2,1.2), ylim=c(-1.2,1.2)) - } else if (layout %in% c("bipartite", "railway") | - (layout == "hierarchy" & length(unique(lo[,2])) <= 2)) { - p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), - size = 2, hjust = "outward", - nudge_y = ifelse(lo[,2] == 1, 0.05, -0.05), - # vjust = ifelse(node_mode(object), -1, 1), - angle = 90) + + } else if (layout %in% c("bipartite", "railway") | layout == "hierarchy" & length(unique(lo[["y"]])) <= 2) { + p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), angle = 90, + size = 3, hjust = "outward", repel = TRUE, + nudge_y = ifelse(lo[,2] == 1, 0.05, -0.05)) + ggplot2::coord_cartesian(ylim=c(-0.2, 1.2)) - } else if (layout == "hierarchy" & length(unique(lo[,2])) > 2) { - p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), size = 2, - hjust = "inward", vjust = -0.4) - } else if (!is_twomode(g)) { # Plot one mode - p <- p + ggraph::geom_node_label(ggplot2::aes(label = name), - label.padding = 0.15, label.size = 0, - repel = TRUE, seed = 1234) - } else { # Plot two modes + } else if (layout == "hierarchy" & length(unique(lo[["y"]])) > 2) { p <- p + ggraph::geom_node_text(ggplot2::aes(label = name), - repel = TRUE, size = 2, - hjust = "outward", - nudge_x = ifelse(lo[,1] == 1, 0.05, -0.05), - seed = 1234) + size = 3, hjust = "inward", repel = TRUE) + } else if (layout %in% c("alluvial", "lineage")) { + p <- p + ggraph::geom_node_label(ggplot2::aes(label = name), size = 3, + repel = TRUE, label.size = 0, + nudge_x = ifelse(lo[,1] == 1, 0.02, -0.02)) + } else { + p <- p + ggraph::geom_node_label(ggplot2::aes(label = name), label.size = 0, + repel = TRUE, seed = 1234, size = 3) } } if (!is.null(node_group)) { @@ -465,10 +458,9 @@ reduce_categories <- function(g, node_group) { if (!is.null(edge_color)) { if (edge_color %in% names(tie_attribute(g))) { p <- p + ggraph::geom_edge_arc(ggplot2::aes( - width = weight, colour = as.factor(tie_attribute(g, edge_color))), + width = esize, colour = as.factor(tie_attribute(g, edge_color))), edge_alpha = 0.4, strength = bend, edge_linetype = "solid", - edge_width = esize, arrow = ggplot2::arrow(angle = 15, length = ggplot2::unit(2, 'mm'), type = "closed"), @@ -477,11 +469,10 @@ reduce_categories <- function(g, node_group) { ggraph::scale_edge_colour_manual(values = colorsafe_palette, guide = ggplot2::guide_legend("")) } else { - p <- p + ggraph::geom_edge_arc(ggplot2::aes(width = weight), + p <- p + ggraph::geom_edge_arc(ggplot2::aes(width = esize), colour = edge_color, edge_alpha = 0.4, strength = bend, edge_linetype = "solid", - edge_width = esize, arrow = ggplot2::arrow(angle = 15, length = ggplot2::unit(2, 'mm'), type = "closed"), @@ -490,22 +481,20 @@ reduce_categories <- function(g, node_group) { } } else if (is_signed(g)) { p <- p + ggraph::geom_edge_arc( - ggplot2::aes(width = weight, + ggplot2::aes(width = esize, colour = ifelse(igraph::E(g)$sign >= 0, "#d73027", "#4575b4"), linetype = ifelse(igraph::E(g)$sign >= 0, "solid", "dashed")), edge_alpha = 0.4, strength = bend, - edge_width = esize, arrow = ggplot2::arrow(angle = 15, length = ggplot2::unit(2, 'mm'), type = "closed"), end_cap = ggraph::circle(1.5, 'mm')) + ggraph::scale_edge_width_continuous(range = c(0.2, 2.5), guide = "none") } else { - p <- p + ggraph::geom_edge_arc(ggplot2::aes(width = weight), + p <- p + ggraph::geom_edge_arc(ggplot2::aes(width = esize), edge_colour = "black", edge_alpha = 0.4, strength = bend, edge_linetype = "solid", - edge_width = esize, arrow = ggplot2::arrow(angle = 15, length = ggplot2::unit(2, 'mm'), type = "closed"), @@ -559,16 +548,15 @@ reduce_categories <- function(g, node_group) { if (!is.null(edge_color)) { if (edge_color %in% names(tie_attribute(g))) { p <- p + ggraph::geom_edge_link0(ggplot2::aes( - width = weight, colour = as.factor(tie_attribute(g, edge_color))), - edge_alpha = 0.4, edge_linetype = "solid", - edge_width = esize) + + width = esize, colour = as.factor(tie_attribute(g, edge_color))), + edge_alpha = 0.4, edge_linetype = "solid") + ggraph::scale_edge_width_continuous(range = c(0.2, 1), guide = "none") + ggraph::scale_edge_colour_manual(values = colorsafe_palette, guide = ggplot2::guide_legend("")) } else { p <- p + ggraph::geom_edge_link0(ggplot2::aes(width = weight), colour = edge_color, - edge_alpha = 0.4, edge_width = esize, + edge_alpha = 0.4, edge_linetype = "solid") + ggraph::scale_edge_width_continuous(range = c(0.2, 1), guide = "none") } @@ -577,13 +565,13 @@ reduce_categories <- function(g, node_group) { ggplot2::aes(width = weight, colour = ifelse(igraph::E(g)$sign >= 0, "#d73027", "#4575b4"), linetype = ifelse(igraph::E(g)$sign >= 0, "solid", "dashed")), - edge_alpha = 0.4, edge_width = esize) + + edge_alpha = 0.4) + ggraph::scale_edge_width_continuous(range = c(0.2, 1), guide = "none") } else { p <- p + ggraph::geom_edge_link0(ggplot2::aes(width = weight), edge_colour = "black", edge_linetype = "solid", - edge_alpha = 0.4, edge_width = esize) + + edge_alpha = 0.4) + ggraph::scale_edge_width_continuous(range = c(0.2, 1), guide = "none") } } else { # unweighted and undirected @@ -771,6 +759,8 @@ reduce_categories <- function(g, node_group) { out <- edge_size } if (length(out > 1) & all(out <= 1 & out >= 0)) out <- out*10 + } else if (is.null(edge_size) & is_weighted(g)) { + out <- tie_attribute(g, "weight") } else { out <- 0.5 } @@ -795,16 +785,6 @@ reduce_categories <- function(g, node_group) { out } -.is_diamond <- function(x) { - x <- as_matrix(x) - if (is.numeric(x)) { - if (length(x) == 100 | length(x) == 10000 & - suppressWarnings(all(unique(rowSums(x)) == c(3, 5, 8)))) { - TRUE - } else FALSE - } else FALSE -} - .node_adoption_time <- function(g){ diff_model <- attr(g, "diff_model") event <- nodes <- NULL @@ -891,7 +871,7 @@ hypot <- function (x, y) { } time_edges_lst <- function(tlist, edges_lst, nodes_lst, edge_color) { - edg <- lapply(1:length(tlist), function(i) { + lapply(1:length(tlist), function(i) { edges_lst[[i]]$x <- nodes_lst[[i]]$x[match(edges_lst[[i]]$from, nodes_lst[[i]]$name)] edges_lst[[i]]$y <- nodes_lst[[i]]$y[match(edges_lst[[i]]$from, @@ -904,10 +884,6 @@ time_edges_lst <- function(tlist, edges_lst, nodes_lst, edge_color) { edges_lst[[i]]$status <- TRUE edges_lst[[i]] }) - # Keep only necessary columns - edg <- lapply(edg, function (x) x[,c("from", "to", "frame", "x", "y", "xend", - "yend", "id", "status"#, edge_color - )]) } transition_edge_lst <- function(tlist, edges_lst, nodes_lst, all_edges) { @@ -961,20 +937,15 @@ map_dynamic <- function(edges_out, nodes_out, edge_color, node_shape, # Plot edges if (!is.null(edge_color)) { # Remove NAs in edge color, if declared - edge_color <- ifelse(is.na(edges_out[[edge_color]]), "black", edges_out[[edge_color]]) - color <- grDevices::colors() - color <- color[!color %in% "black"] - if(!any(grepl(paste(color, collapse = "|"), edge_color)) | - any(grepl("#", edge_color))) { - for(i in unique(edge_color)) { - if (i != "black") { - edge_color[edge_color == i] <- sample(color, 1) - } - } + if (edge_color %in% names(edges_out)) { + edge_color <- .check_color(edges_out[[edge_color]]) } - } else edge_color <- rep("black", nrow(edges_out)) + } else edge_color <- "black" if (!is.null(edge_size)) { + if (edge_size %in% names(edges_out)) { edge_size <- as.numeric(edges_out[[edge_size]]) + edge_size <- ifelse(is.na(edge_size), 0.5, edge_size) + } } else edge_size <- 0.5 p <- ggplot2::ggplot() + ggplot2::geom_segment(aes(x = x, xend = xend, y = y, yend = yend, group = id), @@ -982,22 +953,16 @@ map_dynamic <- function(edges_out, nodes_out, edge_color, node_shape, linewidth = edge_size, show.legend = FALSE) # Set node shape, color, and size if (!is.null(node_shape)) { - node_shape <- as.factor(nodes_out[[node_shape]]) - node_shape <- c("circle", "square", "triangle")[node_shape] - } else node_shape <- rep("circle", nrow(nodes_out)) + if (node_shape %in% names(nodes_out)) { + node_shape <- as.factor(nodes_out[[node_shape]]) + if (!any(grepl("circle|square|triangle", node_shape))) { + node_shape <- c("circle", "square", "triangle")[node_shape] + } + } + } else node_shape <- "circle" if (!is.null(node_color)) { if (node_color %in% names(nodes_out)) { - node_color <- nodes_out[[node_color]] - } - color <- grDevices::colors() - color <- color[!color %in% "black"] - if (!any(grepl(paste(color, collapse = "|"), node_color)) | - any(grepl("#", node_color))) { - for(i in unique(node_color)) { - if (i != "black") { - node_color[node_color == i] <- sample(color, 1) - } - } + node_color <- .check_color(nodes_out[[node_color]]) } } else if (is.null(node_color) & "Infected" %in% names(nodes_out)) { node_color <- as.factor(ifelse(nodes_out[["Exposed"]], "Exposed", @@ -1014,8 +979,7 @@ map_dynamic <- function(edges_out, nodes_out, edge_color, node_shape, } else node_size <- nrow(nodes_out)/length(unique(nodes_out$frame)) # Add labels if (isTRUE(labels)) { - p <- p + ggplot2::geom_text(aes(x, y, label = name), - alpha = alphad, + p <- p + ggplot2::geom_text(aes(x, y, label = name), alpha = alphad, data = nodes_out, color = "black", hjust = -0.2, vjust = -0.2, show.legend = FALSE) } @@ -1039,6 +1003,20 @@ map_dynamic <- function(edges_out, nodes_out, edge_color, node_shape, p } +.check_color <- function(v) { + color <- grDevices::colors() + color <- color[!color %in% "black"] + v <- ifelse(is.na(v), "black", v) + if (!any(grepl(paste(color, collapse = "|"), v)) | any(grepl("#", v))) { + for(i in unique(v)) { + if (i != "black") { + v[v == i] <- sample(color, 1) + } + } + } + v +} + collapse_guides <- function(plist) { glist <- list() for (i in seq_len(length(plist))) { diff --git a/R/map_layout_configurations.R b/R/map_layout_configurations.R index 68f96f01..005ae24a 100644 --- a/R/map_layout_configurations.R +++ b/R/map_layout_configurations.R @@ -1,10 +1,27 @@ #' Layout algorithms based on configurational positions #' +#' @description +#' Configurational layouts locate nodes at symmetric coordinates +#' to help illustrate the particular layouts. +#' Currently "triad" and "quad" layouts are available. +#' The "configuration" layout will choose the appropriate configurational +#' layout automatically. +#' #' @name configuration_layouts #' @family mapping #' @inheritParams partition_layouts NULL +#' @rdname configuration_layouts +#' @export +layout_tbl_graph_configuration <- function(.data, + circular = FALSE, times = 1000){ + if (network_nodes(.data) == 3) { + layout_tbl_graph_triad(.data, circular = circular, times = times) + } else if (network_nodes(.data) == 4) { + layout_tbl_graph_quad(.data, circular = circular, times = times) +}} + #' @rdname configuration_layouts #' @export layout_tbl_graph_triad <- function(.data, diff --git a/R/map_layout_partition.R b/R/map_layout_partition.R index ca25c2b1..aae1172e 100644 --- a/R/map_layout_partition.R +++ b/R/map_layout_partition.R @@ -61,7 +61,7 @@ layout_tbl_graph_hierarchy <- function(.data, center = NULL, if (is.null(center)) { thisRequiresBio("Rgraphviz") prep <- as_matrix(.data, twomode = FALSE) - if(anyDuplicated(rownames(prep))){ + if(anyDuplicated(rownames(prep))) { rownames(prep) <- seq_len(nrow(prep)) colnames(prep) <- seq_len(ncol(prep)) } @@ -84,7 +84,6 @@ layout_tbl_graph_hierarchy <- function(.data, center = NULL, Evt2 <- cbind(rep(2, floor(ncol(net)/2)), nrm(rng(floor(mm/2)))) crd <- rbind(Act, Evt1, Evt2) crd[which(is.nan(crd))] <- 0.5 - crd[, 2] <- crd[, 2] * cos(pi) - crd[, 1] * sin(pi) rownames(crd) <- c(dimnames(net)[[1]], dimnames(net)[[2]]) } else if (center == "events") { Act1 <- cbind(rep(0, ceiling(nrow(net)/2)), nrm(rng(ceiling(nn/2)))) @@ -92,7 +91,6 @@ layout_tbl_graph_hierarchy <- function(.data, center = NULL, Evt <- cbind(rep(1, ncol(net)), nrm(rng(mm))) crd <- rbind(Act1, Act2, Evt) crd[which(is.nan(crd))] <- 0.5 - crd[, 2] <- crd[, 2] * cos(pi) - crd[, 1] * sin(pi) rownames(crd) <- c(dimnames(net)[[1]], dimnames(net)[[2]]) } else { if (center %in% node_names(.data)) { @@ -107,7 +105,6 @@ layout_tbl_graph_hierarchy <- function(.data, center = NULL, } crd <- rbind(side1, side2) crd[which(is.nan(crd))] <- 0.5 - crd[, 2] <- crd[, 2] * cos(pi) - crd[, 1] * sin(pi) rownames(crd) <- c(dimnames(net)[[1]], dimnames(net)[[2]]) } else stop("Please declare actors, events, or a node name as center.") } @@ -139,7 +136,7 @@ layout_tbl_graph_alluvial <- function(.data, #' @rdname partition_layouts #' @export layout_tbl_graph_railway <- function(.data, - circular = FALSE, times = 1000){ + circular = FALSE, times = 1000) { res <- layout_tbl_graph_hierarchy(as_igraph(.data)) res$x <- c(match(res[res[,2]==0,1], sort(res[res[,2]==0,1])), match(res[res[,2]==1,1], sort(res[res[,2]==1,1]))) @@ -239,52 +236,21 @@ layout_tbl_graph_lineage <- function(.data, rank, circular = FALSE) { if (length(rank) > 1 & length(rank) != length(.data)) { stop("Please pass the function a `rank` node attribute or a vector.") } else if (length(rank) != length(.data)) { - rank <- node_attribute(.data, rank) - if (!is.numeric(rank)) - stop("Please declare a numeric attribute to `rank` nodes.") + rank <- as.numeric(node_attribute(.data, rank)) } thisRequiresBio("Rgraphviz") - prep <- as_matrix(.data, twomode = FALSE) - if(anyDuplicated(rownames(prep))){ - rownames(prep) <- seq_len(nrow(prep)) - colnames(prep) <- seq_len(ncol(prep)) - } - if(any(prep<0)) prep[prep<0] <- 0 - out <- as_graphAM(prep) - out <- suppressMessages(Rgraphviz::layoutGraph(out, layoutType = 'dot', - attrs = list(graph = list(rankdir = "BT")))) - nodeX <- .rescale(out@renderInfo@nodes$nodeX) - names <- names(nodeX) - nodeY <- .rescale(rank*(-1)) - .to_lo(.adjust(nodeX, nodeY, names)) + out <- layout_tbl_graph_alluvial( + as_igraph(mutate(.data, type = ifelse( + rank > mean(rank), TRUE, FALSE)), twomode = TRUE)) + out$x <- .rescale(rank) + .check_dup(out) } .rescale <- function(vector){ (vector - min(vector)) / (max(vector) - min(vector)) } -.adjust <- function(x, y, names) { - out <- data.frame(cbind(x, y, names)) - adj <- data.frame() - for (k in levels(as.factor(y))) { - a <- subset(out, y == k) - if (length(a[,1]) == 1) { - a[,1] <- ifelse(a[,1] > 0.8, as.numeric(a[,1])*0.8, - ifelse(a[,1] < 0.2, as.numeric(a[,1])*1.2, - as.numeric(a[,1]))) - } else if (length(a[,1]) > 2) { - a[,1] <- seq(min(a[,1]), max(a[,1]), len = length(a[,1])) - } - adj <- rbind(adj, a) - } - name <- data.frame(names = out[,3]) - out <- dplyr::left_join(name, adj, by = "names") - out <- apply(out[,2:3], 2, as.numeric) - rownames(out) <- name$names - out -} - -.to_lo <- function(mat){ +.to_lo <- function(mat) { res <- as.data.frame(mat) names(res) <- c("x","y") res @@ -299,6 +265,11 @@ to_list <- function(members){ out } +.check_dup <- function(mat) { + mat$y <- ifelse(duplicated(mat[c('x','y')]), mat$y*0.95, mat$y) + mat +} + #' @importFrom igraph degree getNNvec <- function(.data, members){ lapply(members, function(circle){ diff --git a/R/map_theme.R b/R/map_theme.R index 1dd5389b..d5b9043e 100644 --- a/R/map_theme.R +++ b/R/map_theme.R @@ -7,9 +7,12 @@ #' @param base_size Font size, by default 12. #' @param base_family Font family, by default "sans". #' @examples -#' # autographr(to_mentoring(ison_brandes)) + -#' # labs(title = "Who leads and who follows?") + -#' # theme_iheid() +#' to_mentoring(ison_brandes) %>% +#' mutate(color = c(rep(c(1,2,3), 3), 3)) %>% +#' autographr(node_color = "color") + +#' labs(title = "Who leads and who follows?") + +#' scale_color_iheid() + +#' theme_iheid() NULL #' @rdname themes @@ -185,16 +188,16 @@ NULL #' @rdname scales #' @export scale_fill_iheid <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "IHEID", - palette_gen(palette = "IHEID", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "IHEID", direction), na.value = "black", name = "", ...) } #' @rdname scales #' @export scale_colour_iheid <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "IHEID", - palette_gen(palette = "IHEID", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "IHEID", direction), na.value = "black", name = "", ...) } @@ -205,8 +208,8 @@ scale_color_iheid <- scale_colour_iheid #' @rdname scales #' @export scale_edge_colour_iheid <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "IHEID", - palette_gen(palette = "IHEID", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "IHEID", direction), na.value = "black", name = "", ...) } @@ -219,16 +222,16 @@ scale_edge_color_iheid <- scale_edge_colour_iheid #' @rdname scales #' @export scale_fill_centres <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "Centres", - palette_gen(palette = "Centres", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "Centres", direction), na.value = "black", name = "", ...) } #' @rdname scales #' @export scale_colour_centres <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "Centres", - palette_gen(palette = "Centres", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "Centres", direction), na.value = "black", name = "", ...) } @@ -239,8 +242,8 @@ scale_color_centres <- scale_colour_centres #' @rdname scales #' @export scale_edge_colour_centres <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "Centres", - palette_gen(palette = "Centres", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "Centres", direction), na.value = "black", name = "", ...) } @@ -253,16 +256,16 @@ scale_edge_color_centres <- scale_edge_colour_centres #' @rdname scales #' @export scale_fill_sdgs <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "SDGs", - palette_gen(palette = "SDGs", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "SDGs", direction), na.value = "black", name = "", ...) } #' @rdname scales #' @export scale_colour_sdgs <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "SDGs", - palette_gen(palette = "SDGs", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "SDGs", direction), na.value = "black", name = "", ...) } @@ -273,8 +276,8 @@ scale_color_sdgs <- scale_colour_sdgs #' @rdname scales #' @export scale_edge_colour_sdgs <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "SDGs", - palette_gen(palette = "SDGs", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "SDGs", direction), na.value = "black", name = "", ...) } @@ -287,16 +290,16 @@ scale_edge_color_sdgs <- scale_edge_colour_sdgs #' @rdname scales #' @export scale_fill_ethz <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "ETHZ", - palette_gen(palette = "ETHZ", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "ETHZ", direction), na.value = "black", name = "", ...) } #' @rdname scales #' @export scale_colour_ethz <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "ETHZ", - palette_gen(palette = "ETHZ", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "ETHZ", direction), na.value = "black", name = "", ...) } @@ -307,8 +310,8 @@ scale_color_ethz <- scale_colour_ethz #' @rdname scales #' @export scale_edge_colour_ethz <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "ETHZ", - palette_gen(palette = "ETHZ", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "ETHZ", direction), na.value = "black", name = "", ...) } @@ -321,16 +324,16 @@ scale_edge_color_ethz <- scale_edge_colour_ethz #' @rdname scales #' @export scale_fill_uzh <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "UZH", - palette_gen(palette = "UZH", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "UZH", direction), na.value = "black", name = "", ...) } #' @rdname scales #' @export scale_colour_uzh <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "UZH", - palette_gen(palette = "UZH", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "UZH", direction), na.value = "black", name = "", ...) } @@ -341,8 +344,8 @@ scale_color_uzh <- scale_colour_uzh #' @rdname scales #' @export scale_edge_colour_uzh <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "UZH", - palette_gen(palette = "UZH", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "UZH", direction), na.value = "black", name = "", ...) } @@ -355,16 +358,16 @@ scale_edge_color_uzh <- scale_edge_colour_uzh #' @rdname scales #' @export scale_fill_rug <- function(direction = 1, ...) { - ggplot2::discrete_scale("fill", "RUG", - palette_gen(palette = "RUG", direction), + ggplot2::discrete_scale("fill", + palette = palette_gen(palette = "RUG", direction), na.value = "grey", name = "", ...) } #' @rdname scales #' @export scale_colour_rug <- function(direction = 1, ...) { - ggplot2::discrete_scale("colour", "RUG", - palette_gen(palette = "RUG", direction), + ggplot2::discrete_scale("colour", + palette = palette_gen(palette = "RUG", direction), na.value = "grey", name = "", ...) } @@ -375,8 +378,8 @@ scale_color_rug <- scale_colour_rug #' @rdname scales #' @export scale_edge_colour_rug <- function(direction = 1, ...) { - ggplot2::discrete_scale("edge_colour", "RUG", - palette_gen(palette = "RUG", direction), + ggplot2::discrete_scale("edge_colour", + palette = palette_gen(palette = "RUG", direction), na.value = "black", name = "", ...) } @@ -386,7 +389,7 @@ scale_edge_color_rug <- scale_edge_colour_rug # Helper functions corp_color <- function(...) { - corp_colors <- c(`IHEIDRed` = "#E20020", `IHEIDBlack` = "#5c666f", + corp_colors <- c(`IHEIDRed` = "#E20020", `IHEIDBlack` = "#000010", `IHEIDGrey` = "#6f7072", `AHCD` = "#622550", `CFFD` = "#0094D8", `CIES` = "#268D2B", `CTEI` = "#008F92", `CGEN` = "#820C2B", diff --git a/cran-comments.md b/cran-comments.md index 68817bc3..ae763253 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -8,5 +8,3 @@ ## R CMD check results 0 errors | 0 warnings | 0 notes - -- This version addresses expected conflicts with the 'igraph' v2.0.0 and 'ggplot 3.5.0' releases, on which this package depends diff --git a/man/autographing.Rd b/man/autographing.Rd index 9fff415f..81d24613 100644 --- a/man/autographing.Rd +++ b/man/autographing.Rd @@ -99,7 +99,7 @@ It is easiest if this is added as an edge or tie attribute to the graph before plotting. Edges can also be colored by declaring a color instead.} -\item{edge_size}{Edge variable to be used for sizing the edges. +\item{edge_size}{Tie variable to be used for sizing the edges. This can be any continuous variable on the nodes of the network. Since this function expects this to be an existing variable, it is recommended to calculate all edge-related statistics prior @@ -149,30 +149,30 @@ with sensible defaults }} \examples{ -#autographr(ison_adolescents) -#ison_adolescents \%>\% -# mutate(year = rep(c(1985, 1990, 1995, 2000), times = 2)) \%>\% -#autographr(layout = "lineage", rank = "year") -#autographr(ison_algebra, layout = "circle", -# node_size = 8, node_color = "orange", node_shape = "square", -# edge_color = "blue", edge_size = 2) +autographr(ison_adolescents) +autographr(ison_algebra, layout = "circle", + node_size = 8, node_color = "orange", node_shape = "square", + edge_color = "blue", edge_size = 2) +autographr(ison_southern_women, layout = "concentric", + node_color = "type", membership = "type") +autographr(play_diffusion(ison_karateka)) #autographr(ison_algebra, edge_color = "type", -# node_size = migraph::node_betweenness(ison_algebra)*100) +# node_size = migraph::node_betweenness(ison_algebra)*100) +#ison_adolescents \%>\% +# mutate(year = rep(c(1985, 1990, 1995, 2000), times = 2)) \%>\% +# autographr(layout = "lineage", rank = "year") #ison_adolescents \%>\% # mutate(cut = node_is_cutpoint(ison_adolescents)) \%>\% #autographr(node_color = "cut", node_shape = "cut") #autographr(ison_lotr, node_color = Race, -# node_size = migraph::node_degree(ison_lotr)*2, -# edge_color = "darkgreen", -# edge_size = migraph::tie_degree(ison_lotr)) +# node_size = migraph::node_degree(ison_lotr)*2, +# edge_color = "darkgreen", +# edge_size = migraph::tie_degree(ison_lotr)) #autographr(ison_karateka, node_group = allegiance, -# edge_size = migraph::tie_closeness(ison_karateka)) -#autographr(ison_southern_women, layout = "concentric", -# node_color = "type", membership = "type") +# edge_size = migraph::tie_closeness(ison_karateka)) #autographr(ison_southern_women, layout = "hierarchy", center = "events") #autographr(ison_lotr, layout = "multilevel", -# node_color = "Race", level = "Race") -#autographr(play_diffusion(ison_karateka)) +# node_color = "Race", level = "Race") #autographs(to_egos(ison_adolescents)) #autographs(to_egos(ison_adolescents), waves = 8) #autographs(to_egos(ison_adolescents), waves = c(2, 4, 6)) @@ -181,14 +181,18 @@ with sensible defaults # mutate_ties(year = sample(1995:1998, 10, replace = TRUE)) \%>\% # to_waves(attribute = "year", cumulative = TRUE) \%>\% # autographd() -#ison_adolescents \%>\% -# mutate(shape = rep(c("circle", "square"), times = 4), -# color = rep(c("blue", "red"), times = 4), -# size = sample(4:16, 8, replace = TRUE)) \%>\% -# mutate_ties(year = sample(1995:1998, 10, replace = TRUE)) \%>\% +#ison_adolescents \%>\% +# mutate(gender = rep(c("male", "female"), times = 4), +# hair = rep(c("black", "brown"), times = 4), +# age = sample(11:16, 8, replace = TRUE)) \%>\% +# mutate_ties(year = sample(1995:1998, 10, replace = TRUE), +# links = sample(c("friends", "not_friends"), 10, replace = TRUE), +# weekly_meetings = sample(c(3, 5, 7), 10, replace = TRUE)) \%>\% # to_waves(attribute = "year") \%>\% -# autographd(layout = "circle", node_shape = "shape", -# node_color = "color", node_size = "size") +# autographd(layout = "concentric", membership = "gender", +# node_shape = "gender", node_color = "hair", +# node_size = "age", edge_color = "links", +# edge_size = "weekly_meetings") #autographd(play_diffusion(ison_adolescents, seeds = 5, recovery = 0.1)) } \seealso{ diff --git a/man/configuration_layouts.Rd b/man/configuration_layouts.Rd index 729e991d..e4353c07 100644 --- a/man/configuration_layouts.Rd +++ b/man/configuration_layouts.Rd @@ -2,10 +2,13 @@ % Please edit documentation in R/map_layout_configurations.R \name{configuration_layouts} \alias{configuration_layouts} +\alias{layout_tbl_graph_configuration} \alias{layout_tbl_graph_triad} \alias{layout_tbl_graph_quad} \title{Layout algorithms based on configurational positions} \usage{ +layout_tbl_graph_configuration(.data, circular = FALSE, times = 1000) + layout_tbl_graph_triad(.data, circular = FALSE, times = 1000) layout_tbl_graph_quad(.data, circular = FALSE, times = 1000) @@ -26,7 +29,11 @@ Only possible for some layouts. Defaults to FALSE.} \item{times}{Maximum number of iterations, where appropriate} } \description{ -Layout algorithms based on configurational positions +Configurational layouts locate nodes at symmetric coordinates +to help illustrate the particular layouts. +Currently "triad" and "quad" layouts are available. +The "configuration" layout will choose the appropriate configurational +layout automatically. } \seealso{ Other mapping: diff --git a/man/features.Rd b/man/features.Rd index 26a7889e..8cab8bd4 100644 --- a/man/features.Rd +++ b/man/features.Rd @@ -89,7 +89,7 @@ Tutte, W. T. (1950). } \seealso{ Other marking: -\code{\link{is_format}}, -\code{\link{is}()} +\code{\link{is}()}, +\code{\link{is_format}} } \concept{marking} diff --git a/man/themes.Rd b/man/themes.Rd index b60abbbd..f9656da0 100644 --- a/man/themes.Rd +++ b/man/themes.Rd @@ -26,7 +26,10 @@ These functions enable graphs to be easily and quickly themed, e.g. changing the default colour of the graph's vertices and edges. } \examples{ -# autographr(to_mentoring(ison_brandes)) + -# labs(title = "Who leads and who follows?") + -# theme_iheid() +to_mentoring(ison_brandes) \%>\% + mutate(color = c(rep(c(1,2,3), 3), 3)) \%>\% + autographr(node_color = "color") + + labs(title = "Who leads and who follows?") + + scale_color_iheid() + + theme_iheid() } diff --git a/tests/testthat/test-manip_reformat.R b/tests/testthat/test-manip_reformat.R index aa012141..1a1828d2 100644 --- a/tests/testthat/test-manip_reformat.R +++ b/tests/testthat/test-manip_reformat.R @@ -70,7 +70,7 @@ test_that("to_acylic works", { test_that("to_reciprocated works",{ expect_true(is_directed(to_reciprocated(ison_brandes))) expect_true(is_directed(to_reciprocated(as_igraph(ison_brandes)))) - expect_true(is_directed(to_reciprocated(to_directed(as_matrix(ison_brandes))))) + expect_true(isSymmetric(to_reciprocated(as_matrix(ison_brandes)))) expect_true(is_directed(to_reciprocated(to_directed(as_network(ison_brandes))))) expect_true(nrow(as_edgelist(to_reciprocated(ison_brandes))) > length(ison_brandes)*2) diff --git a/tests/testthat/test-map_autographr.R b/tests/testthat/test-map_autographr.R index a7489c59..8c0ecdbf 100644 --- a/tests/testthat/test-map_autographr.R +++ b/tests/testthat/test-map_autographr.R @@ -51,8 +51,8 @@ test_that("weighted, unsigned, directed networks graph correctly", { # Weighted, unsigned, directed network test_networkers <- autographr(ison_networkers) # Node position - expect_equal(round(test_networkers[["data"]][["x"]][[1]]), 0) - expect_equal(round(test_networkers[["data"]][["y"]][[1]]), 0) + expect_equal(round(test_networkers[["data"]][["x"]][[1]]), 9) + expect_equal(round(test_networkers[["data"]][["y"]][[1]]), -1) # Edge parameters expect_equal(test_networkers[["layers"]][[2]][["aes_params"]][["edge_alpha"]], 0.4) expect_equal(test_networkers[["layers"]][[2]][["aes_params"]][["edge_linetype"]], "solid") diff --git a/tests/testthat/test-map_theme.R b/tests/testthat/test-map_theme.R index a97862fc..056eb2af 100644 --- a/tests/testthat/test-map_theme.R +++ b/tests/testthat/test-map_theme.R @@ -47,15 +47,12 @@ test_that("scales graph correctly", { autographr(node_color = color) + scale_color_uzh() test_rug <- ison_brandes %>% - mutate(color = c(rep(c(1,2,3), 3), 1, 2)) %>% + mutate(color = c(rep(c(1,2), 4), 1, 2, 1)) %>% autographr(node_color = color) + scale_color_rug() - # The `scale_name` field is deprecated from 3.5.0 onwards - skip_if(utils::packageVersion("ggplot2") >= "3.5.0") - expect_equal(test_sdg[["scales"]][["scales"]][[1]][["call"]][["scale_name"]], "SDGs") - expect_equal(test_sdg[["scales"]][["scales"]][[1]][["call"]][["palette"]][["palette"]], "SDGs") - expect_equal(test_iheid[["scales"]][["scales"]][[1]][["call"]][["scale_name"]], "IHEID") - expect_equal(test_ethz[["scales"]][["scales"]][[1]][["call"]][["scale_name"]], "ETHZ") - expect_equal(test_uzh[["scales"]][["scales"]][[1]][["call"]][["scale_name"]], "UZH") - expect_equal(test_rug[["scales"]][["scales"]][[1]][["call"]][["scale_name"]], "RUG") + expect_equal(as.character(test_sdg[["scales"]][["scales"]][[1]][["call"]]), "scale_color_sdgs") + expect_equal(as.character(test_iheid[["scales"]][["scales"]][[1]][["call"]]), "scale_color_iheid") + expect_equal(as.character(test_ethz[["scales"]][["scales"]][[1]][["call"]]), "scale_color_ethz") + expect_equal(as.character(test_uzh[["scales"]][["scales"]][[1]][["call"]]), "scale_color_uzh") + expect_equal(as.character(test_rug[["scales"]][["scales"]][[1]][["call"]]), "scale_color_rug") })