diff --git a/NEWS.md b/NEWS.md index 09f29aedc..84d07d4cf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,22 @@ # codeface-extraction-r - Changelog +## Unversioned + +### Added +- ... + +### Changed/Improved +- Vertex and edge types (attribute `"type"`) are now a character string (e.g., "Author" or "Unipartite") (#110, 3ca6ed99cf377200adb94a4b27ed1ea7d3a6981a) +- Default plotting layout is now `igraph::layout.kamada.kawai` (#109, 909965453c47c26c902612cb0c9aa16a5b56746a) +- Remove parameter 'color.attr' from 'motifs.search.in.network' (d33f6863aaf05ae1a8acf7f5667784713796b734) +- Fix and clean-up of both the plotting and the motif modules (3ca6ed99cf377200adb94a4b27ed1ea7d3a6981a, consequence of #110) + +### Fixed +- Probably fixed segfaults during plotting by changing the default layout (see above and issue #109) +- Fix gray-scale plotting of networks (730cc544edbb30ea3aa89a91e123e74b18a942c6) + + ## 3.1.1 ### Changed/Improved diff --git a/showcase.R b/showcase.R index 4f32a1b48..c3f626098 100644 --- a/showcase.R +++ b/showcase.R @@ -11,7 +11,7 @@ ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## -## Copyright 2016-2017 by Claus Hunsen +## Copyright 2016-2018 by Claus Hunsen ## Copyright 2017 by Raphael Nömmer ## Copyright 2017 by Christian Hechtl ## Copyright 2017 by Felix Prasse @@ -302,8 +302,8 @@ y = NetworkBuilder$new(project.data = y.data, network.conf = net.conf) # plot.print.network(g, labels = TRUE, grayscale = FALSE) # ## set a layout and print directly -# lay = matrix(c( 20, 179, 552, 693, 956, 1091, 124, 317, 516, 615, 803, 1038, -# 245, 175, 185, 255, 253, 225, 73, 8, 75, 0, 96, 86), +# lay = matrix(c( 20, 179, 693, 552, 956, 1091, 124, 317, 516, 615, 803, 1038, +# 245, 175, 255, 185, 253, 225, 73, 8, 75, 0, 96, 86), # nrow = 12, byrow = FALSE) # for sample graph # g = igraph::set.graph.attribute(g, "layout", lay) # plot.print.network(g, labels = TRUE, grayscale = FALSE) diff --git a/tests/test-motifs.R b/tests/test-motifs.R index d857b731b..06e7fa308 100644 --- a/tests/test-motifs.R +++ b/tests/test-motifs.R @@ -32,14 +32,14 @@ test_that("Motifs are found in sample network (remove.duplicates = TRUE).", { igraph::V(network)[ c("D3", "D4") ], igraph::V(network)[ c("D4", "D5") ] ) - matchings = motifs.search.in.network(network, MOTIFS.LINE, color.attr = "type", remove.duplicates = TRUE) + matchings = motifs.search.in.network(network, MOTIFS.LINE, remove.duplicates = TRUE) expect_equal(matchings, expected, info = "Line motif") ## Triangle motif (positive) expected = list( igraph::V(network)[ c("D1", "D2", "A1") ] ) - matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.POSITIVE, color.attr = "type", remove.duplicates = TRUE) + matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.POSITIVE, remove.duplicates = TRUE) expect_equal(matchings, expected, info = "Triangle motif (positive)") ## Triangle motif (negative) @@ -47,14 +47,14 @@ test_that("Motifs are found in sample network (remove.duplicates = TRUE).", { igraph::V(network)[ c("D1", "D2", "A1") ], igraph::V(network)[ c("D5", "D6", "A6") ] ) - matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.NEGATIVE, color.attr = "type", remove.duplicates = TRUE) + matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.NEGATIVE, remove.duplicates = TRUE) expect_equal(matchings, expected, info = "Triangle motif (negative)") ## Square motif (positive) expected = list( igraph::V(network)[ c("D4", "D5", "A5", "A6") ] ) - matchings = motifs.search.in.network(network, MOTIFS.SQUARE.POSITIVE, color.attr = "type", remove.duplicates = TRUE) + matchings = motifs.search.in.network(network, MOTIFS.SQUARE.POSITIVE, remove.duplicates = TRUE) expect_equal(matchings, expected, info = "Square motif (positive)") ## Square motif (negative) @@ -64,7 +64,7 @@ test_that("Motifs are found in sample network (remove.duplicates = TRUE).", { igraph::V(network)[ c("D4", "D5", "A5", "A6") ], igraph::V(network)[ c("D4", "D6", "A5", "A6") ] ) - matchings = motifs.search.in.network(network, MOTIFS.SQUARE.NEGATIVE, color.attr = "type", remove.duplicates = TRUE) + matchings = motifs.search.in.network(network, MOTIFS.SQUARE.NEGATIVE, remove.duplicates = TRUE) expect_equal(matchings, expected, info = "Square motif (negative)") }) @@ -86,7 +86,7 @@ test_that("Motifs are found in sample network (remove.duplicates = FALSE).", { igraph::V(network)[ c("D3", "D4") ], # unordered: D4, D3 igraph::V(network)[ c("D4", "D5") ] # unordered: D5, D4 ) - matchings = motifs.search.in.network(network, MOTIFS.LINE, color.attr = "type", remove.duplicates = FALSE) + matchings = motifs.search.in.network(network, MOTIFS.LINE, remove.duplicates = FALSE) expect_equal(matchings, expected, info = "Line motif") ## Triangle motif (positive) @@ -94,7 +94,7 @@ test_that("Motifs are found in sample network (remove.duplicates = FALSE).", { igraph::V(network)[ c("D1", "D2", "A1") ], igraph::V(network)[ c("D1", "D2", "A1") ] # unordered: D2, D1, A1 ) - matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.POSITIVE, color.attr = "type", remove.duplicates = FALSE) + matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.POSITIVE, remove.duplicates = FALSE) expect_equal(matchings, expected, info = "Triangle motif (positive)") ## Triangle motif (negative) @@ -104,7 +104,7 @@ test_that("Motifs are found in sample network (remove.duplicates = FALSE).", { igraph::V(network)[ c("D5", "D6", "A6") ], igraph::V(network)[ c("D5", "D6", "A6") ] # unordered: D6, D5, A1 ) - matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.NEGATIVE, color.attr = "type", remove.duplicates = FALSE) + matchings = motifs.search.in.network(network, MOTIFS.TRIANGLE.NEGATIVE, remove.duplicates = FALSE) expect_equal(matchings, expected, info = "Triangle motif (negative)") ## Square motif (positive) @@ -112,7 +112,7 @@ test_that("Motifs are found in sample network (remove.duplicates = FALSE).", { igraph::V(network)[ c("D4", "D5", "A5", "A6") ], igraph::V(network)[ c("D4", "D5", "A5", "A6") ] # unordered: D5, D4, A5, A6 ) - matchings = motifs.search.in.network(network, MOTIFS.SQUARE.POSITIVE, color.attr = "type", remove.duplicates = FALSE) + matchings = motifs.search.in.network(network, MOTIFS.SQUARE.POSITIVE, remove.duplicates = FALSE) expect_equal(matchings, expected, info = "Square motif (positive)") ## Square motif (negative) @@ -126,7 +126,7 @@ test_that("Motifs are found in sample network (remove.duplicates = FALSE).", { igraph::V(network)[ c("D4", "D5", "A5", "A6") ], # unordered: D5, D4, A5, A6 igraph::V(network)[ c("D4", "D6", "A5", "A6") ] # unordered: D6, D4, A5, A6 ) - matchings = motifs.search.in.network(network, MOTIFS.SQUARE.NEGATIVE, color.attr = "type", remove.duplicates = FALSE) + matchings = motifs.search.in.network(network, MOTIFS.SQUARE.NEGATIVE, remove.duplicates = FALSE) expect_equal(matchings, expected, info = "Square motif (negative)") }) diff --git a/util-core-peripheral.R b/util-core-peripheral.R index 5b98f4fa0..3eb71d2a2 100644 --- a/util-core-peripheral.R +++ b/util-core-peripheral.R @@ -996,13 +996,13 @@ get.author.class = function(author.data.frame, calc.base.name, result.limit = NU sapply(author.cumsum, function(x) isTRUE(all.equal(x, author.class.threshold))) )) - ## classify developers according to threshold + ## classify authors according to threshold core.classification = rep(FALSE, nrow(author.data)) core.classification[1:author.class.threshold.idx] = TRUE ## With no activity/collaboration occurring, all authors are classified as peripheral. if (author.class.threshold == 0) { - logging::logwarn("No collaboration/activity occured, thus, all developer's classification is set to peripheral.") + logging::logwarn("No collaboration/activity occured, thus, all authors' classification is set to peripheral.") core.classification = rep(FALSE, length(core.classification)) # ## old code: if we found no core author (should not happen anymore) # } else if (!any(core.classification)) { diff --git a/util-motifs.R b/util-motifs.R index fac660e3f..c462b17c2 100644 --- a/util-motifs.R +++ b/util-motifs.R @@ -66,38 +66,81 @@ MOTIFS.SQUARE.NEGATIVE = igraph::make_empty_graph(directed = FALSE) + type = c(TYPE.EDGES.INTER, TYPE.EDGES.INTER, TYPE.EDGES.INTRA)) +## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / +## Mapping of vertex and edge types to numerics ---------------------------- + +## Map vertex and edge types to numerics +MOTIF.TYPE.MAPPING = as.data.frame(rbind( + c(character = TYPE.AUTHOR, numeric = 1), + c(character = TYPE.ARTIFACT, numeric = 2), + c(character = TYPE.EDGES.INTRA, numeric = 3), + c(character = TYPE.EDGES.INTER, numeric = 4) +)) + + +#' Get numeric vertex types from the given \code{network}. +#' +#' For this, the actual vertex types are mapped to numerics using the pre-defined +#' mapping \code{MOTIF.TYPE.MAPPING}. +#' +#' @param network the network from which to get the vertex types +#' @param index the subset of vertices to consider [default: igraph::V(network)] +#' +#' @return the vector of numeric vertex types for the (subset of) vertices in the network +#' +#' @seealso \code{MOTIF.TYPE.MAPPING} +get.vertex.types.as.numeric = function(network, index = igraph::V(network)) { + + ## get the vertex attribute as factor + attr.factor = factor(igraph::get.vertex.attribute(network, "type", index)) + + ## replace factor levels with corresponding numerics + levels(attr.factor) = sapply(levels(attr.factor), function(f) { + ## get the numeric from MOTIF.TYPE.MAPPING + num = subset(MOTIF.TYPE.MAPPING, character == f, numeric, drop = TRUE) + return(num) + }) + ## re-create vertex-attribute vector with the numerics + attr.numeric = as.numeric(levels(attr.factor))[attr.factor] + + return(attr.numeric) +} + + ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ## Motif-identification functions ------------------------------------------ #' Search for the given \code{motif} in the given \code{network}. #' +#' The vertex attribute *"type"* is used to match the vertices in the \code{motif} with +#' the ones in the given \code{network}. This means basically that a vertex with +#' a certain value for its attribute "type" in the \code{motif} is only matched with +#' vertices in \code{network} that have the same attribute values. +#' #' @param network The network in which the given \code{motif} is searched #' @param motif The motif to search for in the given \code{network} -#' @param color.attr The the vertex and edge attribute that is used to match the vertices -#' and the edges in the \code{motif} with the ones in the given \code{network}. -#' This means that a vertex A with the attribute declared in \code{color.attr} -#' in the \code{motif} is only matched with vertices with the same attribute in -#' the network. The same holds for edges. #' @param remove.duplicates If \code{remove.duplicates == TRUE}, any duplicate matched motifs are #' removed. This logical value basically resembles the idea of respecting #' the order within a matched motif or not. #' -#' @return A list of vertex sequences denoting the matched motifs, ordered by \code{color.attr} -#' and "name" vertex attributes +#' @return A list of vertex sequences denoting the matched motifs, ordered by "type" and "name" +#' vertex attributes +#' +#' @seealso \code{igraph::subgraph_isomorphisms} (method "vf2") #' #' @examples -#' motifs.search.in.network(get.sample.network(), MOTIFS.TRIANGLE.NEGATIVE, color.attr = "type", remove.duplicates = TRUE) -motifs.search.in.network = function(network, motif, color.attr = "type", remove.duplicates = TRUE) { +#' motifs.search.in.network(get.sample.network(), MOTIFS.TRIANGLE.NEGATIVE, remove.duplicates = TRUE) +motifs.search.in.network = function(network, motif, remove.duplicates = TRUE) { ## find motif in network vs = igraph::subgraph_isomorphisms(target = network, pattern = motif, method = "vf2", - vertex.color1 = igraph::get.vertex.attribute(network, color.attr), - vertex.color2 = igraph::get.vertex.attribute(motif, color.attr)) + vertex.color1 = get.vertex.types.as.numeric(network), + vertex.color2 = get.vertex.types.as.numeric(motif)) ## normalize found vertex sequences (sort vertices) vs.cleaned = lapply(vs, function(seq) { ## get types and names of vertices - types = igraph::get.vertex.attribute(network, color.attr, index = seq) + types = get.vertex.types.as.numeric(network, index = seq) names = igraph::get.vertex.attribute(network, "name", index = seq) ## sort vertex sequence by types and names @@ -160,7 +203,6 @@ motifs.count = function(network, motifs, remove.duplicates = TRUE, raw.data = FA motifs.search.in.network( network = network, motif = motif, - color.attr = "type", remove.duplicates = remove.duplicates ) }) diff --git a/util-networks.R b/util-networks.R index 12f04dcc6..2948a9f4c 100644 --- a/util-networks.R +++ b/util-networks.R @@ -31,13 +31,13 @@ requireNamespace("igraph") # networks ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ## Vertex and edge types --------------------------------------------------- -## node types -TYPE.AUTHOR = 1 -TYPE.ARTIFACT = 2 +## vertex types +TYPE.AUTHOR = "Author" +TYPE.ARTIFACT = "Artifact" -# edge types -TYPE.EDGES.INTRA = 3 -TYPE.EDGES.INTER = 4 +## edge types +TYPE.EDGES.INTRA = "Unipartite" +TYPE.EDGES.INTER = "Bipartite" ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / @@ -790,12 +790,12 @@ construct.network.from.list = function(list, network.conf, directed = FALSE) { ## get vertex data nodes = unique(set[, 1]) - ## break if there is no developer + ## break if there is no author if (length(nodes) < 1) { return(NULL) } - ## if there is only one developer, just create the node, but no edges + ## if there is only one author, just create the node, but no edges if (length(nodes) == 1) { edges = data.frame() attr(edges, "nodes.processed") = nodes # store set of processed nodes @@ -803,7 +803,7 @@ construct.network.from.list = function(list, network.conf, directed = FALSE) { } ## get combinations - combinations = combn(nodes, 2) # all unique pairs of developers + combinations = combn(nodes, 2) # all unique pairs of authors ## construct edge list edges = apply(combinations, 2, function(comb) { @@ -911,7 +911,7 @@ add.edges.for.bipartite.relation = function(net, net1.to.net2, network.conf) { vertex.sequence.for.edges = parallel::mcmapply(function(d, a.df) { a = a.df[["data.vertices"]] new.edges = lapply(a, function(vert) { - igraph::V(net)[d, vert] # get two vertices from source network: c(developer, artifact) + igraph::V(net)[d, vert] # get two vertices from source network: c(author, artifact) }) return(new.edges) }, names(net1.to.net2), net1.to.net2) diff --git a/util-plot.R b/util-plot.R index bd846e85b..c4c16c64a 100644 --- a/util-plot.R +++ b/util-plot.R @@ -11,7 +11,7 @@ ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## -## Copyright 2017 by Claus Hunsen +## Copyright 2017-2018 by Claus Hunsen ## All Rights Reserved. @@ -27,8 +27,13 @@ requireNamespace("ggraph") ## plotting networks ## Global plot options ----------------------------------------------------- ## (e.g., vertex size and colors) +## vertex-type names +PLOT.VERTEX.TYPE.AUTHOR = "Developer" # TYPE.AUTHOR +PLOT.VERTEX.TYPE.ARTIFACT = TYPE.ARTIFACT # "Artifact" + ## vertex size -VERTEX.SIZE = 10 +PLOT.VERTEX.SIZE = 10 +PLOT.VERTEX.SIZE.LEGEND = PLOT.VERTEX.SIZE / 2 ## colors for vertices and edges (colored) PLOT.COLORS.BY.TYPE.VERTEX = c("#00AEFF", "#FF8B00") @@ -38,15 +43,15 @@ names(PLOT.COLORS.BY.TYPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) ## colors for vertices and edges (grayscale) PLOT.COLORS.BY.TYPE.VERTEX.GRAY = c("gray40", "gray30") -names(PLOT.COLORS.BY.TYPE.VERTEX) = c(TYPE.AUTHOR, TYPE.ARTIFACT) +names(PLOT.COLORS.BY.TYPE.VERTEX.GRAY) = c(TYPE.AUTHOR, TYPE.ARTIFACT) PLOT.COLORS.BY.TYPE.EDGE.GRAY = c("gray60", "gray40") -names(PLOT.COLORS.BY.TYPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) +names(PLOT.COLORS.BY.TYPE.EDGE.GRAY) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) -## names for vertex and edge types -PLOT.NAMES.BY.TYPE.VERTEX = c("Developer", "Artifact") -names(PLOT.NAMES.BY.TYPE.VERTEX) = c(TYPE.AUTHOR, TYPE.ARTIFACT) -PLOT.NAMES.BY.TYPE.EDGE = c("unipartite", "bipartite") -names(PLOT.NAMES.BY.TYPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) +## shapes of vertices and edges +PLOT.SHAPE.VERTEX = c(16, 15) # (authors, artifacts) +names(PLOT.SHAPE.VERTEX) = c(TYPE.AUTHOR, TYPE.ARTIFACT) +PLOT.SHAPE.EDGE = c("dashed", "solid") # (unipartite, bipartite) +names(PLOT.SHAPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / @@ -54,6 +59,15 @@ names(PLOT.NAMES.BY.TYPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) #' Construct a ggplot2/ggraph plot object for the given network and print it directly. #' +#' As a layout, by default, \code{igraph::layout.kamada.kawai} (also known as \code{igraph::layout_with_kk}) +#' is used, unless a graph attribute "layout" is set. For a comprehensive list of layouts and more information +#' on layouts in general, see \link{http://igraph.org/r/doc/layout_.html}. +#' To set the graph attribute on your network, run the following code while replacing \code{layout.to.set} +#' to your liking: \code{network = igraph::set.graph.attribute(network, "layout", layout.to.set)}. +#' +#' Note: The names for the vertex types are taken from the variables \code{PLOT.VERTEX.TYPE.AUTHOR} and +#' \code{PLOT.VERTEX.TYPE.ARTIFACT}. The defaults are \code{"Developer"} and \code{TYPE.ARTIFACT}, respectively. +#' #' @param network the network to plot and print #' @param labels logical indicating whether vertex lables should be plotted [default: TRUE] #' @param grayscale logical indicating whether the plot is to be in grayscale, by default, it is colored @@ -68,6 +82,15 @@ plot.network = function(network, labels = TRUE, grayscale = FALSE) { #' Construct a ggplot2/ggraph plot object for the given network and print it directly. #' +#' As a layout, by default, \code{igraph::layout.kamada.kawai} (also known as \code{igraph::layout_with_kk}) +#' is used, unless a graph attribute "layout" is set. For a comprehensive list of layouts and more information +#' on layouts in general, see \link{http://igraph.org/r/doc/layout_.html}. +#' To set the graph attribute on your network, run the following code while replacing \code{layout.to.set} +#' to your liking: \code{network = igraph::set.graph.attribute(network, "layout", layout.to.set)}. +#' +#' Note: The names for the vertex types are taken from the variables \code{PLOT.VERTEX.TYPE.AUTHOR} and +#' \code{PLOT.VERTEX.TYPE.ARTIFACT}. The defaults are \code{"Developer"} and \code{TYPE.ARTIFACT}, respectively. +#' #' @param network the network to plot and print #' @param labels logical indicating whether vertex lables should be plotted [default: TRUE] #' @param grayscale logical indicating whether the plot is to be in grayscale, by default, it is colored @@ -83,6 +106,15 @@ plot.print.network = function(network, labels = TRUE, grayscale = FALSE) { #' Construct a ggplot2/ggraph plot object for the given network. #' +#' As a layout, by default, \code{igraph::layout.kamada.kawai} (also known as \code{igraph::layout_with_kk}) +#' is used, unless a graph attribute "layout" is set. For a comprehensive list of layouts and more information +#' on layouts in general, see \link{http://igraph.org/r/doc/layout_.html}. +#' To set the graph attribute on your network, run the following code while replacing \code{layout.to.set} +#' to your liking: \code{network = igraph::set.graph.attribute(network, "layout", layout.to.set)}. +#' +#' Note: The names for the vertex types are taken from the variables \code{PLOT.VERTEX.TYPE.AUTHOR} and +#' \code{PLOT.VERTEX.TYPE.ARTIFACT}. The defaults are \code{"Developer"} and \code{TYPE.ARTIFACT}, respectively. +#' #' @param network the network to plot #' @param labels logical indicating whether vertex lables should be plotted [default: TRUE] #' @param grayscale logical indicating whether the plot is to be in grayscale, by default, it is colored @@ -103,62 +135,63 @@ plot.get.plot.for.network = function(network, labels = TRUE, grayscale = FALSE) colors.vertex.label = "black" } - ## set size of vertices in legend - VERTEX.SIZE.LEGEND = VERTEX.SIZE / 2 - ## check if network is empty if (igraph::vcount(network) == 0) { network = create.empty.network(directed = igraph::is.directed(network)) + - igraph::vertices(c("", ""), type = c(TYPE.AUTHOR, TYPE.ARTIFACT)) # + igraph::edges(c(1, 2), type = TYPE.EDGES.INTER) - VERTEX.SIZE = 0 + igraph::vertices(c("", ""), type = c(TYPE.AUTHOR, TYPE.ARTIFACT)) + PLOT.VERTEX.SIZE = 0 } + ## properly set vertex-type names for legend + PLOT.VERTEX.TYPES = c(PLOT.VERTEX.TYPE.AUTHOR, PLOT.VERTEX.TYPE.ARTIFACT) + names(PLOT.VERTEX.TYPES) = c(TYPE.AUTHOR, TYPE.ARTIFACT) + ## fix the type attributes (add new ones, also named) network = plot.fix.type.attributes(network, colors.vertex = colors.vertex, colors.edge = colors.edge) + ## set network layout + if (!("layout" %in% igraph::list.graph.attributes(network))) { + network = igraph::set.graph.attribute(network, "layout", igraph::layout.kamada.kawai) + } + ## create a ggraph object - p = ggraph::ggraph(network, layout = "igraph", algorithm = "nicely") + p = ggraph::ggraph(network) ## plot edges if there are any if (igraph::ecount(network) > 0) { p = p + ggraph::geom_edge_fan( mapping = ggplot2::aes(colour = edge.type, linetype = edge.type), - end_cap = ggraph::circle(VERTEX.SIZE + 3, "pt"), - start_cap = ggraph::circle(VERTEX.SIZE + 3, "pt"), + end_cap = ggraph::circle(PLOT.VERTEX.SIZE + 3, "pt"), + start_cap = ggraph::circle(PLOT.VERTEX.SIZE + 3, "pt"), arrow = if (igraph::is.directed(network)) { - ggplot2::arrow(length = ggplot2::unit(VERTEX.SIZE / 2, 'pt'), ends = "last", type = "closed") + ggplot2::arrow(length = ggplot2::unit(PLOT.VERTEX.SIZE / 2, 'pt'), ends = "last", type = "closed") } else { NULL } ) } - ## vertex and edge types - values.vertex.type = c(16, 15) - names(values.vertex.type) = names(PLOT.NAMES.BY.TYPE.VERTEX) - values.edge.type = c("dashed", "solid") - names(values.edge.type) = names(PLOT.NAMES.BY.TYPE.EDGE) - + ## construct plot with proper colors and shapes everywhere p = p + ## plot vertices - ggraph::geom_node_point(ggplot2::aes(color = vertex.type, shape = vertex.type), size = VERTEX.SIZE) + + ggraph::geom_node_point(ggplot2::aes(color = vertex.type, shape = vertex.type), size = PLOT.VERTEX.SIZE) + ggraph::geom_node_text(ggplot2::aes(label = if (labels) name else c("")), size = 3.5, color = colors.vertex.label) + ## scale vertices (colors and styles) - ggplot2::scale_shape_manual("Vertices", values = values.vertex.type, labels = PLOT.NAMES.BY.TYPE.VERTEX) + - ggplot2::scale_color_manual("Vertices", values = colors.vertex, labels = PLOT.NAMES.BY.TYPE.VERTEX) + + ggplot2::scale_shape_manual("Vertices", values = PLOT.SHAPE.VERTEX, labels = PLOT.VERTEX.TYPES) + + ggplot2::scale_color_manual("Vertices", values = colors.vertex, labels = PLOT.VERTEX.TYPES) + ## scale edges (colors and styles) - ggraph::scale_edge_linetype_manual("Relations", values = values.edge.type, labels = PLOT.NAMES.BY.TYPE.EDGE) + - ggraph::scale_edge_colour_manual("Relations", values = colors.edge, labels = PLOT.NAMES.BY.TYPE.EDGE) + + ggraph::scale_edge_linetype_manual("Relations", values = PLOT.SHAPE.EDGE) + + ggraph::scale_edge_colour_manual("Relations", values = colors.edge) + ## theme ggplot2::theme_light() + ggplot2::guides( ## reduce size of symbols in legend - shape = ggplot2::guide_legend(override.aes = list(size = VERTEX.SIZE.LEGEND)) + shape = ggplot2::guide_legend(override.aes = list(size = PLOT.VERTEX.SIZE.LEGEND)) ) + ggplot2::theme( legend.position = "bottom", @@ -195,26 +228,20 @@ plot.get.plot.for.network = function(network, labels = TRUE, grayscale = FALSE) #' Furthermore, the following attributes are added to either vertices or edges: #' - vertex.color = a color code (hex or else) for coloring the vertices (see 'colors.vertex' parameter), #' - edge.color = a color code (hex or else) for coloring the edges (see 'colors.edge' parameter), -#' - vertex.type = the old vertex attribute 'type', but as a character, -#' - edge.type = the old edge attribute 'type', but as a character, -#' - vertex.type.char = a word/name describing the vertex type (see PLOT.NAMES.BY.TYPE.VERTEX), and -#' - edge.type.char = a word/name describing the edge type (see PLOT.NAMES.BY.TYPE.EDGE). +#' - vertex.type = a copy of the old vertex attribute 'type', and +#' - edge.type = a copy of the old edge attribute 'type'. #' #' @param network the igraph object to augment -#' @param colors.vertex a vector of length 2, the entries named with 'TYPE.AUTHOR' and 'TYPE.ARTIFACT' +#' @param colors.vertex a vector of length 2, the entries named with the values of 'TYPE.AUTHOR' and 'TYPE.ARTIFACT' #' [default: PLOT.COLORS.BY.TYPE.VERTEX] -#' @param colors.edge a vector of length 2, the entries named with 'TYPE.EDGES.INTER' and 'TYPE.EDGES.INTRA' +#' @param colors.edge a vector of length 2, the entries named with the values of 'TYPE.EDGES.INTER' and 'TYPE.EDGES.INTRA' #' [default: PLOT.COLORS.BY.TYPE.EDGE] #' #' @return the old network with the new and changed vertex and edge attributes plot.fix.type.attributes = function(network, colors.vertex = PLOT.COLORS.BY.TYPE.VERTEX, colors.edge = PLOT.COLORS.BY.TYPE.EDGE) { ## copy type attribute to vertex.type and edge.type - network = igraph::set.vertex.attribute(network, "vertex.type", value = as.character(igraph::get.vertex.attribute(network, "type"))) - network = igraph::set.vertex.attribute(network, "vertex.type.char", - value = as.character(PLOT.NAMES.BY.TYPE.VERTEX[ igraph::get.vertex.attribute(network, "vertex.type") ])) - network = igraph::set.edge.attribute(network, "edge.type", value = as.character(igraph::get.edge.attribute(network, "type"))) - network = igraph::set.edge.attribute(network, "edge.type.char", - value = as.character(PLOT.NAMES.BY.TYPE.EDGE[ igraph::get.edge.attribute(network, "edge.type") ])) + network = igraph::set.vertex.attribute(network, "vertex.type", value = igraph::get.vertex.attribute(network, "type")) + network = igraph::set.edge.attribute(network, "edge.type", value = igraph::get.edge.attribute(network, "type")) ## add edge and vertex colors network = igraph::set.vertex.attribute(network, "vertex.color", @@ -222,11 +249,11 @@ plot.fix.type.attributes = function(network, colors.vertex = PLOT.COLORS.BY.TYPE network = igraph::set.edge.attribute(network, "edge.color", value = colors.edge[ igraph::get.edge.attribute(network, "edge.type") ]) - ## adjust 'type' attribute for vertices for bipartite plotting + ## adjust 'type' attribute for vertices for bipartite plotting (we need Booleans there) types = igraph::get.vertex.attribute(network, "type") network = igraph::remove.vertex.attribute(network, "type") network = igraph::set.vertex.attribute(network, "type", value = sapply( - types, function(t) switch(t, FALSE, TRUE) + types, function(t) return(t == TYPE.ARTIFACT) )) return(network)