From 730cc544edbb30ea3aa89a91e123e74b18a942c6 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Fri, 23 Mar 2018 16:45:17 +0100 Subject: [PATCH 1/7] Fix gray-scale plotting of networks Due to a copy-paste error, gray-scale plotting misbehaved. This is fixed by setting proper names on the correct vectors. Thanks to @ecklbarb for pointing this out. Signed-off-by: Claus Hunsen --- util-plot.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util-plot.R b/util-plot.R index bd846e85..bd736676 100644 --- a/util-plot.R +++ b/util-plot.R @@ -38,9 +38,9 @@ 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") From 15d8e5f554d845099ab33065e57d028fed1d3835 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Fri, 23 Mar 2018 16:54:25 +0100 Subject: [PATCH 2/7] Change default plotting layout to 'igraph::layout.kamada.kawai' When plotting networks using the default layout of the package 'ggraph' (i.e., 'igraph::layout_nicely'), we experienced segfaults in R. Therefore, we change the default layout to 'igraph::layout.kamada.kawai'. This essentially fixes #109. Thanks to @hechtlC for experiencing the problems and @bockthom for helping to find a solution. Signed-off-by: Claus Hunsen --- util-plot.R | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/util-plot.R b/util-plot.R index bd736676..c6a034dd 100644 --- a/util-plot.R +++ b/util-plot.R @@ -54,6 +54,12 @@ 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)}. +#' #' @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 +74,12 @@ 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)}. +#' #' @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 +95,12 @@ 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)}. +#' #' @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 @@ -116,8 +134,13 @@ plot.get.plot.for.network = function(network, labels = TRUE, grayscale = FALSE) ## 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) { From f2b785118508a6b7ef88130bfcc10703765fa70a Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Fri, 23 Mar 2018 17:00:46 +0100 Subject: [PATCH 3/7] Remove parameter 'color.attr' from 'motifs.search.in.network' As we always match motifs on the vertex attribute 'type', the existing parameter to the function 'motifs.search.in.network' is removed. The test suite is adapted accordingly. Additionally, the documentation is updated. Signed-off-by: Claus Hunsen --- tests/test-motifs.R | 20 ++++++++++---------- util-motifs.R | 27 ++++++++++++++------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/test-motifs.R b/tests/test-motifs.R index d857b731..06e7fa30 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-motifs.R b/util-motifs.R index fac660e3..7438ed3c 100644 --- a/util-motifs.R +++ b/util-motifs.R @@ -71,33 +71,35 @@ MOTIFS.SQUARE.NEGATIVE = igraph::make_empty_graph(directed = FALSE) + #' 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 = igraph::get.vertex.attribute(network, "type"), + vertex.color2 = igraph::get.vertex.attribute(motif, "type")) ## 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 = igraph::get.vertex.attribute(network, "type", index = seq) names = igraph::get.vertex.attribute(network, "name", index = seq) ## sort vertex sequence by types and names @@ -160,7 +162,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 ) }) From 59871b712a723e104c869c483d4f9a13761bf481 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Fri, 23 Mar 2018 17:16:09 +0100 Subject: [PATCH 4/7] Change/Improve values of vertex-type and edge-type constants The vertex and edge types as defined in the networks module ('TYPE.AUTHOR', 'TYPE.ARTIFACT', 'TYPE.EDGES.INTRA', and 'TYPE.EDGES.INTER') are defined as magic numbers (historically coming from vector indices). We refactor them to be of type 'character'. This way, we are able to actually "read" the vertex and edge attributes without any lookup from the network objects we construct. The constants are now defined as follows: TYPE.AUTHOR = "Author" TYPE.ARTIFACT = "Artifact" TYPE.EDGES.INTRA = "Unipartite" TYPE.EDGES.INTER = "Bipartite" As the motif identification works on numerics for the vertex encoding (see 'igraph::subgraph_isomorphisms', method 'vf2'), the corresponding code is adapted to transform the (now) character constants reliably back to numerics. For this, we introduce the mapping 'MOTIF.TYPE.MAPPING' and the function 'get.vertex.types.as.numeric'. As we manually translate vertex and edge types to strings for display in the plotting module, the module gets appropriate fixes and clean-ups. Here, the legend name for author vertices is not 'TYPE.AUTHOR', but "Developer". The behavior can be overwritten via setting 'PLOT.VERTEX.TYPE.AUTHOR' (and 'PLOT.VERTEX.TYPE.ARTIFACT', respectively) appropriately. This fixes #110. Thanks to @bockthom for his numerous remarks on this change. Signed-off-by: Claus Hunsen --- util-motifs.R | 47 +++++++++++++++++++++++++-- util-networks.R | 12 +++---- util-plot.R | 86 ++++++++++++++++++++++++++----------------------- 3 files changed, 95 insertions(+), 50 deletions(-) diff --git a/util-motifs.R b/util-motifs.R index 7438ed3c..c462b17c 100644 --- a/util-motifs.R +++ b/util-motifs.R @@ -66,6 +66,47 @@ 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 ------------------------------------------ @@ -93,13 +134,13 @@ 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, "type"), - vertex.color2 = igraph::get.vertex.attribute(motif, "type")) + 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, "type", 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 diff --git a/util-networks.R b/util-networks.R index 12f04dcc..68c208b7 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" ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / diff --git a/util-plot.R b/util-plot.R index c6a034dd..c4c16c64 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") @@ -42,11 +47,11 @@ 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.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) ## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / @@ -60,6 +65,9 @@ names(PLOT.NAMES.BY.TYPE.EDGE) = c(TYPE.EDGES.INTRA, TYPE.EDGES.INTER) #' 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 @@ -80,6 +88,9 @@ plot.network = function(network, labels = TRUE, grayscale = FALSE) { #' 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 @@ -101,6 +112,9 @@ plot.print.network = function(network, labels = TRUE, grayscale = FALSE) { #' 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 @@ -121,16 +135,17 @@ 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) @@ -147,41 +162,36 @@ plot.get.plot.for.network = function(network, labels = TRUE, grayscale = FALSE) 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", @@ -218,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", @@ -245,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) From 56b1f1f6d1b5e2c92b759620463169a4cf4388e6 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Fri, 23 Mar 2018 17:21:45 +0100 Subject: [PATCH 5/7] Update changelog Signed-off-by: Claus Hunsen --- NEWS.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/NEWS.md b/NEWS.md index 09f29aed..84d07d4c 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 From 04180c4079854b83638618c0714abb0656afcd06 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Mon, 26 Mar 2018 10:37:35 +0200 Subject: [PATCH 6/7] Enforce coding guideline on "authors" In some places, we still use the word "developer" in our codebase, although we state in the contribution guide that we only use "author". This is fixed by replacing the occurrences properly. Thanks to @bockthom for pointing this out. Signed-off-by: Claus Hunsen --- util-core-peripheral.R | 4 ++-- util-networks.R | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/util-core-peripheral.R b/util-core-peripheral.R index 5b98f4fa..3eb71d2a 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-networks.R b/util-networks.R index 68c208b7..2948a9f4 100644 --- a/util-networks.R +++ b/util-networks.R @@ -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) From e1a339d1610c18bcc2b87ff6e065a7c257f62b70 Mon Sep 17 00:00:00 2001 From: Claus Hunsen Date: Mon, 26 Mar 2018 14:10:59 +0200 Subject: [PATCH 7/7] Fix plot layout for sample network in showcase file Signed-off-by: Claus Hunsen --- showcase.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/showcase.R b/showcase.R index 4f32a1b4..c3f62609 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)