diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..d61b28f --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,14 @@ +^pkgdown$ +^_pkgdown\.yml$ +^.github$ +^inst/pkgdown$ +^docs$ +^.*\.Rproj$ +^\.Rproj\.user$ +^README\.Rmd$ +^README +^README-.*\.png$ +^\.travis\.yml$ +^appveyor\.yml$ +^codecov\.yml$ +^\.github$ diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0343527 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,25 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for +everyone, regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or +imagery, derogatory comments or personal attacks, trolling, public or private harassment, +insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, +commits, code, wiki edits, issues, and other contributions that are not aligned to this +Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed +from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by +opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the Contributor Covenant +(https://www.contributor-covenant.org), version 1.0.0, available at +https://contributor-covenant.org/version/1/0/0/. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6afec4b --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to ggnetwork + +This outlines how to propose a change to ggnetwork. For more detailed +info about contributing to this, and other tidyverse packages, please see the +[**development contributing guide**](https://rstd.io/tidy-contrib). + +### Fixing typos + +Small typos or grammatical errors in documentation may be edited directly using +the GitHub web interface, so long as the changes are made in the _source_ file. + +* YES: you edit a roxygen comment in a `.R` file below `R/`. +* NO: you edit an `.Rd` file below `man/`. + +### Prerequisites + +Before you make a substantial pull request, you should always file an issue and +make sure someone from the team agrees that it’s a problem. If you’ve found a +bug, create an associated issue and illustrate the bug with a minimal +[reprex](https://www.tidyverse.org/help/#reprex). + +### Pull request process + +* We recommend that you create a Git branch for each pull request (PR). +* Look at the Travis and AppVeyor build status before and after making changes. +The `README` should contain badges for any continuous integration services used +by the package. +* New code should follow the tidyverse [style guide](http://style.tidyverse.org). +You can use the [styler](https://CRAN.R-project.org/package=styler) package to +apply these styles, but please don't restyle code that has nothing to do with +your PR. +* We use [roxygen2](https://cran.r-project.org/package=roxygen2), with +[Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/markdown.html), +for documentation. +* We use [testthat](https://cran.r-project.org/package=testthat). Contributions +with test cases included are easier to accept. +* For user-facing changes, add a bullet to the top of `NEWS.md` below the +current development version header describing the changes made followed by your +GitHub username, and links to relevant issue(s)/PR(s). + +### Code of Conduct + +Please note that the ggnetwork project is released with a +[Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this +project you agree to abide by its terms. + +### See tidyverse [development contributing guide](https://rstd.io/tidy-contrib) +for further details. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..84f5c43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +Please briefly describe your problem and what output you expect. If you have a question, please don't use this form. Instead, ask on or . + +Please include a minimal reproducible example (AKA a reprex). If you've never heard of a [reprex](https://reprex.tidyverse.org/) before, start by reading . + +--- + +Brief description of the problem + +```r +# insert reprex here +``` diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..736a724 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,35 @@ +# Getting help with ggnetwork + +Thanks for using ggnetwork. Before filing an issue, there are a few places +to explore and pieces to put together to make the process as smooth as possible. + +Start by making a minimal **repr**oducible **ex**ample using the +[reprex](https://reprex.tidyverse.org/) package. If you haven't heard of or used +reprex before, you're in for a treat! Seriously, reprex will make all of your +R-question-asking endeavors easier (which is a pretty insane ROI for the five to +ten minutes it'll take you to learn what it's all about). For additional reprex +pointers, check out the [Get help!](https://www.tidyverse.org/help/) section of +the tidyverse site. + +Armed with your reprex, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). + + * If it's a question: start with [community.rstudio.com](https://community.rstudio.com/), + and/or StackOverflow. There are more people there to answer questions. + * If it's a bug: you're in the right place, file an issue. + * If you're not sure: let the community help you figure it out! If your + problem _is_ a bug or a feature request, you can easily return here and + report it. + +Before opening a new issue, be sure to [search issues and pull requests](https://github.com/tidyverse/ggnetwork/issues) to make sure the +bug hasn't been reported and/or already fixed in the development version. By +default, the search will be pre-populated with `is:issue is:open`. You can +[edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/) +(e.g. `is:pr`, `is:closed`) as needed. For example, you'd simply +remove `is:open` to search _all_ issues in the repo, open or closed. + + +If you _are_ in the right place, and need to file an issue, please review the +["File issues"](https://www.tidyverse.org/contribute/#issues) paragraph from +the tidyverse contributing guidelines. + +Thanks for your help! diff --git a/.gitignore b/.gitignore index d86621c..b437030 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ +**.Rproj.user .Rproj.user -.Rhistory -.RData -.Ruserdata -.Rbuildignore -ggnetwork.Rproj +docs \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 468cc47..710f270 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,44 @@ -language: r +language: R cache: packages +sudo: true + + +env: + global: + - _R_CHECK_FORCE_SUGGESTS_=false + - R_REMOTES_NO_ERRORS_FROM_WARNINGS=true + - _R_CHECK_SYSTEM_CLOCK_=false + +before_install: + - sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable --yes + - sudo apt-get --yes --force-yes update -qq + - sudo apt-get install --yes libudunits2-dev libproj-dev libgeos-dev libgdal-dev + - Rscript -e 'update.packages(ask = FALSE)' + +r_github_packages: + - r-lib/covr + +matrix: + include: + - r: devel + - r: release + after_success: Rscript -e 'covr::coveralls(); covr::codecov()' + before_deploy: Rscript -e 'remotes::install_cran("pkgdown")' + deploy: + provider: script + script: Rscript -e 'pkgdown::deploy_site_github()' + skip_cleanup: true + - r: 3.1 + - r: 3.2 + - r: 3.3 + - r: 3.4 + - r: 3.5 + - r: 3.6 + +after_success: + - Rscript -e 'covr::coveralls(); covr::codecov()' + +notifications: + email: + on_success: change + on_failure: change diff --git a/DESCRIPTION b/DESCRIPTION index 1dae1e2..4ed4fb4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggnetwork Type: Package Title: Geometries to Plot Networks with 'ggplot2' -Version: 0.5.4 +Version: 0.5.6.9000 Date: 2016-03-24 Maintainer: François Briatte Authors@R: c( @@ -14,24 +14,30 @@ Authors@R: c( ) Description: Geometries to plot network objects with 'ggplot2'. License: GPL-3 -Encoding: UTF-8 URL: https://github.com/briatte/ggnetwork BugReports: https://github.com/briatte/ggnetwork/issues -LazyData: true -NeedsCompilation: no -RoxygenNote: 6.0.1 -VignetteBuilder: knitr Depends: - ggplot2 (>= 2.0.0), R (>= 3.1), + ggplot2 (>= 2.0.0) Imports: ggrepel (>= 0.5), network, + igraph, sna, utils Suggests: - igraph, knitr, testthat -Enhances: - ggplot2 +Collate: + 'utilities.R' + 'fortify-igraph.R' + 'fortify-network.R' + 'geom-nodes.R' + 'geom-edges.R' + 'ggnetwork.R' +VignetteBuilder: knitr +RoxygenNote: 6.1.1 +Roxygen: list(markdown = TRUE) +Encoding: UTF-8 +LazyData: true +NeedsCompilation: no diff --git a/NAMESPACE b/NAMESPACE index 6411436..b538ae1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ S3method(fortify,igraph) S3method(fortify,network) +export(fortify) export(geom_edgelabel) export(geom_edgelabel_repel) export(geom_edges) @@ -15,17 +16,6 @@ export(geom_nodetext_repel) export(ggnetwork) export(theme_blank) export(theme_facet) -import(sna) -importFrom(ggplot2,GeomCurve) -importFrom(ggplot2,GeomLabel) -importFrom(ggplot2,GeomPoint) -importFrom(ggplot2,GeomSegment) -importFrom(ggplot2,GeomText) +export(unit) importFrom(ggplot2,fortify) -importFrom(ggplot2,ggproto) -importFrom(ggplot2,layer) -importFrom(ggplot2,position_nudge) importFrom(ggplot2,unit) -importFrom(ggrepel,GeomLabelRepel) -importFrom(ggrepel,GeomTextRepel) -importFrom(utils,installed.packages) diff --git a/NEWS.md b/NEWS.md index 57727bd..2a657f1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,17 +1,68 @@ +ggnetwork 0.5.6 +=============== + +## Repository changes + +* Add `README.Rmd` (replace `README.md`), + + use badges. + + list all GitHub contributors. + + add Contributing, Code of Conduct Issue Template and Support markdown file (`usethis`). + +* Improve `.travis.yml`, + + use `pkgdown` for website deployment. + - add `_pkgdown.yml` configuration file. + + use `covr` for code coverage. + - add default `codecov.yml` configuration file. + +* Add `ggnetwork.Rproj`, for ease of use within Rstudio. + +## Minor improvements and fixes + +* In `DESCRIPTION`, + + update RoxygenNote version. + + remove `ggplot2` from `Enhances` field. + + add `Collate` field to load sequentially the functions. + + move `igraph` to `Imports` field. + +* Remove `inst/doc/` directory, *i.e.*, the vignette is part of the `pkgdown` website. + +* Use tidy code style. + +* In `R/fortify-igraph.R` and `R/fortify-network.R`, + + use subsetting functions instead of `with` and `transform` (*i.e.*, intended to be use interactively). + + fix issue from CRAN check with undefined global variables. + + remove namespace loading for `sna` package (*i.e.*, `gplot.layout.*` functions). + +* In `R/geom-nodes.R` and `R/geom-edges.R`, + + remove unnecessary "@importFom". + + add missing function packages prefix. + +* In `R/ggnetwork.R`, `switch` and `tryCatch` to make the `igraph` and `network` testing consistent. + +* In `R/utilities.R`, reexport `fortify` and `unit` from `ggplot2`. + + + +ggnetwork 0.5.5 (2017-08-XX) +============================ + + ggnetwork 0.5.4 (2017-07-XX) ----------------------------- +============================ -Changes +## Minor improvements and fixes * Added native support for igraph. Thanks to [Jake Fisher](www.src.isr.umich.edu/people/jake-fisher/). ggnetwork 0.5.3 (2016-06-XX) ----------------------------- +============================ + +## Repository changes -Added Travis CI. +* Added Travis CI. -FIXES +## Minor improvements and fixes * Export ggplot2::Stat to enable loading ggnetwork first (#14). Thanks to Tyler Rinker. @@ -19,23 +70,22 @@ FIXES * Safer calls to the sna package (#9). Thanks to Michał Bojanowski. + ggnetwork 0.5.2 (2016-05-01) ----------------------------- +============================ -FIXES +## Minor improvements and fixes * Fixed a bug that removed labels from strictly vertical or strictly horizontal edges (#5). * Fixed a small documentation issue that was corrected in roxygen 5.0.2 (#4). -CHANGES - * Support for segment colors in all geoms using ggrepel 0.5.1 (#3). * Added some acknowledgements to the README and links to the DESCRIPTION. ggnetwork 0.5.1 (2016-03-25) ----------------------------- +============================ First CRAN release. diff --git a/R/fortify-igraph.R b/R/fortify-igraph.R index da1b409..6cf39ab 100644 --- a/R/fortify-igraph.R +++ b/R/fortify-igraph.R @@ -2,124 +2,115 @@ #' #' @param model an object of class \code{\link[igraph:igraph-package]{igraph}} #' @param data not used by this method. -#' @param layout a function call to an -#' \code{\link[igraph:igraph-package]{igraph}} layout function, such as -#' \code{\link[igraph]{layout_nicely}} (the default), or a 2 column matrix -#' giving the x and y coordinates for the vertices. -#' See \code{\link[igraph]{layout_}} for details. +#' @param layout a function call to an +#' \code{\link[igraph:igraph-package]{igraph}} layout function, such as +#' \code{\link[igraph]{layout_nicely}} (the default), or a 2 column matrix +#' giving the x and y coordinates for the vertices. +#' See \code{\link[igraph]{layout_}} for details. #' @param arrow.gap a parameter that will shorten the network edges in order to -#' avoid overplotting edge arrows and nodes; defaults to \code{0} when the -#' network is undirected (no edge shortening), or to \code{0.025} when the -#' network is directed. Small values near \code{0.025} will generally achieve -#' good results when the size of the nodes is reasonably small. +#' avoid overplotting edge arrows and nodes; defaults to \code{0} when the +#' network is undirected (no edge shortening), or to \code{0.025} when the +#' network is directed. Small values near \code{0.025} will generally achieve +#' good results when the size of the nodes is reasonably small. #' @param by a character vector that matches an edge attribute, which will be -#' used to generate a data frame that can be plotted with -#' @param ... additional parameters for the \code{\link[igraph]{layout_}} -#' function -#' @method fortify igraph -#' @importFrom utils installed.packages +#' used to generate a data frame that can be plotted with +#' @param ... additional parameters for the \code{\link[igraph]{layout_}} function +#' #' @export -fortify.igraph <- function(model, data = NULL, layout = igraph::nicely(), - arrow.gap = ifelse(igraph::is.directed(x), 0.025, 0), - by = NULL, ...) { - - - x = model - +fortify.igraph <- function( + model, + data = NULL, + layout = igraph::nicely(), + arrow.gap = ifelse(igraph::is.directed(model), 0.025, 0), + by = NULL, + ... +) { # node placement - if (class(layout) == "matrix" && - nrow(layout) == igraph::gorder(x) && - ncol(layout) == 2) { - nodes = layout[, 1:2 ] + if (class(layout) == "matrix" && identical(dim(layout), c(igraph::gorder(model), 2L))) { + nodes <- layout[, 1:2 ] } else { - nodes <- igraph::layout_(x, layout, ...) + nodes <- igraph::layout_(model, layout, ...) } - + # store coordinates - nodes = data.frame(nodes) - names(nodes) = c("x", "y") - + nodes <- data.frame(nodes) + names(nodes) <- c("x", "y") + # rescale coordinates nodes$x <- scale_safely(nodes$x) nodes$y <- scale_safely(nodes$y) - + # import vertex attributes - if (length(igraph::list.vertex.attributes(x))) { + if (length(igraph::list.vertex.attributes(model))) { nodes <- cbind( nodes, sapply( - igraph::list.vertex.attributes(x), + igraph::list.vertex.attributes(model), FUN = igraph::get.vertex.attribute, - graph = x, - USE.NAMES = T + graph = model, + USE.NAMES = TRUE ) ) } - + # edge list - edges <- igraph::as_edgelist(x, names = F) - + edges <- igraph::as_edgelist(model, names = FALSE) + # edge list (if there are duplicated rows) if (nrow(edges[, 1:2]) > nrow(unique(edges[, 1:2]))) { warning("duplicated edges detected") } - - edges = data.frame(nodes[edges[, 1], 1:2], nodes[edges[, 2], 1:2]) - names(edges) = c("x", "y", "xend", "yend") - + + edges <- data.frame(nodes[edges[, 1], 1:2], nodes[edges[, 2], 1:2]) + names(edges) <- c("x", "y", "xend", "yend") + # arrow gap (thanks to @heike and @ethen8181 for their work on this issue) if (arrow.gap > 0) { - x.length = with(edges, xend - x) - y.length = with(edges, yend - y) - arrow.gap = with(edges, arrow.gap / sqrt(x.length ^ 2 + y.length ^ 2)) - edges = transform( - edges, - # x = x + arrow.gap * x.length, - # y = y + arrow.gap * y.length, - xend = x + (1 - arrow.gap) * x.length, - yend = y + (1 - arrow.gap) * y.length - ) + x.length <- edges$xend - edges$x + y.length <- edges$yend - edges$y + arrow.gap <- arrow.gap / sqrt(x.length^2 + y.length^2) + edges$xend <- edges$x + (1 - arrow.gap) * x.length + edges$yend <- edges$y + (1 - arrow.gap) * y.length } - + # import edge attributes - if (length(igraph::list.edge.attributes(x))) { + if (length(igraph::list.edge.attributes(model))) { edges <- cbind( edges, sapply( - igraph::list.edge.attributes(x), + igraph::list.edge.attributes(model), FUN = igraph::get.edge.attribute, - graph = x, - USE.NAMES = T - ) + graph = model, + USE.NAMES = TRUE ) + ) } # merge edges and nodes data - edges = merge(nodes, edges, by = c("x", "y"), all = TRUE) - + edges <- merge(nodes, edges, by = c("x", "y"), all = TRUE) + # add missing columns to nodes data - nodes$xend = nodes$x - nodes$yend = nodes$y - names(nodes) = names(edges)[1:ncol(nodes)] - + nodes$xend <- nodes$x + nodes$yend <- nodes$y + names(nodes) <- names(edges)[1:ncol(nodes)] + # make nodes data of identical dimensions to edges data missing.cols <- names(edges)[which(!(names(edges) %in% names(nodes)))] nodes[missing.cols] <- NA - + # panelize nodes (for temporal networks) if (!is.null(by)) { - nodes = lapply(sort(unique(edges[, by ])), function(x) { - y = nodes - y[, by ] = x + nodes <- lapply(sort(unique(edges[, by])), function(x) { + y <- nodes + y[, by] <- x y }) - nodes = do.call(rbind, nodes) + nodes <- do.call(rbind, nodes) } - - # return a data frame with network.size(x) + network.edgecount(x) rows, - # or length(unique(edges[, by ])) * network.size(x) + network.edgecount(x) + + # return a data frame with network.size(model) + network.edgecount(model) rows, + # or length(unique(edges[, by])) * network.size(model) + network.edgecount(model) # rows if the nodes have been panelized - unique(rbind(nodes, edges[ !is.na(edges$xend), ])) - + unique(rbind(nodes, edges[!is.na(edges$xend), ])) } diff --git a/R/fortify-network.R b/R/fortify-network.R index 6ada7f1..f025953 100644 --- a/R/fortify-network.R +++ b/R/fortify-network.R @@ -1,43 +1,40 @@ -if (getRversion() >= "2.15.1") { - utils::globalVariables(c("xend", "yend")) -} - #' Fortify method for networks of class \code{\link[network]{network}} #' #' See the vignette at \url{https://briatte.github.io/ggnetwork/} for a #' description of both this function and the rest of the \code{ggnetwork} #' package. +#' #' @param model an object of class \code{\link[network]{network}}. #' @param data not used by this method. #' @param layout a network layout supplied by \code{\link[sna]{gplot.layout}}, -#' such as \code{"fruchtermanreingold"} (the default), or a two-column matrix -#' with as many rows as there are nodes in the network, in which case the -#' matrix is used as nodes coordinates. +#' such as \code{"fruchtermanreingold"} (the default), or a two-column matrix +#' with as many rows as there are nodes in the network, in which case the +#' matrix is used as nodes coordinates. #' @param weights the name of an edge attribute to use as edge weights when -#' computing the network layout, if the layout supports such weights (see -#' 'Details'). -#' Defaults to \code{NULL} (no edge weights). +#' computing the network layout, if the layout supports such weights (see +#' 'Details'). +#' Defaults to \code{NULL} (no edge weights). #' @param arrow.gap a parameter that will shorten the network edges in order to -#' avoid overplotting edge arrows and nodes; defaults to \code{0} when the -#' network is undirected (no edge shortening), or to \code{0.025} when the -#' network is directed. Small values near \code{0.025} will generally achieve -#' good results when the size of the nodes is reasonably small. +#' avoid overplotting edge arrows and nodes; defaults to \code{0} when the +#' network is undirected (no edge shortening), or to \code{0.025} when the +#' network is directed. Small values near \code{0.025} will generally achieve +#' good results when the size of the nodes is reasonably small. #' @param by a character vector that matches an edge attribute, which will be -#' used to generate a data frame that can be plotted with -#' \code{\link[ggplot2]{facet_wrap}} or \code{\link[ggplot2]{facet_grid}}. The -#' nodes of the network will appear in all facets, at the same coordinates. -#' Defaults to \code{NULL} (no faceting). -#' @return a data.frame object. +#' used to generate a data frame that can be plotted with +#' \code{\link[ggplot2]{facet_wrap}} or \code{\link[ggplot2]{facet_grid}}. The +#' nodes of the network will appear in all facets, at the same coordinates. +#' Defaults to \code{NULL} (no faceting). #' @param ... additional parameters for the \code{layout} argument; see -#' \code{\link[sna]{gplot.layout}} for available options. +#' \code{\link[sna]{gplot.layout}} for available options. +#' #' @details \code{fortify.network} will return a warning if it finds duplicated -#' edges after converting the network to an edge list. Duplicated edges should -#' be eliminated in favour of single weighted edges before using a network -#' layout that supports edge weights, such as the Kamada-Kawai force-directed -#' placement algorithm. -#' @import sna -#' @importFrom ggplot2 fortify -#' @method fortify network +#' edges after converting the network to an edge list. Duplicated edges should +#' be eliminated in favour of single weighted edges before using a network +#' layout that supports edge weights, such as the Kamada-Kawai force-directed +#' placement algorithm. +#' +#' @return a data.frame object. +#' #' @examples #' if (require(ggplot2) && require(network)) { #' @@ -64,142 +61,143 @@ if (getRversion() >= "2.15.1") { #' ggnetwork(emon[[1]], layout = "kamadakawai", weights = "Frequency") #' #' # plot example with straight edges -#' ggplot(ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), -#' aes(x, y, xend = xend, yend = yend)) + +#' ggplot( +#' ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), +#' aes(x, y, xend = xend, yend = yend) +#' ) + #' geom_edges(aes(color = Frequency), -#' arrow = arrow(length = unit(10, "pt"), type = "closed")) + +#' arrow = arrow(length = unit(10, "pt"), type = "closed") +#' ) + #' geom_nodes(aes(size = Formalization)) + #' scale_color_gradient(low = "grey50", high = "tomato") + #' scale_size_area(breaks = 1:3) + #' theme_blank() #' #' # plot example with curved edges -#' ggplot(ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), -#' aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(color = Frequency), curvature = 0.1, -#' arrow = arrow(length = unit(10, "pt"), type = "open")) + +#' ggplot( +#' ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), +#' aes(x, y, xend = xend, yend = yend) +#' ) + +#' geom_edges(aes(color = Frequency), +#' curvature = 0.1, +#' arrow = arrow(length = unit(10, "pt"), type = "open") +#' ) + #' geom_nodes(aes(size = Formalization)) + #' scale_color_gradient(low = "grey50", high = "tomato") + #' scale_size_area(breaks = 1:3) + #' theme_blank() #' #' # facet by edge attribute -#' ggplot(ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency"), -#' aes(x, y, xend = xend, yend = yend)) + +#' ggplot( +#' ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency"), +#' aes(x, y, xend = xend, yend = yend) +#' ) + #' geom_edges(arrow = arrow(length = unit(5, "pt"), type = "closed")) + #' geom_nodes() + #' theme_blank() + #' facet_grid(. ~ Frequency, labeller = label_both) #' #' # user-provided layout -#' ggplot(ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)), -#' aes(x, y, xend = xend, yend = yend)) + +#' ggplot( +#' ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)), +#' aes(x, y, xend = xend, yend = yend) +#' ) + #' geom_edges(arrow = arrow(length = unit(5, "pt"), type = "closed")) + #' geom_nodes() + #' theme_blank() -#' #' } +#' #' @export -fortify.network <- function(model, data = NULL, - layout = "fruchtermanreingold", weights = NULL, - arrow.gap = ifelse(network::is.directed(model), 0.025, 0), - by = NULL, - ...) { - x = model - +fortify.network <- function( + model, + data = NULL, + layout = "fruchtermanreingold", + weights = NULL, + arrow.gap = ifelse(network::is.directed(model), 0.025, 0), + by = NULL, + ... +) { # node placement - if (class(layout) == "matrix" && - nrow(layout) == network::network.size(x) && - ncol(layout) == 2) { - nodes = layout[, 1:2 ] + if (class(layout) == "matrix" && identical(dim(layout), c(network::network.size(model), 2L))) { + nodes <- layout[, 1:2 ] } else { - layout = paste0("gplot.layout.", layout) - ns <- loadNamespace("sna") - if (!exists(layout, envir=ns, inherits=FALSE)) { - stop("unsupported layout") - } - nodes = do.call( utils::getFromNamespace(layout, ns), list(x, layout.par = list(...))) + layout <- eval(parse(text = paste0("sna::gplot.layout.", layout))) + nodes <- do.call(layout, list(model, layout.par = list(...))) } # store coordinates - nodes = data.frame(nodes) - names(nodes) = c("x", "y") + nodes <- data.frame(nodes) + names(nodes) <- c("x", "y") # rescale coordinates nodes$x <- scale_safely(nodes$x) nodes$y <- scale_safely(nodes$y) # import vertex attributes - for (y in network::list.vertex.attributes(x)) { - nodes = cbind(nodes, network::get.vertex.attribute(x, y)) - names(nodes)[ncol(nodes)] = y + for (y in network::list.vertex.attributes(model)) { + nodes <- cbind(nodes, network::get.vertex.attribute(model, y)) + names(nodes)[ncol(nodes)] <- y } # edge list - edges = network::as.matrix.network.edgelist(x, attrname = weights) + edges <- network::as.matrix.network.edgelist(model, attrname = weights) # edge list (if there are duplicated rows) if (nrow(edges[, 1:2, drop = FALSE]) > nrow(unique(edges[, 1:2, drop = FALSE]))) { warning("duplicated edges detected") } - edges = data.frame(nodes[edges[, 1], 1:2], nodes[edges[, 2], 1:2]) - names(edges) = c("x", "y", "xend", "yend") + edges <- data.frame(nodes[edges[, 1], 1:2], nodes[edges[, 2], 1:2]) + names(edges) <- c("x", "y", "xend", "yend") # arrow gap (thanks to @heike and @ethen8181 for their work on this issue) if (arrow.gap > 0) { - x.length = with(edges, xend - x) - y.length = with(edges, yend - y) - arrow.gap = with(edges, arrow.gap / sqrt(x.length ^ 2 + y.length ^ 2)) - edges = transform( - edges, - # x = x + arrow.gap * x.length, - # y = y + arrow.gap * y.length, - xend = x + (1 - arrow.gap) * x.length, - yend = y + (1 - arrow.gap) * y.length - ) + x.length <- edges$xend - edges$x + y.length <- edges$yend - edges$y + arrow.gap <- arrow.gap / sqrt(x.length^2 + y.length^2) + edges$xend <- edges$x + (1 - arrow.gap) * x.length + edges$yend <- edges$y + (1 - arrow.gap) * y.length } # import edge attributes - for (y in network::list.edge.attributes(x)) { - edges = cbind(edges, network::get.edge.attribute(x, y)) - names(edges)[ncol(edges)] = y + for (iattribute in network::list.edge.attributes(model)) { + edges <- cbind(edges, network::get.edge.attribute(model, iattribute)) + names(edges)[ncol(edges)] <- iattribute } - if (nrow(edges)!=0) { + if (nrow(edges) != 0) { # merge edges and nodes data - edges = merge(nodes, edges, by = c("x", "y"), all = TRUE) - + edges <- merge(nodes, edges, by = c("x", "y"), all = TRUE) + # add missing columns to nodes data - nodes$xend = nodes$x - nodes$yend = nodes$y - names(nodes) = names(edges)[1:ncol(nodes)] + nodes$xend <- nodes$x + nodes$yend <- nodes$y + names(nodes) <- names(edges)[1:ncol(nodes)] # make nodes data of identical dimensions to edges data for (y in names(edges)[(1 + ncol(nodes)):ncol(edges)]) { - nodes = cbind(nodes, NA) - names(nodes)[ncol(nodes)] = y + nodes <- cbind(nodes, NA) + names(nodes)[ncol(nodes)] <- y } # panelize nodes (for temporal networks) if (!is.null(by)) { - nodes = lapply(sort(unique(edges[, by ])), function(x) { - y = nodes - y[, by ] = x + nodes <- lapply(sort(unique(edges[, by])), function(x) { + y <- nodes + y[, by] <- x y }) - nodes = do.call(rbind, nodes) + nodes <- do.call(rbind, nodes) } - # return a data frame with network.size(x) + network.edgecount(x) rows, - # or length(unique(edges[, by ])) * network.size(x) + network.edgecount(x) + # return a data frame with network.size(model) + network.edgecount(model) rows, + # or length(unique(edges[, by ])) * network.size(model) + network.edgecount(model) # rows if the nodes have been panelized - return(unique(rbind(nodes, edges[ !is.na(edges$xend), ]))) + return(unique(rbind(nodes, edges[!is.na(edges$xend), ]))) } else { # add missing columns to nodes data - nodes$xend = nodes$x - nodes$yend = nodes$y + nodes$xend <- nodes$x + nodes$yend <- nodes$y return(nodes) } - } diff --git a/R/geom-edges.R b/R/geom-edges.R index 2c4f2fc..41668b5 100644 --- a/R/geom-edges.R +++ b/R/geom-edges.R @@ -7,85 +7,99 @@ #' \code{ncp} arguments of \code{\link[ggplot2]{geom_curve}} are also available: #' if \code{curvature} is set to any value above \code{0} (the default), the #' edges produced by \code{geom_edges} will be curved. +#' #' @inheritParams ggplot2::geom_segment #' @inheritParams ggplot2::geom_curve -#' @importFrom ggplot2 GeomSegment GeomCurve layer +#' #' @examples #' if (require(network) && require(sna)) { #' -#' # rerun if the example does not produce reciprocated ties -#' n <- network(rgraph(10, tprob = 0.2), directed = TRUE) -#' -#' # just edges -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(size = 1, colour = "steelblue") + -#' theme_blank() +#' # rerun if the example does not produce reciprocated ties +#' n <- network(rgraph(10, tprob = 0.2), directed = TRUE) #' -#' # with nodes -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(size = 1, colour = "steelblue") + -#' geom_nodes(size = 3, colour = "steelblue") + -#' theme_blank() +#' # just edges +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(size = 1, colour = "steelblue") + +#' theme_blank() #' -#' # with arrows -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(size = 1, colour = "steelblue", -#' arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + -#' geom_nodes(size = 3, colour = "steelblue") + -#' theme_blank() +#' # with nodes +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(size = 1, colour = "steelblue") + +#' geom_nodes(size = 3, colour = "steelblue") + +#' theme_blank() #' -#' # with curvature -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(size = 1, colour = "steelblue", curvature = 0.15, -#' arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + -#' geom_nodes(size = 3, colour = "steelblue") + -#' theme_blank() +#' # with arrows +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges( +#' size = 1, colour = "steelblue", +#' arrow = arrow(length = unit(0.5, "lines"), type = "closed") +#' ) + +#' geom_nodes(size = 3, colour = "steelblue") + +#' theme_blank() #' -#' # arbitrary categorical edge attribute -#' e <- sample(letters[ 1:2 ], network.edgecount(n), replace = TRUE) -#' set.edge.attribute(n, "type", e) -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(linetype = type), size = 1, curvature = 0.15, -#' arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + -#' geom_nodes(size = 3, colour = "steelblue") + -#' theme_blank() +#' # with curvature +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges( +#' size = 1, colour = "steelblue", curvature = 0.15, +#' arrow = arrow(length = unit(0.5, "lines"), type = "closed") +#' ) + +#' geom_nodes(size = 3, colour = "steelblue") + +#' theme_blank() #' -#' # arbitrary numeric edge attribute (signed network) -#' e <- sample(-2:2, network.edgecount(n), replace = TRUE) -#' set.edge.attribute(n, "weight", e) -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(colour = weight), curvature = 0.15, -#' arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + -#' geom_nodes(size = 3, colour = "grey50") + -#' scale_colour_gradient(low = "steelblue", high = "tomato") + -#' theme_blank() +#' # arbitrary categorical edge attribute +#' e <- sample(letters[ 1:2 ], network.edgecount(n), replace = TRUE) +#' set.edge.attribute(n, "type", e) +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(aes(linetype = type), +#' size = 1, curvature = 0.15, +#' arrow = arrow(length = unit(0.5, "lines"), type = "closed") +#' ) + +#' geom_nodes(size = 3, colour = "steelblue") + +#' theme_blank() #' -#' # draw only a subset of all edges -#' positive_weight <- function(x) { x[ x$weight >= 0, ] } -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(colour = weight), data = positive_weight) + -#' geom_nodes(size = 4, colour = "grey50") + -#' scale_colour_gradient(low = "gold", high = "tomato") + -#' theme_blank() +#' # arbitrary numeric edge attribute (signed network) +#' e <- sample(-2:2, network.edgecount(n), replace = TRUE) +#' set.edge.attribute(n, "weight", e) +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(aes(colour = weight), +#' curvature = 0.15, +#' arrow = arrow(length = unit(0.5, "lines"), type = "closed") +#' ) + +#' geom_nodes(size = 3, colour = "grey50") + +#' scale_colour_gradient(low = "steelblue", high = "tomato") + +#' theme_blank() #' +#' # draw only a subset of all edges +#' positive_weight <- function(x) { +#' x[ x$weight >= 0, ] +#' } +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(aes(colour = weight), data = positive_weight) + +#' geom_nodes(size = 4, colour = "grey50") + +#' scale_colour_gradient(low = "gold", high = "tomato") + +#' theme_blank() #' } +#' #' @export -geom_edges <- function(mapping = NULL, data = NULL, - position = "identity", arrow = NULL, - curvature = 0, angle = 90, ncp = 5, - na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, - ...) { - +geom_edges <- function( + mapping = NULL, + data = NULL, + position = "identity", + arrow = NULL, + curvature = 0, + angle = 90, + ncp = 5, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE, + ... +) { if (!curvature) { - geom = ggplot2::GeomSegment - params = list( - arrow = arrow, - na.rm = na.rm, - ... - ) + geom <- ggplot2::GeomSegment + params <- list(arrow = arrow, na.rm = na.rm, ...) } else { - geom = ggplot2::GeomCurve - params = list( + geom <- ggplot2::GeomCurve + params <- list( arrow = arrow, curvature = curvature, angle = angle, @@ -105,7 +119,6 @@ geom_edges <- function(mapping = NULL, data = NULL, inherit.aes = inherit.aes, params = params ) - } # note: the nudge_x,nudge_y arguments below are duplicated from the original @@ -120,44 +133,54 @@ geom_edges <- function(mapping = NULL, data = NULL, #' border around the edge labels. The labels will be drawn at mid-edges. #' \code{\link{geom_text}} and \code{\link{geom_label}} produce strictly #' identical results. +#' #' @inheritParams ggplot2::geom_label #' @param nudge_x,nudge_y Horizontal and vertical adjustment to nudge labels by. #' Useful for offsetting text from points, particularly on discrete scales. -#' @importFrom ggplot2 unit position_nudge layer GeomLabel +#' #' @examples #' if (require(network) && require(sna)) { +#' data(flo, package = "network") +#' n <- network(flo, directed = FALSE) #' -#' data(flo, package = "network") -#' n <- network(flo, directed = FALSE) -#' -#' # arbitrary categorical edge attribute -#' e <- sample(letters[ 1:4 ], network.edgecount(n), replace = TRUE) -#' set.edge.attribute(n, "type", e) +#' # arbitrary categorical edge attribute +#' e <- sample(letters[ 1:4 ], network.edgecount(n), replace = TRUE) +#' set.edge.attribute(n, "type", e) #' -#' # with labelled edges -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(colour = type)) + -#' geom_edgetext(aes(label = type, colour = type)) + -#' geom_nodes(size = 4, colour = "grey50") + -#' theme_blank() -#' -#' # label only a subset of all edges with arbitrary symbol -#' edge_type <- function(x) { x[ x$type == "a", ] } -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges() + -#' geom_edgetext(label = "=", data = edge_type) + -#' geom_nodes(size = 4, colour = "grey50") + -#' theme_blank() +#' # with labelled edges +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(aes(colour = type)) + +#' geom_edgetext(aes(label = type, colour = type)) + +#' geom_nodes(size = 4, colour = "grey50") + +#' theme_blank() #' +#' # label only a subset of all edges with arbitrary symbol +#' edge_type <- function(x) { +#' x[ x$type == "a", ] +#' } +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges() + +#' geom_edgetext(label = "=", data = edge_type) + +#' geom_nodes(size = 4, colour = "grey50") + +#' theme_blank() #' } +#' #' @export -geom_edgetext <- function(mapping = NULL, data = NULL, - position = "identity", parse = FALSE, ..., - nudge_x = 0, nudge_y = 0, - label.padding = unit(0.25, "lines"), - label.r = ggplot2::unit(0.15, "lines"), - label.size = 0, - na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { +geom_edgetext <- function( + mapping = NULL, + data = NULL, + position = "identity", + parse = FALSE, + ..., + nudge_x = 0, + nudge_y = 0, + label.padding = unit(0.25, "lines"), + label.r = unit(0.15, "lines"), + label.size = 0, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE +) { if (!missing(nudge_x) || !missing(nudge_y)) { if (!missing(position)) { stop("Specify either `position` or `nudge_x`/`nudge_y`", call. = FALSE) @@ -194,40 +217,45 @@ geom_edgetext <- function(mapping = NULL, data = NULL, #' \code{\link{geom_edgelabel_repel}} are identical to those of #' \code{\link[ggrepel]{geom_label_repel}}. \code{\link{geom_text_repel}} and #' \code{\link{geom_label_repel}} produce strictly identical results. +#' #' @inheritParams ggrepel::geom_label_repel #' @param nudge_x,nudge_y Horizontal and vertical adjustments to nudge the #' starting position of each text label. -#' @importFrom ggplot2 layer -#' @importFrom ggrepel GeomLabelRepel +#' #' @examples #' if (require(network) && require(sna)) { +#' data(flo, package = "network") +#' n <- network(flo, directed = FALSE) #' -#' data(flo, package = "network") -#' n <- network(flo, directed = FALSE) -#' -#' # arbitrary categorical edge attribute -#' e <- sample(1:4, network.edgecount(n), replace = TRUE) -#' set.edge.attribute(n, "day", e) +#' # arbitrary categorical edge attribute +#' e <- sample(1:4, network.edgecount(n), replace = TRUE) +#' set.edge.attribute(n, "day", e) #' -#' # with repulsive edge labels -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges() + -#' geom_edgetext_repel(aes(label = day), box.padding = unit(0.5, "lines")) + -#' geom_nodes(size = 4, colour = "grey50") + -#' theme_blank() -#' -#' # repulsive edge labels for only a subset of all edges -#' edge_day <- function(x) { x[ x$day > 2, ] } -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(aes(colour = cut(day, (4:0)[ -3 ]))) + -#' geom_edgetext_repel(aes(label = paste("day", day), -#' colour = cut(day, (4:0)[ -3 ])), data = edge_day) + -#' geom_nodes(size = 4, colour = "grey50") + -#' scale_colour_manual("day", labels = c("old ties", "day 3", "day 4"), -#' values = c("grey50", "gold", "tomato")) + -#' theme_blank() +#' # with repulsive edge labels +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges() + +#' geom_edgetext_repel(aes(label = day), box.padding = unit(0.5, "lines")) + +#' geom_nodes(size = 4, colour = "grey50") + +#' theme_blank() #' +#' # repulsive edge labels for only a subset of all edges +#' edge_day <- function(x) { +#' x[ x$day > 2, ] +#' } +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(aes(colour = cut(day, (4:0)[ -3 ]))) + +#' geom_edgetext_repel(aes( +#' label = paste("day", day), +#' colour = cut(day, (4:0)[ -3 ]) +#' ), data = edge_day) + +#' geom_nodes(size = 4, colour = "grey50") + +#' scale_colour_manual("day", +#' labels = c("old ties", "day 3", "day 4"), +#' values = c("grey50", "gold", "tomato") +#' ) + +#' theme_blank() #' } +#' #' @export geom_edgetext_repel <- function( mapping = NULL, data = NULL, @@ -249,7 +277,7 @@ geom_edgetext_repel <- function( show.legend = NA, inherit.aes = TRUE ) { - layer( + ggplot2::layer( data = data, mapping = mapping, stat = StatMidEdges, @@ -259,9 +287,9 @@ geom_edgetext_repel <- function( inherit.aes = inherit.aes, params = list( parse = parse, - box.padding = box.padding, + box.padding = box.padding, label.padding = label.padding, - point.padding = point.padding, + point.padding = point.padding, label.r = label.r, label.size = label.size, segment.colour = segment.colour, @@ -285,23 +313,19 @@ geom_edgelabel <- geom_edgetext #' @export geom_edgelabel_repel <- geom_edgetext_repel -#' @importFrom ggplot2 ggproto #' @keywords internal -StatEdges <- - ggplot2::ggproto("StatEdges", ggplot2::Stat, - compute_layer = function(data, scales, params) { - unique(subset(data, !(x == xend & y == yend))) - } - ) +StatEdges <- ggplot2::ggproto("StatEdges", ggplot2::Stat, + compute_layer = function(data, scales, params) { + unique(subset(data, !(x == xend & y == yend))) + } +) -#' @importFrom ggplot2 ggproto #' @keywords internal -StatMidEdges <- - ggplot2::ggproto("StatMidEdges", ggplot2::Stat, - compute_layer = function(data, scales, params) { - data = subset(data, !(x == xend & y == yend)) - data$x = (data$x + data$xend) / 2 - data$y = (data$y + data$yend) / 2 - unique(subset(data, select = c(-xend, -yend))) - } - ) +StatMidEdges <- ggplot2::ggproto("StatMidEdges", ggplot2::Stat, + compute_layer = function(data, scales, params) { + data <- subset(data, !(x == xend & y == yend)) + data$x <- (data$x + data$xend) / 2 + data$y <- (data$y + data$yend) / 2 + unique(subset(data, select = c(-xend, -yend))) + } +) diff --git a/R/geom-nodes.R b/R/geom-nodes.R index 1bdd3c0..6b0c41b 100644 --- a/R/geom-nodes.R +++ b/R/geom-nodes.R @@ -2,47 +2,52 @@ #' #' All arguments to this geom are identical to those of #' \code{\link[ggplot2]{geom_point}}. +#' #' @inheritParams ggplot2::geom_point -#' @importFrom ggplot2 layer GeomPoint +#' #' @examples #' if (require(network) && require(sna)) { +#' data(flo, package = "network") +#' n <- network(flo, directed = FALSE) #' -#' data(flo, package = "network") -#' n <- network(flo, directed = FALSE) -#' -#' # just nodes -#' ggplot(n, aes(x, y)) + -#' geom_nodes(size = 3, shape = 21, colour = "steelblue") + -#' theme_blank() +#' # just nodes +#' ggplot(n, aes(x, y)) + +#' geom_nodes(size = 3, shape = 21, colour = "steelblue") + +#' theme_blank() #' -#' # with edges -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodes(size = 3, shape = 21, colour = "steelblue", fill = "white") + -#' theme_blank() +#' # with edges +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodes(size = 3, shape = 21, colour = "steelblue", fill = "white") + +#' theme_blank() #' -#' # with nodes sized according to degree centrality -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodes(size = degree(n), shape = 21, colour = "steelblue", fill = "white") + -#' theme_blank() +#' # with nodes sized according to degree centrality +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodes(size = degree(n), shape = 21, colour = "steelblue", fill = "white") + +#' theme_blank() #' -#' # with nodes colored according to betweenness centrality -#' -#' n %v% "betweenness" <- betweenness(flo) -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "grey50") + -#' geom_nodes(aes(colour = betweenness), size = 3) + -#' scale_colour_gradient(low = "gold", high = "tomato") + -#' theme_blank() + -#' theme(legend.position = "bottom") +#' # with nodes colored according to betweenness centrality #' +#' n %v% "betweenness" <- betweenness(flo) +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "grey50") + +#' geom_nodes(aes(colour = betweenness), size = 3) + +#' scale_colour_gradient(low = "gold", high = "tomato") + +#' theme_blank() + +#' theme(legend.position = "bottom") #' } +#' #' @export -geom_nodes <- function(mapping = NULL, data = NULL, - position = "identity", na.rm = FALSE, - show.legend = NA, inherit.aes = TRUE, ...) { - +geom_nodes <- function( + mapping = NULL, + data = NULL, + position = "identity", + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE, + ... +) { ggplot2::layer( data = data, mapping = mapping, @@ -56,7 +61,6 @@ geom_nodes <- function(mapping = NULL, data = NULL, ... ) ) - } # note: the nudge_x,nudge_y arguments below are duplicated from the original @@ -66,57 +70,55 @@ geom_nodes <- function(mapping = NULL, data = NULL, #' #' All arguments to these geoms are identical to those of #' \code{\link[ggplot2]{geom_text}} and \code{\link[ggplot2]{geom_label}}. +#' #' @inheritParams ggplot2::geom_text #' @param nudge_x,nudge_y Horizontal and vertical adjustment to nudge labels by. #' Useful for offsetting text from points, particularly on discrete scales. -#' @importFrom ggplot2 position_nudge layer GeomText -#' @export +#' #' @examples #' ## geom_nodetext examples #' #' if (require(network) && require(sna)) { +#' n <- network(rgraph(10, tprob = 0.2), directed = FALSE) #' -#' n <- network(rgraph(10, tprob = 0.2), directed = FALSE) -#' -#' # just node labels -#' ggplot(n, aes(x, y)) + -#' geom_nodetext(aes(label = vertex.names)) + -#' theme_blank() -#' -#' # with nodes underneath -#' ggplot(n, aes(x, y)) + -#' geom_nodes(colour = "gold", size = 9) + -#' geom_nodetext(aes(label = vertex.names)) + -#' theme_blank() +#' # just node labels +#' ggplot(n, aes(x, y)) + +#' geom_nodetext(aes(label = vertex.names)) + +#' theme_blank() #' -#' # with nodes and edges -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "gold") + -#' geom_nodes(colour = "gold", size = 9) + -#' geom_nodetext(aes(label = vertex.names)) + -#' theme_blank() +#' # with nodes underneath +#' ggplot(n, aes(x, y)) + +#' geom_nodes(colour = "gold", size = 9) + +#' geom_nodetext(aes(label = vertex.names)) + +#' theme_blank() #' +#' # with nodes and edges +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "gold") + +#' geom_nodes(colour = "gold", size = 9) + +#' geom_nodetext(aes(label = vertex.names)) + +#' theme_blank() #' } -geom_nodetext <- function(mapping = NULL, - data = NULL, - position = "identity", - ..., - parse = FALSE, - nudge_x = 0, - nudge_y = 0, - check_overlap = FALSE, - na.rm = FALSE, - show.legend = NA, - inherit.aes = TRUE) { - +#' +#' @export +geom_nodetext <- function( + mapping = NULL, + data = NULL, + position = "identity", + ..., + parse = FALSE, + nudge_x = 0, + nudge_y = 0, + check_overlap = FALSE, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE +) { if (!missing(nudge_x) || !missing(nudge_y)) { - if (!missing(position)) { stop("Specify either `position` or `nudge_x`/`nudge_y`", call. = FALSE) } - position <- ggplot2::position_nudge(nudge_x, nudge_y) - } ggplot2::layer( @@ -134,7 +136,6 @@ geom_nodetext <- function(mapping = NULL, ... ) ) - } # note: the nudge_x,nudge_y arguments below are duplicated from the original @@ -145,126 +146,134 @@ geom_nodetext <- function(mapping = NULL, #' All arguments to these geoms are identical to those of #' \code{\link[ggrepel]{geom_text_repel}} and #' \code{\link[ggrepel]{geom_label_repel}}. +#' #' @inheritParams ggrepel::geom_text_repel #' @param nudge_x,nudge_y Horizontal and vertical adjustments to nudge the #' starting position of each text label. -#' @importFrom ggplot2 layer -#' @importFrom ggrepel GeomTextRepel +#' #' @examples #' ## geom_nodetext_repel example #' #' if (require(network) && require(sna)) { -#' -#' n <- network(rgraph(10, tprob = 0.2), directed = FALSE) -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodetext_repel(aes(label = paste("node", vertex.names)), -#' box.padding = unit(1, "lines")) + -#' geom_nodes(colour = "steelblue", size = 3) + -#' theme_blank() -#' +#' n <- network(rgraph(10, tprob = 0.2), directed = FALSE) +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodetext_repel(aes(label = paste("node", vertex.names)), +#' box.padding = unit(1, "lines") +#' ) + +#' geom_nodes(colour = "steelblue", size = 3) + +#' theme_blank() #' } +#' #' @export -geom_nodetext_repel <- function(mapping = NULL, - data = NULL, - # stat = "identity", - parse = FALSE, - ..., - box.padding = unit(0.25, "lines"), - point.padding = unit(1e-06, "lines"), - segment.colour = "#666666", - segment.size = 0.5, - arrow = NULL, - force = 1, - max.iter = 2000, - nudge_x = 0, - nudge_y = 0, - na.rm = FALSE, - show.legend = NA, - inherit.aes = TRUE) { - ggplot2::layer(data = data, - mapping = mapping, - stat = StatNodes, - geom = ggrepel::GeomTextRepel, - position = "identity", - show.legend = show.legend, - inherit.aes = inherit.aes, - params = list( - parse = parse, - na.rm = na.rm, - box.padding = box.padding, - point.padding = point.padding, - segment.colour = segment.colour, - segment.size = segment.size, - arrow = arrow, - force = force, - max.iter = max.iter, - nudge_x = nudge_x, - nudge_y = nudge_y, - ... - ) +geom_nodetext_repel <- function( + mapping = NULL, + data = NULL, + # stat = "identity", + parse = FALSE, + ..., + box.padding = unit(0.25, "lines"), + point.padding = unit(1e-06, "lines"), + segment.colour = "#666666", + segment.size = 0.5, + arrow = NULL, + force = 1, + max.iter = 2000, + nudge_x = 0, + nudge_y = 0, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE +) { + ggplot2::layer( + data = data, + mapping = mapping, + stat = StatNodes, + geom = ggrepel::GeomTextRepel, + position = "identity", + show.legend = show.legend, + inherit.aes = inherit.aes, + params = list( + parse = parse, + na.rm = na.rm, + box.padding = box.padding, + point.padding = point.padding, + segment.colour = segment.colour, + segment.size = segment.size, + arrow = arrow, + force = force, + max.iter = max.iter, + nudge_x = nudge_x, + nudge_y = nudge_y, + ... + ) ) - } #' @rdname geom_nodetext +#' #' @inheritParams ggplot2::geom_label -#' @importFrom ggplot2 position_nudge layer GeomLabel +#' #' @examples #' #' ## geom_nodelabel examples #' #' if (require(network) && require(sna)) { +#' data(flo, package = "network") +#' n <- network(flo, directed = FALSE) +#' +#' # with text labels +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "grey50") + +#' geom_nodelabel(aes(label = vertex.names)) + +#' theme_blank() +#' +#' # with text labels coloured according to degree centrality +#' n %v% "degree" <- degree(n) +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "grey50") + +#' geom_nodelabel(aes(label = vertex.names, fill = degree)) + +#' scale_fill_gradient(low = "gold", high = "tomato") + +#' theme_blank() #' -#' data(flo, package = "network") -#' n <- network(flo, directed = FALSE) -#' -#' # with text labels -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "grey50") + -#' geom_nodelabel(aes(label = vertex.names)) + -#' theme_blank() -#' -#' # with text labels coloured according to degree centrality -#' n %v% "degree" <- degree(n) -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "grey50") + -#' geom_nodelabel(aes(label = vertex.names, fill = degree)) + -#' scale_fill_gradient(low = "gold", high = "tomato") + -#' theme_blank() -#' -#' # label only a subset of all nodes -#' high_degree <- function(x) { x[ x$degree > median(x$degree), ] } -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodes(aes(size = degree), colour = "steelblue") + -#' geom_nodelabel(aes(label = vertex.names), data = high_degree, -#' colour = "white", fill = "tomato") + -#' theme_blank() +#' # label only a subset of all nodes +#' high_degree <- function(x) { +#' x[ x$degree > median(x$degree), ] +#' } +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodes(aes(size = degree), colour = "steelblue") + +#' geom_nodelabel(aes(label = vertex.names), +#' data = high_degree, +#' colour = "white", fill = "tomato" +#' ) + +#' theme_blank() #' } #' #' @export -geom_nodelabel <- function(mapping = NULL, data = NULL, - position = "identity", - ..., - parse = FALSE, - nudge_x = 0, - nudge_y = 0, - label.padding = unit(0.25, "lines"), - label.r = unit(0.15, "lines"), - label.size = 0.25, - na.rm = FALSE, - show.legend = NA, - inherit.aes = TRUE) { +geom_nodelabel <- function( + mapping = NULL, + data = NULL, + position = "identity", + ..., + parse = FALSE, + nudge_x = 0, + nudge_y = 0, + label.padding = unit(0.25, "lines"), + label.r = unit(0.15, "lines"), + label.size = 0.25, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE +) { if (!missing(nudge_x) || !missing(nudge_y)) { if (!missing(position)) { stop("Specify either `position` or `nudge_x`/`nudge_y`", call. = FALSE) } - position <- ggplot2::position_nudge(nudge_x, nudge_y) } - layer( + ggplot2::layer( data = data, mapping = mapping, stat = StatNodes, @@ -284,41 +293,45 @@ geom_nodelabel <- function(mapping = NULL, data = NULL, } #' @rdname geom_nodetext_repel +#' #' @inheritParams ggrepel::geom_label_repel -#' @importFrom ggplot2 layer -#' @importFrom ggrepel GeomLabelRepel +#' #' @examples #' ## geom_nodelabel_repel examples #' #' if (require(network) && require(sna)) { +#' data(flo, package = "network") +#' n <- network(flo, directed = FALSE) #' -#' data(flo, package = "network") -#' n <- network(flo, directed = FALSE) -#' -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodelabel_repel(aes(label = vertex.names), -#' box.padding = unit(1, "lines")) + -#' geom_nodes(colour = "steelblue", size = 3) + -#' theme_blank() -#' -#' # label only a subset of all nodes -#' n %v% "degree" <- degree(n) -#' low_degree <- function(x) { x[ x$degree < median(x$degree), ] } -#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + -#' geom_edges(colour = "steelblue") + -#' geom_nodelabel_repel(aes(label = vertex.names), -#' box.padding = unit(1.5, "lines"), -#' data = low_degree, -#' segment.colour = "tomato", -#' colour = "white", fill = "tomato") + -#' geom_nodes(aes(size = degree), colour = "steelblue") + -#' theme_blank() +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodelabel_repel(aes(label = vertex.names), +#' box.padding = unit(1, "lines") +#' ) + +#' geom_nodes(colour = "steelblue", size = 3) + +#' theme_blank() #' +#' # label only a subset of all nodes +#' n %v% "degree" <- degree(n) +#' low_degree <- function(x) { +#' x[ x$degree < median(x$degree), ] +#' } +#' ggplot(n, aes(x, y, xend = xend, yend = yend)) + +#' geom_edges(colour = "steelblue") + +#' geom_nodelabel_repel(aes(label = vertex.names), +#' box.padding = unit(1.5, "lines"), +#' data = low_degree, +#' segment.colour = "tomato", +#' colour = "white", fill = "tomato" +#' ) + +#' geom_nodes(aes(size = degree), colour = "steelblue") + +#' theme_blank() #' } +#' #' @export geom_nodelabel_repel <- function( - mapping = NULL, data = NULL, + mapping = NULL, + data = NULL, parse = FALSE, ..., box.padding = unit(0.25, "lines"), @@ -337,7 +350,7 @@ geom_nodelabel_repel <- function( show.legend = NA, inherit.aes = TRUE ) { - layer( + ggplot2::layer( data = data, mapping = mapping, stat = StatNodes, @@ -347,9 +360,9 @@ geom_nodelabel_repel <- function( inherit.aes = inherit.aes, params = list( parse = parse, - box.padding = box.padding, + box.padding = box.padding, label.padding = label.padding, - point.padding = point.padding, + point.padding = point.padding, label.r = label.r, label.size = label.size, segment.colour = segment.colour, @@ -365,15 +378,13 @@ geom_nodelabel_repel <- function( ) } -#' @importFrom ggplot2 ggproto #' @keywords internal -StatNodes <- - ggplot2::ggproto("StatNodes", ggplot2::Stat, - compute_layer = function(data, scales, params) { - if (all(c("xend", "yend") %in% names(data))) { - unique(subset(data, select = c(-xend, -yend))) - } else { - unique(data) - } - } - ) +StatNodes <- ggplot2::ggproto("StatNodes", ggplot2::Stat, + compute_layer = function(data, scales, params) { + if (all(c("xend", "yend") %in% names(data))) { + unique(subset(data, select = c(-xend, -yend))) + } else { + unique(data) + } + } +) diff --git a/R/ggnetwork.R b/R/ggnetwork.R index 169102d..b6feeb0 100644 --- a/R/ggnetwork.R +++ b/R/ggnetwork.R @@ -3,40 +3,29 @@ #' A wrapper for the \code{\link{fortify.network}} and #' \code{\link{fortify.igraph}} functions that will also try to coerce matrices #' and data frames to network objects. +#' #' @param x an object of class \code{\link[network]{network}}, or any object -#' that can be coerced to this class, such as an adjacency or incidence matrix, -#' or an edge list: see \code{\link[network]{edgeset.constructors}} and -#' \code{\link[network]{network}} for details. If the object is of class -#' \code{\link[igraph:igraph-package]{igraph}} and the -#' \code{\link[intergraph:intergraph-package]{intergraph}} package is installed, -#' it will be used to convert the object: see -#' \code{\link{fortify.igraph}} for details. -#' @param ... arguments passed to the \code{\link{fortify.network}} function. +#' that can be coerced to this class, such as an adjacency or incidence matrix, +#' or an edge list: see \code{\link[network]{edgeset.constructors}} and +#' \code{\link[network]{network}} for details. If the object is of class +#' \code{\link[igraph:igraph-package]{igraph}} and the +#' \code{\link[intergraph:intergraph-package]{intergraph}} package is installed, +#' it will be used to convert the object: see +#' \code{\link{fortify.igraph}} for details. +#' +#' @param ... arguments passed to the \code{\link{fortify.network}} or \code{\link{fortify.igraph}} function. +#' #' @export ggnetwork <- function(x, ...) { - - if (class(x) == "igraph") { - - fortify.igraph(x, ...) - - } else { - - if (!network::is.network(x)) { - - x = try(network::network(x), silent = TRUE) - - } - - if (!network::is.network(x)) { - - stop("could not coerce object to a network") - - } else { - - fortify.network(x, ...) - + tryCatch( + switch( + EXPR = class(x), + "igraph" = fortify.igraph(x, ...), + "network" = fortify.network(x, ...), + fortify.network(network::network(x), ...) + ), + error = function(e) { + stop('could not coerce object to a "network"') } - - } - + ) } diff --git a/R/utilities.R b/R/utilities.R index 2cb96da..a5d0e57 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -2,9 +2,11 @@ #' #' A \code{ggplot2} theme without lines, borders, axis text or titles, suited #' for plotting networks. +#' #' @param base_size base font size #' @param base_family base font family #' @param ... other \code{\link{theme}} arguments +#' #' @export theme_blank <- function(base_size = 12, base_family = "", ...) { ggplot2::theme_bw(base_size = base_size, base_family = base_family) + @@ -24,9 +26,11 @@ theme_blank <- function(base_size = 12, base_family = "", ...) { #' #' A variation of \code{\link{theme_blank}} that adds a panel border to the #' plot, which is often suitable for plotting faceted networks. +#' #' @param base_size base font size #' @param base_family base font family #' @param ... other \code{\link{theme}} arguments +#' #' @export theme_facet <- function(base_size = 12, base_family = "", ...) { theme_blank(base_size = base_size, base_family = base_family) + @@ -37,18 +41,42 @@ theme_facet <- function(base_size = 12, base_family = "", ...) { } #' Rescale x to (0, 1), except if x is constant -#' +#' #' Discussed in PR #32: https://github.com/briatte/ggnetwork/pull/32 #' @param x a vector to rescale #' @param scale the scale on which to rescale the vector +#' #' @return The rescaled vector, coerced to a vector if necessary. -#' If the original vector was constant, all of its values are replaced by 0.5. +#' If the original vector was constant, all of its values are replaced by 0.5. +#' #' @author Kipp Johnson scale_safely <- function(x, scale = diff(range(x))) { if (!scale) { x <- rep(0.5, length.out = length(x)) } else { - x <- base::scale(x, center = min(x), scale = scale) + x <- scale(x, center = min(x), scale = scale) } as.vector(x) } + +#' fortify generic +#' +#' See \code{ggplot2::\link[ggplot2]{fortify}} for details. +#' +#' @name fortify +#' @rdname fortify +#' @keywords internal +#' @export +#' @importFrom ggplot2 fortify +NULL + +#' unit +#' +#' See \code{ggplot2::\link[ggplot2]{unit}} for details. +#' +#' @name unit +#' @rdname unit +#' @keywords internal +#' @export +#' @importFrom ggplot2 unit +NULL diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 0000000..0d1c90c --- /dev/null +++ b/README.Rmd @@ -0,0 +1,78 @@ +--- +output: github_document +--- + + + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.path = "man/figures/README-", + out.width = "100%" +) +``` +# ggnetwork + + +[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://www.tidyverse.org/lifecycle/#maturing) +[![GitHub tag](https://img.shields.io/github/tag/briatte/ggnetwork.svg?label="latest tag")](https://github.com/briatte/ggnetwork) +[![Travis-CI Build Status](https://travis-ci.org/briatte/ggnetwork.svg?branch=master)](https://travis-ci.org/briatte/ggnetwork) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/briatte/ggnetwork?branch=master&svg=true)](https://ci.appveyor.com/project/briatte/ggnetwork) +[![Coverage Status (codecov)](https://codecov.io/gh/briatte/ggnetwork/branch/master/graph/badge.svg)](https://codecov.io/gh/briatte/ggnetwork) +[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version-ago/ggnetwork)](https://cran.r-project.org/package=ggnetwork) +[![cran checks_worst](https://cranchecks.info/badges/worst/ggnetwork)](https://cran.r-project.org/web/checks/check_results_ggnetwork.html) +[![CRAN_Download_total](http://cranlogs.r-pkg.org/badges/grand-total/ggnetwork)](https://cran.r-project.org/package=ggnetwork) + + +This package allows to pass network objects to [`ggplot2`](http://ggplot2.org/), along with several geometries to plot their elements. + +## Get started + +You can install the released version of ggnetwork from [CRAN](https://CRAN.R-project.org) with: + +``` r +install.packages("ggnetwork") +``` + +And the development version from [GitHub](https://github.com/) with: + +``` r +# install.packages("remotes") +remotes::install_github("briatte/ggnetwork") +``` + +__IMPORTANT: the `ggnetwork` package depends on `ggplot2` version 2.0.0 or above.__ + +__The package vignette__ contains [detailed examples](https://briatte.github.io/ggnetwork/) of how to use its fortify method and each of its geometries. + +__For further examples__ that use `ggnetwork` with other packages to produce animated graphs, see James Curley's slides on "[Interactive and Dynamic Network Visualization in R](http://curleylab.psych.columbia.edu/netviz/)." + +--- + +## Getting help + +If you encounter a clear bug, please file a minimal reproducible example on [github](https://github.com/briatte/ggnetwork/issues). +For questions and other discussion, please contact the package maintainer. + +## See also + +Related packages: + +* [geomnet](https://github.com/sctyner/geomnet) +* [ggnet](https://github.com/briatte/ggnet) +* [ggraph](https://github.com/thomasp85/ggraph) + +## Thanks + +```{r, include = FALSE, eval = FALSE} +usethis::use_tidy_thanks() +``` + +Thanks to two anonymous _[R Journal](https://journal.r-project.org/)_ reviewers, +[@achmurzy](https://github.com/achmurzy), [@andrewd789](https://github.com/andrewd789), [@ArtemSokolov](https://github.com/ArtemSokolov), [@aterhorst](https://github.com/aterhorst), [@briatte](https://github.com/briatte), [@emillykkejensen](https://github.com/emillykkejensen), [@evinhas](https://github.com/evinhas), [@FinScience](https://github.com/FinScience), [@ghost](https://github.com/ghost), [@instantkaffee](https://github.com/instantkaffee), [@jalapic](https://github.com/jalapic), [@jcfisher](https://github.com/jcfisher), [@jfaganUK](https://github.com/jfaganUK), [@kippjohnson](https://github.com/kippjohnson), [@koheiw](https://github.com/koheiw), [@mbojan](https://github.com/mbojan), [@mcanouil](https://github.com/mcanouil), [@mgagliol](https://github.com/mgagliol), [@mhairi](https://github.com/mhairi), [@minimaxir](https://github.com/minimaxir), [@mkarikom](https://github.com/mkarikom), [@nick-youngblut](https://github.com/nick-youngblut), [@SantiFilippo](https://github.com/SantiFilippo), [@sciabolazza](https://github.com/sciabolazza), [@sctyner](https://github.com/sctyner), [@trinker](https://github.com/trinker) and [@zachcp](https://github.com/zachcp) + + +--- +Please note that this project is released with a [Contributor Code of Conduct](.github/CODE_OF_CONDUCT.md). +By participating in this project you agree to abide by its terms. diff --git a/README.md b/README.md index 2b79784..ce7d5d6 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,108 @@ -## This package allows to pass network objects to [`ggplot2`](http://ggplot2.org/), along with several geometries to plot their elements. -__Install from CRAN__, or from GitHub with `devtools`: + -```r -# stable +# ggnetwork + + + +[![Lifecycle: +maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://www.tidyverse.org/lifecycle/#maturing) +[![GitHub +tag](https://img.shields.io/github/tag/briatte/ggnetwork.svg?label=%22latest%20tag%22)](https://github.com/briatte/ggnetwork) +[![Travis-CI Build +Status](https://travis-ci.org/briatte/ggnetwork.svg?branch=master)](https://travis-ci.org/briatte/ggnetwork) +[![AppVeyor Build +Status](https://ci.appveyor.com/api/projects/status/github/briatte/ggnetwork?branch=master&svg=true)](https://ci.appveyor.com/project/briatte/ggnetwork) +[![Coverage Status +(codecov)](https://codecov.io/gh/briatte/ggnetwork/branch/master/graph/badge.svg)](https://codecov.io/gh/briatte/ggnetwork) +[![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version-ago/ggnetwork)](https://cran.r-project.org/package=ggnetwork) +[![cran +checks\_worst](https://cranchecks.info/badges/worst/ggnetwork)](https://cran.r-project.org/web/checks/check_results_ggnetwork.html) +[![CRAN\_Download\_total](http://cranlogs.r-pkg.org/badges/grand-total/ggnetwork)](https://cran.r-project.org/package=ggnetwork) + + +This package allows to pass network objects to +[`ggplot2`](http://ggplot2.org/), along with several geometries to plot +their elements. + +## Get started + +You can install the released version of ggnetwork from +[CRAN](https://CRAN.R-project.org) with: + +``` r install.packages("ggnetwork") +``` -# dev -devtools::install_github("briatte/ggnetwork") +And the development version from [GitHub](https://github.com/) with: + +``` r +# install.packages("remotes") +remotes::install_github("briatte/ggnetwork") ``` -__IMPORTANT: the `ggnetwork` package depends on `ggplot2` version 2.0.0 or above.__ +**IMPORTANT: the `ggnetwork` package depends on `ggplot2` version 2.0.0 +or above.** + +**The package vignette** contains [detailed +examples](https://briatte.github.io/ggnetwork/) of how to use its +fortify method and each of its geometries. + +**For further examples** that use `ggnetwork` with other packages to +produce animated graphs, see James Curley’s slides on “[Interactive and +Dynamic Network Visualization in +R](http://curleylab.psych.columbia.edu/netviz/).” + +----- + +## Getting help + +If you encounter a clear bug, please file a minimal reproducible example +on [github](https://github.com/briatte/ggnetwork/issues). +For questions and other discussion, please contact the package +maintainer. -* * * +## See also -__The package vignette__ contains [detailed examples](https://briatte.github.io/ggnetwork/) of how to use its fortify method and each of its geometries. +Related packages: -__For further examples__ that use `ggnetwork` with other packages to produce animated graphs, see James Curley's slides on “[Interactive and Dynamic Network Visualization in R](http://curleylab.psych.columbia.edu/netviz/).” + - [geomnet](https://github.com/sctyner/geomnet) + - [ggnet](https://github.com/briatte/ggnet) + - [ggraph](https://github.com/thomasp85/ggraph) -* * * +## Thanks -__Comments and suggestions__ are very welcome: please file them as [issues](https://github.com/briatte/ggnetwork/issues). +Thanks to two anonymous *[R Journal](https://journal.r-project.org/)* +reviewers, [@achmurzy](https://github.com/achmurzy), +[@andrewd789](https://github.com/andrewd789), +[@ArtemSokolov](https://github.com/ArtemSokolov), +[@aterhorst](https://github.com/aterhorst), +[@briatte](https://github.com/briatte), +[@emillykkejensen](https://github.com/emillykkejensen), +[@evinhas](https://github.com/evinhas), +[@FinScience](https://github.com/FinScience), +[@ghost](https://github.com/ghost), +[@instantkaffee](https://github.com/instantkaffee), +[@jalapic](https://github.com/jalapic), +[@jcfisher](https://github.com/jcfisher), +[@jfaganUK](https://github.com/jfaganUK), +[@kippjohnson](https://github.com/kippjohnson), +[@koheiw](https://github.com/koheiw), +[@mbojan](https://github.com/mbojan), +[@mcanouil](https://github.com/mcanouil), +[@mgagliol](https://github.com/mgagliol), +[@mhairi](https://github.com/mhairi), +[@minimaxir](https://github.com/minimaxir), +[@mkarikom](https://github.com/mkarikom), +[@nick-youngblut](https://github.com/nick-youngblut), +[@SantiFilippo](https://github.com/SantiFilippo), +[@sciabolazza](https://github.com/sciabolazza), +[@sctyner](https://github.com/sctyner), +[@trinker](https://github.com/trinker) and +[@zachcp](https://github.com/zachcp) -__See also__ the [geomnet](https://github.com/sctyner/geomnet), [ggnet](https://github.com/briatte/ggnet) and [ggraph](https://github.com/thomasp85/ggraph) packages for related work. +----- -__Thanks to__ [Michał Bojanowski](https://github.com/mbojan), [Jake Fisher](https://github.com/jcfisher), [Heike Hofmann](https://github.com/heike), [Pedro Jordano](https://github.com/pedroj), [Moritz Marbach](https://github.com/sumtxt), [Tyler Rinker](https://github.com/trinker), Bertrand Sudre, [Samantha C. Tyner](https://github.com/sctyner), and two anonymous _[R Journal](https://journal.r-project.org/)_ reviewers. +Please note that this project is released with a [Contributor Code of +Conduct](.github/CODE_OF_CONDUCT.md). +By participating in this project you agree to abide by its terms. diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..76bd885 --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,7 @@ +template: + params: + bootswatch: simplex + +development: + mode: auto + diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..96c985e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,52 @@ +# DO NOT CHANGE the "init" and "install" sections below + +# Download script file from GitHub +init: + ps: | + $ErrorActionPreference = "Stop" + Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" + Import-Module '..\appveyor-tool.ps1' + +install: + ps: Bootstrap + +cache: + - C:\RLibrary + +# Adapt as necessary starting from here +version: {build}-{branch} + +environment: + global: + USE_RTOOLS: true + _R_CHECK_FORCE_SUGGESTS_: false + BIOC_USE_DEVEL: FALSE + +build_script: + - travis-tool.sh install_deps + +test_script: + - travis-tool.sh run_tests + +on_failure: + - 7z a failure.zip *.Rcheck\* + - appveyor PushArtifact failure.zip + +artifacts: + - path: '*.Rcheck\**\*.log' + name: Logs + + - path: '*.Rcheck\**\*.out' + name: Logs + + - path: '*.Rcheck\**\*.fail' + name: Logs + + - path: '*.Rcheck\**\*.Rout' + name: Logs + + - path: '\*_*.tar.gz' + name: Bits + + - path: '\*_*.zip' + name: Bit diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..69cb760 --- /dev/null +++ b/codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/ggnetwork.Rproj b/ggnetwork.Rproj new file mode 100755 index 0000000..3cc56ac --- /dev/null +++ b/ggnetwork.Rproj @@ -0,0 +1,23 @@ +Version: 1.0 + +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: No + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: knitr +LaTeX: pdfLaTeX + +StripTrailingWhitespace: Yes +LineEndingConversion: Posix + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace,vignette + +QuitChildProcessesOnExit: Yes diff --git a/inst/doc/ggnetwork.R b/inst/doc/ggnetwork.R deleted file mode 100644 index 32f1a37..0000000 --- a/inst/doc/ggnetwork.R +++ /dev/null @@ -1,182 +0,0 @@ -## ---- echo=FALSE, message=FALSE------------------------------------------ -library(ggplot2) -library(ggnetwork) - -## ---- eval=FALSE--------------------------------------------------------- -# install.packages("ggnetwork") - -## ---- eval=FALSE--------------------------------------------------------- -# devtools::install_github("briatte/ggnetwork") - -## ---- eval=FALSE--------------------------------------------------------- -# install.packages("ggplot2") -# library(ggplot2) - -## ---- message=FALSE------------------------------------------------------ -library(network) -library(sna) -n <- network(rgraph(10, tprob = 0.2), directed = FALSE) - -## ------------------------------------------------------------------------ -n %v% "family" <- sample(letters[1:3], 10, replace = TRUE) -n %v% "importance" <- sample(1:3, 10, replace = TRUE) - -## ------------------------------------------------------------------------ -e <- network.edgecount(n) -set.edge.attribute(n, "type", sample(letters[24:26], e, replace = TRUE)) -set.edge.attribute(n, "day", sample(1:3, e, replace = TRUE)) - -## ------------------------------------------------------------------------ -theme_blank - -## ---- eval=FALSE--------------------------------------------------------- -# ggnetwork(n, layout = "fruchtermanreingold", cell.jitter = 0.75) -# ggnetwork(n, layout = "target", niter = 100) - -## ------------------------------------------------------------------------ -head(ggnetwork(n)) - -## ------------------------------------------------------------------------ -tail(ggnetwork(n)) - -## ---- eval=FALSE--------------------------------------------------------- -# ggplot(n) - -## ---- eval=FALSE--------------------------------------------------------- -# ggplot(ggnetwork(n)) - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50", curvature = 0.1) + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - geom_nodes(aes(color = family, size = importance)) + - theme_blank() - -## ---- eval=FALSE--------------------------------------------------------- -# ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + -# geom_edges(aes(color = type)) + -# geom_nodes(aes(color = family)) + -# theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodes(color = "black", size = 8) + - geom_nodetext(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold") + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodelabel(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold") + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodelabel_repel(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold", box.padding = unit(1, "lines")) + - geom_nodes(color = "black", size = 8) + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey75") + - geom_nodes(color = "gold", size = 8) + - geom_nodetext(aes(label = LETTERS[ vertex.names ])) + - geom_edgetext(aes(label = day), color = "white", fill = "grey25") + - theme_minimal() + - theme(axis.text = element_blank(), - axis.title = element_blank(), - panel.background = element_rect(fill = "grey25"), - panel.grid = element_blank()) - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey75") + - geom_nodes(color = "gold", size = 8) + - geom_nodetext(aes(label = LETTERS[ vertex.names ])) + - geom_edgetext_repel(aes(label = day), color = "white", fill = "grey25", - box.padding = unit(1, "lines")) + - theme_minimal() + - theme(axis.text = element_blank(), - axis.title = element_blank(), - panel.background = element_rect(fill = "grey25"), - panel.grid = element_blank()) - -## ------------------------------------------------------------------------ -data(emon) -emon[[1]] - -## ---- echo=FALSE--------------------------------------------------------- -ggplot(emon[[1]], aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges() + - geom_nodes(color = "tomato", size = 4) + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(emon[[1]], aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed")) + - geom_nodes(color = "tomato", size = 4) + - theme_blank() - -## ---- eval=FALSE--------------------------------------------------------- -# ggnetwork(emon[[1]], weights = "Frequency") - -## ------------------------------------------------------------------------ -ggplot(ggnetwork(emon[[1]], arrow.gap = 0.04, by = "Frequency"), - aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed"), - aes(color = Sponsorship)) + - geom_nodes(aes(color = Sponsorship), size = 4) + - facet_wrap(~ Frequency) + - theme_facet() - -## ------------------------------------------------------------------------ -theme_facet - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) + - geom_nodetext(aes(label = LETTERS[ vertex.names ], size = 0.5 * importance)) + - geom_edgetext(aes(label = day), color = "grey25") + - scale_color_brewer(palette = "Set2") + - scale_size_area("importance", breaks = 1:3, max_size = 9) + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "grey50", alpha = 0.5) + - geom_nodes(aes(x, y, color = family, size = 5.5 * importance), alpha = 0.5) + - geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) + - scale_color_brewer(palette = "Set1") + - guides(size = FALSE) + - theme_blank() - -## ------------------------------------------------------------------------ -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "grey50", alpha = 0.5) + - geom_nodes(aes(x, y, color = family), size = 3) + - geom_nodelabel_repel(aes(label = vertex.names), - box.padding = unit(1, "lines"), - data = function(x) { x[ x$family == "a", ]}) + - scale_color_brewer(palette = "Set1") + - theme_blank() - -## ---- results='asis', echo=FALSE----------------------------------------- -cat("Last printed on ", gsub("\\s+", " ", format(Sys.time(), "%b %e, %Y")), - ", using ggnetwork version ", as.character(packageVersion("ggnetwork")), - ".", sep = "") - diff --git a/inst/doc/ggnetwork.Rmd b/inst/doc/ggnetwork.Rmd deleted file mode 100644 index 0501193..0000000 --- a/inst/doc/ggnetwork.Rmd +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: 'ggnetwork: Network geometries for ggplot2' -author: "François Briatte" -date: "`r Sys.Date()`" -output: - html_document: - highlight: default - toc: yes -vignette: > - %\VignetteIndexEntry{ggnetwork} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r, echo=FALSE, message=FALSE} -library(ggplot2) -library(ggnetwork) -``` - -> The [`ggnetwork`](https://github.com/briatte/ggnetwork) package provides a way to build network plots with [`ggplot2`](http://ggplot2.org/). - -Install the stable version from CRAN: - -```{r, eval=FALSE} -install.packages("ggnetwork") -``` - -Or use `devtools` to install the latest version of the package [from GitHub](https://github.com/briatte/ggnetwork): - -```{r, eval=FALSE} -devtools::install_github("briatte/ggnetwork") -``` - -The package is meant to be used with `ggplot2` version 2.0.0 or above, so make sure that you update your version of `ggplot2` from CRAN before using `ggnetwork`: - -```{r, eval=FALSE} -install.packages("ggplot2") -library(ggplot2) -``` - -`ggnetwork` further requires the `network` and `sna` packages for network manipulation, and will also install the [`ggrepel`](https://github.com/slowkow/ggrepel) package for repulsive label drawing. - -The `ggnetwork` package is very much related to the development of [`geom_net`](https://github.com/sctyner/ggnet) by [Samantha C. Tyner](https://github.com/sctyner) and [Heike Hoffmann](https://github.com/heike). It also shares some similarity to the [`ggnet` and `ggnet2` functions](https://github.com/briatte/ggnet), which are part of the [`GGally`](https://github.com/ggobi/ggally) package by Barret Schloerke and others. Each of these projects are extensions to Hadley Wickham's implementation of Leland Wilkinson's "grammar of graphics" in [ggplot2](http://ggplot2.org/). - -## Minimal example - -Let's define a small random graph to illustrate each component of `ggnetwork`: - -```{r, message=FALSE} -library(network) -library(sna) -n <- network(rgraph(10, tprob = 0.2), directed = FALSE) -``` - -Let's now add categorical and continuous attributes for both edges and vertices. We'll start with nodes, adding a categorical vertex attribute called `"family"`, which is set to either `"a"`, `"b"` or `"c"`, and a continuous vertex attribute called `"importance"`, which is set to either 1, 2 or 3. - -```{r} -n %v% "family" <- sample(letters[1:3], 10, replace = TRUE) -n %v% "importance" <- sample(1:3, 10, replace = TRUE) -``` - -We now add a categorical edge attribute called `"type"`, which is set to either `"x"`, `"y"` or `"z"`, and a continuous vertex attribute called `"day"`, which is set to either 1, 2 or 3. - -```{r} -e <- network.edgecount(n) -set.edge.attribute(n, "type", sample(letters[24:26], e, replace = TRUE)) -set.edge.attribute(n, "day", sample(1:3, e, replace = TRUE)) -``` - -Last, note that `ggnetwork` contains a "blank" plot theme that will avoid plotting axes on the sides of the network. We will use that theme in most of the plots: - -```{r} -theme_blank -``` - -## Main building blocks - -### `ggnetwork` - -The `ggnetwork` package is organised around a ‘workhorse’ function of the same name, which will ‘flatten’ the network object to a data frame that contains the edge list of the network, along with the edge attributes and the vertex attributes of the sender nodes. - -The network object referred to above might be an object of class `network`, or any data structure that can be coerced to it, such as an edge list, an adjacency matrix or an incidence matrix. If the `intergraph` package is installed, then objects of class `igraph` can also be used with the `ggnetwork` package. - -The data frame returned by `ggnetwork` also contains the coordinates needed for node placement as columns `"x"`, `"y"`, `"xend"` and `"yend"`, which as a consequence are "reserved" names in the context of `ggnetwork`. If these names show up in the edge or the vertex attributes, the function will simply fail to work. - -The default node placement algorithm used by `ggnetwork` to produce these coordinates is the Fruchterman-Reingold force-directed layout algorithm. All of the [placement algorithms implemented in the `sna` package](http://www.rdocumentation.org/packages/sna/functions/gplot.layout) are available through `ggnetwork`, which also accepts additional layout parameters: - -```{r, eval=FALSE} -ggnetwork(n, layout = "fruchtermanreingold", cell.jitter = 0.75) -ggnetwork(n, layout = "target", niter = 100) -``` - -The `layout` argument will also accept user-submitted coordinates as a two-column matrix with as many rows as the number of nodes in the network. - -The top of the data frame produced by `ggnetwork` contains self-loops to force every node to be included in the plot. This explains why the rows shown below have the same values in `"x"` and `"xend"` (and in `"y"` and `"yend"`), and only missing values in the columns corresponding to the edge attributes: - -```{r} -head(ggnetwork(n)) -``` - -The next rows of the data frame contain the actual edges: - -```{r} -tail(ggnetwork(n)) -``` - -The data frame returned by `ggnetwork` has (_N_ + _E_) rows, where _N_ is the number of nodes of the network, and _E_ its number of edges. This data format is very likely to include duplicate information about the nodes, which is unavoidable. - -Note that `ggnetwork` does not include any safety mechanism against duplicate column names. As a consequence, if there is both a vertex attribute called `"na"` and an edge attribute called `"na"`, as in the example above, then the vertex attribute will be renamed `"na.x"` and the edge attribute will be renamed `"na.y"`. - -### `fortify.network` and `fortify.igraph` - -The ‘flattening’ process described above is implemented by `ggnetwork` as `fortify` methods that are recognised by `ggplot2`. As a result, `ggplot2` will understand the following syntax as long as `n` is an object of class `network` or of class `igraph`: - -```{r, eval=FALSE} -ggplot(n) -``` - -However, if the object `n` is a matrix or an edge list to be coerced to a network object, you are required to use the `ggnetwork` function to pass the object to `ggplot2`: - -```{r, eval=FALSE} -ggplot(ggnetwork(n)) -``` - -### `geom_edges` - -Let's now draw the network edges using `geom_edges`, which is just a lightly hacked version of `geom_segment`. In the example below, we map the `type` edge attribute to the linetype of the network edges: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - theme_blank() -``` - -The other aesthetics that we mapped are the basic coordinates of the network plot. These might also be set as part of the call to `geom_segment`, but setting them at the root of the plot avoids having to repeat them in additional geoms. - -Note that `geom_edges` can also produce curved edges by setting its `curvature` argument to any value above `0` (the default): - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50", curvature = 0.1) + - theme_blank() -``` - -### `geom_nodes` - -Let's now draw the nodes using `geom_nodes`, which is just a lightly hacked version of `geom_point`. In the example below, we map the `family` vertex attribute to the color of the nodes, and make the size of these nodes proportional to the `importance` vertx attribute: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - geom_nodes(aes(color = family, size = importance)) + - theme_blank() -``` - -Because `ggplot2` follows Wilkinson's grammar of graphics, it accepts only one color scale. In the example above, that scale is mapped to a vertex attribute, but it could have also been mapped to an edge attribute. Mapping a color to _both_ a vertex attribute and an edge attribute will create a single color scale that incorrectly merges both attributes into one: - -```{r, eval=FALSE} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(color = type)) + - geom_nodes(aes(color = family)) + - theme_blank() -``` - -This is a limitation of `ggnetwork` that would require violating some fundamental aspects of the grammar of graphics to be circumvented. - -## More building blocks - -### `geom_nodetext` - -Let's now add node labels. These are simply plotted over the nodes by the `nodetext` geom, which works exactly like `geom_text`. In the example below, we map the `vertex.names` attribute (which contains numbers 1 to 10) to uppercase letters: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodes(color = "black", size = 8) + - geom_nodetext(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold") + - theme_blank() -``` - -### `geom_nodelabel` - -If you prefer to use the `geom_label` geom recently introduced in `ggplot2`, `ggnetwork` also supports these through the `nodelabel` geom: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodelabel(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold") + - theme_blank() -``` - -### `geom_nodetext_repel` and `geom_nodelabel_repel` - -`ggnetwork` supports the repulsive label functions introduced by the `ggrepel` package, which allows to label nodes with non-overlapping annotations. Simply add `_repel` to either `geom_nodetext` or `geom_nodelabel` to use that functionality: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "black") + - geom_nodelabel_repel(aes(color = family, label = LETTERS[ vertex.names ]), - fontface = "bold", box.padding = unit(1, "lines")) + - geom_nodes(color = "black", size = 8) + - theme_blank() -``` - -### `geom_edgetext` and `geom_edgelabel` - -Let's now add edge labels. These are plotted at mid-distance of the nodes that the edges connect by the `edgetext` geom, which works exactly like `geom_label`, except that its default arguments do not draw a border around the labels. Here's an example where we map the `day` edge attribute to edge labels: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey75") + - geom_nodes(color = "gold", size = 8) + - geom_nodetext(aes(label = LETTERS[ vertex.names ])) + - geom_edgetext(aes(label = day), color = "white", fill = "grey25") + - theme_minimal() + - theme(axis.text = element_blank(), - axis.title = element_blank(), - panel.background = element_rect(fill = "grey25"), - panel.grid = element_blank()) -``` - -The `edgelabel` geom is just an alias of the `edgetext` geom. Note that these geoms are unlikely to produce adequate results if the edges produced by `geom_edges` are curved. - -### `geom_edgetext_repel` and `geom_edgelabel_repel` - -As you would do with nodes, simply add `_repel` to either `geom_edgetext` or `geom_edgelabel` to draw repulsive edge labels: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey75") + - geom_nodes(color = "gold", size = 8) + - geom_nodetext(aes(label = LETTERS[ vertex.names ])) + - geom_edgetext_repel(aes(label = day), color = "white", fill = "grey25", - box.padding = unit(1, "lines")) + - theme_minimal() + - theme(axis.text = element_blank(), - axis.title = element_blank(), - panel.background = element_rect(fill = "grey25"), - panel.grid = element_blank()) -``` - -## More plotting parameters - -This section presents some rather experimental features of `ggnetwork`. - -### Edge arrows - -`ggnetwork` uses code by [Heike Hoffmann](https://github.com/heike) to better show arrows in directed graphs. To illustrate this, we will need a directed graph example, so let's use the first of the seven `emon` networks bundled in the `network` package: - -```{r} -data(emon) -emon[[1]] -``` - -If this network is passed to `ggnetwork` without any further plotting parameter, the result will feature "shortened" edges that do not reach their receiver nodes: - -```{r, echo=FALSE} -ggplot(emon[[1]], aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges() + - geom_nodes(color = "tomato", size = 4) + - theme_blank() -``` - -This is because directed networks are expected to be plotted with edge arrows indicating the directedness of each edge. Adding edge arrows with `geom_edges` works through the same call to the `arrow` function that is supported by `geom_segment`: - -```{r} -ggplot(emon[[1]], aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed")) + - geom_nodes(color = "tomato", size = 4) + - theme_blank() -``` - -The slightly shortened edges avoid overplotting the edge arrows and the nodes. The amount of "edge shortening" can be set through the `arrow.gap` parameter of `ggnetwork`, which defaults to `0` when the network is undirected and `0.025` when it is. This parameter might need adjustment depending on the size of the nodes, and it will probably not manage to avoid any overplotting when the size of the nodes is not constant. - -### Edge weights - -`ggnetwork` can use an edge attribute as edge weights when computing the network layout. The name of that edge attribute should be passed to the `weights` argument for that to happen, as in this example, which will produce different layouts than if `weights` had been left set to `NULL` (the default): - -```{r, eval=FALSE} -ggnetwork(emon[[1]], weights = "Frequency") -``` - -The Kamada-Kawai is one example of a network layout that supports edge weights. The user should refer to the documentation of each network layout to understand which of these can make use of edge weights. - -If `ggnetwork` finds duplicated edges in a network, it will return a warning, as these edges should probably have been converted to single weighted edges for adequate plotting. - -### Node faceting - -In order for `ggnetwork` to operate correctly with faceted plots, the `by` argument, which is `NULL` by default, can be set to the name of an edge attribute. The result will be a longer data frame that can be plotted with either `facet_wrap` or `facet_grid`, as in the example below, where the faceting variable, the `Frequency` edge attribute, has to be specified twice (once to `ggnetwork`, once to `facet_wrap`): - -```{r} -ggplot(ggnetwork(emon[[1]], arrow.gap = 0.04, by = "Frequency"), - aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed"), - aes(color = Sponsorship)) + - geom_nodes(aes(color = Sponsorship), size = 4) + - facet_wrap(~ Frequency) + - theme_facet() -``` - -The `by` argument is basically an attempt to bring minimal support for temporal networks in `ggnetwork`. It will systematically show all nodes in all plot facets, using the same coordinates in every facet. For more advanced plots of dynamic networks, the user should turn to the [`ndtv`](https://cran.r-project.org/package=ndtv) and [`tsna`](https://cran.r-project.org/package=tsna) packages. - -The example above also shows how to use a vertex attribute as part of the aesthetics of the edges. Given how `ggnetwork` operates, these vertex attributes will always be those of the sender node. - -Last, the example also shows that `ggnetwork` comes with a theme called `theme_facet`. This theme is a variation of the previously mentioned `theme_blank` that preserves its facet boxes: - -```{r} -theme_facet -``` - -## Additional methods - -Since `ggnetwork` works entirely through `ggplot2`, all `ggplot2` methods apply: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), color = "grey50") + - geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) + - geom_nodetext(aes(label = LETTERS[ vertex.names ], size = 0.5 * importance)) + - geom_edgetext(aes(label = day), color = "grey25") + - scale_color_brewer(palette = "Set2") + - scale_size_area("importance", breaks = 1:3, max_size = 9) + - theme_blank() -``` - -Similarly, it is possible to use any of the geometries more than once per plot: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "grey50", alpha = 0.5) + - geom_nodes(aes(x, y, color = family, size = 5.5 * importance), alpha = 0.5) + - geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) + - scale_color_brewer(palette = "Set1") + - guides(size = FALSE) + - theme_blank() -``` - -Last, all geoms provided by `ggnetwork` can be subsetted through the `data` argument, just as any `ggplot2` geom, and as in the example below, which draws only a subset of all node labels: - -```{r} -ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) + - geom_edges(color = "grey50", alpha = 0.5) + - geom_nodes(aes(x, y, color = family), size = 3) + - geom_nodelabel_repel(aes(label = vertex.names), - box.padding = unit(1, "lines"), - data = function(x) { x[ x$family == "a", ]}) + - scale_color_brewer(palette = "Set1") + - theme_blank() -``` - ---- - -```{r, results='asis', echo=FALSE} -cat("Last printed on ", gsub("\\s+", " ", format(Sys.time(), "%b %e, %Y")), - ", using ggnetwork version ", as.character(packageVersion("ggnetwork")), - ".", sep = "") -``` diff --git a/inst/doc/ggnetwork.html b/inst/doc/ggnetwork.html deleted file mode 100644 index 852b66b..0000000 --- a/inst/doc/ggnetwork.html +++ /dev/null @@ -1,460 +0,0 @@ - - - - - - - - - - - - - - - -ggnetwork: Network geometries for ggplot2 - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -
-

The ggnetwork package provides a way to build network plots with ggplot2.

-
-

Install the stable version from CRAN:

-
install.packages("ggnetwork")
-

Or use devtools to install the latest version of the package from GitHub:

-
devtools::install_github("briatte/ggnetwork")
-

The package is meant to be used with ggplot2 version 2.0.0 or above, so make sure that you update your version of ggplot2 from CRAN before using ggnetwork:

-
install.packages("ggplot2")
-library(ggplot2)
-

ggnetwork further requires the network and sna packages for network manipulation, and will also install the ggrepel package for repulsive label drawing.

-

The ggnetwork package is very much related to the development of geom_net by Samantha C. Tyner and Heike Hoffmann. It also shares some similarity to the ggnet and ggnet2 functions, which are part of the GGally package by Barret Schloerke and others. Each of these projects are extensions to Hadley Wickham’s implementation of Leland Wilkinson’s “grammar of graphics” in ggplot2.

-
-

Minimal example

-

Let’s define a small random graph to illustrate each component of ggnetwork:

-
library(network)
-library(sna)
-n <- network(rgraph(10, tprob = 0.2), directed = FALSE)
-

Let’s now add categorical and continuous attributes for both edges and vertices. We’ll start with nodes, adding a categorical vertex attribute called "family", which is set to either "a", "b" or "c", and a continuous vertex attribute called "importance", which is set to either 1, 2 or 3.

-
n %v% "family" <- sample(letters[1:3], 10, replace = TRUE)
-n %v% "importance" <- sample(1:3, 10, replace = TRUE)
-

We now add a categorical edge attribute called "type", which is set to either "x", "y" or "z", and a continuous vertex attribute called "day", which is set to either 1, 2 or 3.

-
e <- network.edgecount(n)
-set.edge.attribute(n, "type", sample(letters[24:26], e, replace = TRUE))
-set.edge.attribute(n, "day", sample(1:3, e, replace = TRUE))
-

Last, note that ggnetwork contains a “blank” plot theme that will avoid plotting axes on the sides of the network. We will use that theme in most of the plots:

-
theme_blank
-
## function(base_size = 12, base_family = "", ...) {
-##   ggplot2::theme_bw(base_size = base_size, base_family = base_family) +
-##     ggplot2::theme(
-##       axis.text = ggplot2::element_blank(),
-##       axis.ticks = ggplot2::element_blank(),
-##       axis.title = ggplot2::element_blank(),
-##       legend.key = ggplot2::element_blank(),
-##       panel.background = ggplot2::element_rect(fill = "white", colour = NA),
-##       panel.border = ggplot2::element_blank(),
-##       panel.grid = ggplot2::element_blank(),
-##       ...
-##     )
-## }
-## <environment: namespace:ggnetwork>
-
-
-

Main building blocks

-
-

ggnetwork

-

The ggnetwork package is organised around a ‘workhorse’ function of the same name, which will ‘flatten’ the network object to a data frame that contains the edge list of the network, along with the edge attributes and the vertex attributes of the sender nodes.

-

The network object referred to above might be an object of class network, or any data structure that can be coerced to it, such as an edge list, an adjacency matrix or an incidence matrix. If the intergraph package is installed, then objects of class igraph can also be used with the ggnetwork package.

-

The data frame returned by ggnetwork also contains the coordinates needed for node placement as columns "x", "y", "xend" and "yend", which as a consequence are “reserved” names in the context of ggnetwork. If these names show up in the edge or the vertex attributes, the function will simply fail to work.

-

The default node placement algorithm used by ggnetwork to produce these coordinates is the Fruchterman-Reingold force-directed layout algorithm. All of the placement algorithms implemented in the sna package are available through ggnetwork, which also accepts additional layout parameters:

-
ggnetwork(n, layout = "fruchtermanreingold", cell.jitter = 0.75)
-ggnetwork(n, layout = "target", niter = 100)
-

The layout argument will also accept user-submitted coordinates as a two-column matrix with as many rows as the number of nodes in the network.

-

The top of the data frame produced by ggnetwork contains self-loops to force every node to be included in the plot. This explains why the rows shown below have the same values in "x" and "xend" (and in "y" and "yend"), and only missing values in the columns corresponding to the edge attributes:

-
head(ggnetwork(n))
-
##           x          y family importance  na.x vertex.names      xend
-## 1 0.1830104 0.42047953      c          2 FALSE            1 0.1830104
-## 2 0.4690597 0.00000000      b          2 FALSE            2 0.4690597
-## 3 1.0000000 0.25891124      c          3 FALSE            3 1.0000000
-## 4 0.2814689 1.00000000      c          3 FALSE            4 0.2814689
-## 5 0.0000000 0.50312529      a          2 FALSE            5 0.0000000
-## 6 0.9306397 0.02183163      c          1 FALSE            6 0.9306397
-##         yend day na.y type
-## 1 0.42047953  NA   NA <NA>
-## 2 0.00000000  NA   NA <NA>
-## 3 0.25891124  NA   NA <NA>
-## 4 1.00000000  NA   NA <NA>
-## 5 0.50312529  NA   NA <NA>
-## 6 0.02183163  NA   NA <NA>
-

The next rows of the data frame contain the actual edges:

-
tail(ggnetwork(n))
-
##            x         y family importance  na.x vertex.names      xend
-## 18 0.8748765 0.2445744      a          2 FALSE            5 0.4203488
-## 19 0.8748765 0.2445744      a          2 FALSE            5 0.4989169
-## 20 1.0000000 0.7239847      c          2 FALSE           10 0.8748765
-## 21 1.0000000 0.7239847      c          2 FALSE           10 0.7160749
-## 22 1.0000000 0.7239847      c          2 FALSE           10 0.7091942
-## 23 1.0000000 0.7239847      c          2 FALSE           10 0.4939615
-##         yend day  na.y type
-## 18 0.5530424   2 FALSE    z
-## 19 0.0000000   2 FALSE    y
-## 20 0.2445744   2 FALSE    x
-## 21 0.6762465   3 FALSE    y
-## 22 0.4029760   1 FALSE    z
-## 23 0.8738652   1 FALSE    x
-

The data frame returned by ggnetwork has (N + E) rows, where N is the number of nodes of the network, and E its number of edges. This data format is very likely to include duplicate information about the nodes, which is unavoidable.

-

Note that ggnetwork does not include any safety mechanism against duplicate column names. As a consequence, if there is both a vertex attribute called "na" and an edge attribute called "na", as in the example above, then the vertex attribute will be renamed "na.x" and the edge attribute will be renamed "na.y".

-
-
-

fortify.network and fortify.igraph

-

The ‘flattening’ process described above is implemented by ggnetwork as fortify methods that are recognised by ggplot2. As a result, ggplot2 will understand the following syntax as long as n is an object of class network or of class igraph:

-
ggplot(n)
-

However, if the object n is a matrix or an edge list to be coerced to a network object, you are required to use the ggnetwork function to pass the object to ggplot2:

-
ggplot(ggnetwork(n))
-
-
-

geom_edges

-

Let’s now draw the network edges using geom_edges, which is just a lightly hacked version of geom_segment. In the example below, we map the type edge attribute to the linetype of the network edges:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey50") +
-  theme_blank()
-

-

The other aesthetics that we mapped are the basic coordinates of the network plot. These might also be set as part of the call to geom_segment, but setting them at the root of the plot avoids having to repeat them in additional geoms.

-

Note that geom_edges can also produce curved edges by setting its curvature argument to any value above 0 (the default):

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey50", curvature = 0.1) +
-  theme_blank()
-

-
-
-

geom_nodes

-

Let’s now draw the nodes using geom_nodes, which is just a lightly hacked version of geom_point. In the example below, we map the family vertex attribute to the color of the nodes, and make the size of these nodes proportional to the importance vertx attribute:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey50") +
-  geom_nodes(aes(color = family, size = importance)) +
-  theme_blank()
-

-

Because ggplot2 follows Wilkinson’s grammar of graphics, it accepts only one color scale. In the example above, that scale is mapped to a vertex attribute, but it could have also been mapped to an edge attribute. Mapping a color to both a vertex attribute and an edge attribute will create a single color scale that incorrectly merges both attributes into one:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(color = type)) +
-  geom_nodes(aes(color = family)) +
-  theme_blank()
-

This is a limitation of ggnetwork that would require violating some fundamental aspects of the grammar of graphics to be circumvented.

-
-
-
-

More building blocks

-
-

geom_nodetext

-

Let’s now add node labels. These are simply plotted over the nodes by the nodetext geom, which works exactly like geom_text. In the example below, we map the vertex.names attribute (which contains numbers 1 to 10) to uppercase letters:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(color = "black") +
-  geom_nodes(color = "black", size = 8) +
-  geom_nodetext(aes(color = family, label = LETTERS[ vertex.names ]),
-                fontface = "bold") +
-  theme_blank()
-

-
-
-

geom_nodelabel

-

If you prefer to use the geom_label geom recently introduced in ggplot2, ggnetwork also supports these through the nodelabel geom:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(color = "black") +
-  geom_nodelabel(aes(color = family, label = LETTERS[ vertex.names ]),
-                 fontface = "bold") +
-  theme_blank()
-

-
-
-

geom_nodetext_repel and geom_nodelabel_repel

-

ggnetwork supports the repulsive label functions introduced by the ggrepel package, which allows to label nodes with non-overlapping annotations. Simply add _repel to either geom_nodetext or geom_nodelabel to use that functionality:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(color = "black") +
-  geom_nodelabel_repel(aes(color = family, label = LETTERS[ vertex.names ]),
-                       fontface = "bold", box.padding = unit(1, "lines")) +
-  geom_nodes(color = "black", size = 8) +
-  theme_blank()
-

-
-
-

geom_edgetext and geom_edgelabel

-

Let’s now add edge labels. These are plotted at mid-distance of the nodes that the edges connect by the edgetext geom, which works exactly like geom_label, except that its default arguments do not draw a border around the labels. Here’s an example where we map the day edge attribute to edge labels:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey75") +
-  geom_nodes(color = "gold", size = 8) +
-  geom_nodetext(aes(label = LETTERS[ vertex.names ])) +
-  geom_edgetext(aes(label = day), color = "white", fill = "grey25") +
-  theme_minimal() +
-  theme(axis.text = element_blank(),
-        axis.title = element_blank(),
-        panel.background = element_rect(fill = "grey25"),
-        panel.grid = element_blank())
-

-

The edgelabel geom is just an alias of the edgetext geom. Note that these geoms are unlikely to produce adequate results if the edges produced by geom_edges are curved.

-
-
-

geom_edgetext_repel and geom_edgelabel_repel

-

As you would do with nodes, simply add _repel to either geom_edgetext or geom_edgelabel to draw repulsive edge labels:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey75") +
-  geom_nodes(color = "gold", size = 8) +
-  geom_nodetext(aes(label = LETTERS[ vertex.names ])) +
-  geom_edgetext_repel(aes(label = day), color = "white", fill = "grey25",
-                      box.padding = unit(1, "lines")) +
-  theme_minimal() +
-  theme(axis.text = element_blank(),
-        axis.title = element_blank(),
-        panel.background = element_rect(fill = "grey25"),
-        panel.grid = element_blank())
-

-
-
-
-

More plotting parameters

-

This section presents some rather experimental features of ggnetwork.

-
-

Edge arrows

-

ggnetwork uses code by Heike Hoffmann to better show arrows in directed graphs. To illustrate this, we will need a directed graph example, so let’s use the first of the seven emon networks bundled in the network package:

-
data(emon)
-emon[[1]]
-
##  Network attributes:
-##   vertices = 14 
-##   directed = TRUE 
-##   hyper = FALSE 
-##   loops = FALSE 
-##   multiple = FALSE 
-##   total edges= 83 
-##     missing edges= 0 
-##     non-missing edges= 83 
-## 
-##  Vertex attribute names: 
-##     Command.Rank.Score Decision.Rank.Score Formalization Location Paid.Staff Sponsorship vertex.names Volunteer.Staff 
-## 
-##  Edge attribute names: 
-##     Frequency
-

If this network is passed to ggnetwork without any further plotting parameter, the result will feature “shortened” edges that do not reach their receiver nodes:

-

-

This is because directed networks are expected to be plotted with edge arrows indicating the directedness of each edge. Adding edge arrows with geom_edges works through the same call to the arrow function that is supported by geom_segment:

-
ggplot(emon[[1]], aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed")) +
-  geom_nodes(color = "tomato", size = 4) +
-  theme_blank()
-

-

The slightly shortened edges avoid overplotting the edge arrows and the nodes. The amount of “edge shortening” can be set through the arrow.gap parameter of ggnetwork, which defaults to 0 when the network is undirected and 0.025 when it is. This parameter might need adjustment depending on the size of the nodes, and it will probably not manage to avoid any overplotting when the size of the nodes is not constant.

-
-
-

Edge weights

-

ggnetwork can use an edge attribute as edge weights when computing the network layout. The name of that edge attribute should be passed to the weights argument for that to happen, as in this example, which will produce different layouts than if weights had been left set to NULL (the default):

-
ggnetwork(emon[[1]], weights = "Frequency")
-

The Kamada-Kawai is one example of a network layout that supports edge weights. The user should refer to the documentation of each network layout to understand which of these can make use of edge weights.

-

If ggnetwork finds duplicated edges in a network, it will return a warning, as these edges should probably have been converted to single weighted edges for adequate plotting.

-
-
-

Node faceting

-

In order for ggnetwork to operate correctly with faceted plots, the by argument, which is NULL by default, can be set to the name of an edge attribute. The result will be a longer data frame that can be plotted with either facet_wrap or facet_grid, as in the example below, where the faceting variable, the Frequency edge attribute, has to be specified twice (once to ggnetwork, once to facet_wrap):

-
ggplot(ggnetwork(emon[[1]], arrow.gap = 0.04, by = "Frequency"),
-       aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(arrow = arrow(length = unit(6, "pt"), type = "closed"),
-             aes(color = Sponsorship)) +
-  geom_nodes(aes(color = Sponsorship), size = 4) +
-  facet_wrap(~ Frequency) +
-  theme_facet()
-

-

The by argument is basically an attempt to bring minimal support for temporal networks in ggnetwork. It will systematically show all nodes in all plot facets, using the same coordinates in every facet. For more advanced plots of dynamic networks, the user should turn to the ndtv and tsna packages.

-

The example above also shows how to use a vertex attribute as part of the aesthetics of the edges. Given how ggnetwork operates, these vertex attributes will always be those of the sender node.

-

Last, the example also shows that ggnetwork comes with a theme called theme_facet. This theme is a variation of the previously mentioned theme_blank that preserves its facet boxes:

-
theme_facet
-
## function(base_size = 12, base_family = "", ...) {
-##   theme_blank(base_size = base_size, base_family = base_family) +
-##     ggplot2::theme(
-##       panel.border = ggplot2::element_rect(fill = NA, color = "grey50"),
-##       ...
-##     )
-## }
-## <environment: namespace:ggnetwork>
-
-
-
-

Additional methods

-

Since ggnetwork works entirely through ggplot2, all ggplot2 methods apply:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(aes(linetype = type), color = "grey50") +
-  geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) +
-  geom_nodetext(aes(label = LETTERS[ vertex.names ], size = 0.5 * importance)) +
-  geom_edgetext(aes(label = day), color = "grey25") +
-  scale_color_brewer(palette = "Set2") +
-  scale_size_area("importance", breaks = 1:3, max_size = 9) +
-  theme_blank()
-

-

Similarly, it is possible to use any of the geometries more than once per plot:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(color = "grey50", alpha = 0.5) +
-  geom_nodes(aes(x, y, color = family, size = 5.5 * importance), alpha = 0.5) +
-  geom_nodes(aes(x, y, color = family, size = 1.5 * importance)) +
-  scale_color_brewer(palette = "Set1") +
-  guides(size = FALSE) +
-  theme_blank()
-

-

Last, all geoms provided by ggnetwork can be subsetted through the data argument, just as any ggplot2 geom, and as in the example below, which draws only a subset of all node labels:

-
ggplot(n, aes(x = x, y = y, xend = xend, yend = yend)) +
-  geom_edges(color = "grey50", alpha = 0.5) +
-  geom_nodes(aes(x, y, color = family), size = 3) +
-  geom_nodelabel_repel(aes(label = vertex.names),
-                       box.padding = unit(1, "lines"),
-                       data = function(x) { x[ x$family == "a", ]}) +
-  scale_color_brewer(palette = "Set1") +
-  theme_blank()
-

-
-

Last printed on Mar 25, 2016, using ggnetwork version 0.4.

-
- - - - -
- - - - - - - - diff --git a/man/fortify.Rd b/man/fortify.Rd new file mode 100644 index 0000000..e07fce8 --- /dev/null +++ b/man/fortify.Rd @@ -0,0 +1,9 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities.R +\name{fortify} +\alias{fortify} +\title{fortify generic} +\description{ +See \code{ggplot2::\link[ggplot2]{fortify}} for details. +} +\keyword{internal} diff --git a/man/fortify.igraph.Rd b/man/fortify.igraph.Rd index b6c8bb9..07d0768 100644 --- a/man/fortify.igraph.Rd +++ b/man/fortify.igraph.Rd @@ -5,15 +5,16 @@ \title{Fortify method for networks of class \code{\link[igraph:igraph-package]{igraph}}} \usage{ \method{fortify}{igraph}(model, data = NULL, layout = igraph::nicely(), - arrow.gap = ifelse(igraph::is.directed(x), 0.025, 0), by = NULL, ...) + arrow.gap = ifelse(igraph::is.directed(model), 0.025, 0), by = NULL, + ...) } \arguments{ \item{model}{an object of class \code{\link[igraph:igraph-package]{igraph}}} \item{data}{not used by this method.} -\item{layout}{a function call to an -\code{\link[igraph:igraph-package]{igraph}} layout function, such as +\item{layout}{a function call to an +\code{\link[igraph:igraph-package]{igraph}} layout function, such as \code{\link[igraph]{layout_nicely}} (the default), or a 2 column matrix giving the x and y coordinates for the vertices. See \code{\link[igraph]{layout_}} for details.} @@ -27,8 +28,7 @@ good results when the size of the nodes is reasonably small.} \item{by}{a character vector that matches an edge attribute, which will be used to generate a data frame that can be plotted with} -\item{...}{additional parameters for the \code{\link[igraph]{layout_}} -function} +\item{...}{additional parameters for the \code{\link[igraph]{layout_}} function} } \description{ Fortify method for networks of class \code{\link[igraph:igraph-package]{igraph}} diff --git a/man/fortify.network.Rd b/man/fortify.network.Rd index 23f703d..2355aa4 100644 --- a/man/fortify.network.Rd +++ b/man/fortify.network.Rd @@ -80,39 +80,50 @@ if (require(ggplot2) && require(network)) { ggnetwork(emon[[1]], layout = "kamadakawai", weights = "Frequency") # plot example with straight edges - ggplot(ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), - aes(x, y, xend = xend, yend = yend)) + + ggplot( + ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), + aes(x, y, xend = xend, yend = yend) + ) + geom_edges(aes(color = Frequency), - arrow = arrow(length = unit(10, "pt"), type = "closed")) + + arrow = arrow(length = unit(10, "pt"), type = "closed") + ) + geom_nodes(aes(size = Formalization)) + scale_color_gradient(low = "grey50", high = "tomato") + scale_size_area(breaks = 1:3) + theme_blank() # plot example with curved edges - ggplot(ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), - aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(color = Frequency), curvature = 0.1, - arrow = arrow(length = unit(10, "pt"), type = "open")) + + ggplot( + ggnetwork(emon[[1]], layout = "kamadakawai", arrow.gap = 0.025), + aes(x, y, xend = xend, yend = yend) + ) + + geom_edges(aes(color = Frequency), + curvature = 0.1, + arrow = arrow(length = unit(10, "pt"), type = "open") + ) + geom_nodes(aes(size = Formalization)) + scale_color_gradient(low = "grey50", high = "tomato") + scale_size_area(breaks = 1:3) + theme_blank() # facet by edge attribute - ggplot(ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency"), - aes(x, y, xend = xend, yend = yend)) + + ggplot( + ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency"), + aes(x, y, xend = xend, yend = yend) + ) + geom_edges(arrow = arrow(length = unit(5, "pt"), type = "closed")) + geom_nodes() + theme_blank() + facet_grid(. ~ Frequency, labeller = label_both) # user-provided layout - ggplot(ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)), - aes(x, y, xend = xend, yend = yend)) + + ggplot( + ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)), + aes(x, y, xend = xend, yend = yend) + ) + geom_edges(arrow = arrow(length = unit(5, "pt"), type = "closed")) + geom_nodes() + theme_blank() - } + } diff --git a/man/geom_edges.Rd b/man/geom_edges.Rd index fc4c5c8..6003956 100644 --- a/man/geom_edges.Rd +++ b/man/geom_edges.Rd @@ -78,60 +78,70 @@ edges produced by \code{geom_edges} will be curved. \examples{ if (require(network) && require(sna)) { -# rerun if the example does not produce reciprocated ties -n <- network(rgraph(10, tprob = 0.2), directed = TRUE) - -# just edges -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(size = 1, colour = "steelblue") + - theme_blank() - -# with nodes -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(size = 1, colour = "steelblue") + - geom_nodes(size = 3, colour = "steelblue") + - theme_blank() - -# with arrows -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(size = 1, colour = "steelblue", - arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + - geom_nodes(size = 3, colour = "steelblue") + - theme_blank() - -# with curvature -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(size = 1, colour = "steelblue", curvature = 0.15, - arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + - geom_nodes(size = 3, colour = "steelblue") + - theme_blank() - -# arbitrary categorical edge attribute -e <- sample(letters[ 1:2 ], network.edgecount(n), replace = TRUE) -set.edge.attribute(n, "type", e) -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(linetype = type), size = 1, curvature = 0.15, - arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + - geom_nodes(size = 3, colour = "steelblue") + - theme_blank() - -# arbitrary numeric edge attribute (signed network) -e <- sample(-2:2, network.edgecount(n), replace = TRUE) -set.edge.attribute(n, "weight", e) -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(colour = weight), curvature = 0.15, - arrow = arrow(length = unit(0.5, "lines"), type = "closed")) + - geom_nodes(size = 3, colour = "grey50") + - scale_colour_gradient(low = "steelblue", high = "tomato") + - theme_blank() - -# draw only a subset of all edges -positive_weight <- function(x) { x[ x$weight >= 0, ] } -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(colour = weight), data = positive_weight) + - geom_nodes(size = 4, colour = "grey50") + - scale_colour_gradient(low = "gold", high = "tomato") + - theme_blank() - + # rerun if the example does not produce reciprocated ties + n <- network(rgraph(10, tprob = 0.2), directed = TRUE) + + # just edges + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(size = 1, colour = "steelblue") + + theme_blank() + + # with nodes + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(size = 1, colour = "steelblue") + + geom_nodes(size = 3, colour = "steelblue") + + theme_blank() + + # with arrows + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges( + size = 1, colour = "steelblue", + arrow = arrow(length = unit(0.5, "lines"), type = "closed") + ) + + geom_nodes(size = 3, colour = "steelblue") + + theme_blank() + + # with curvature + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges( + size = 1, colour = "steelblue", curvature = 0.15, + arrow = arrow(length = unit(0.5, "lines"), type = "closed") + ) + + geom_nodes(size = 3, colour = "steelblue") + + theme_blank() + + # arbitrary categorical edge attribute + e <- sample(letters[ 1:2 ], network.edgecount(n), replace = TRUE) + set.edge.attribute(n, "type", e) + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(aes(linetype = type), + size = 1, curvature = 0.15, + arrow = arrow(length = unit(0.5, "lines"), type = "closed") + ) + + geom_nodes(size = 3, colour = "steelblue") + + theme_blank() + + # arbitrary numeric edge attribute (signed network) + e <- sample(-2:2, network.edgecount(n), replace = TRUE) + set.edge.attribute(n, "weight", e) + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(aes(colour = weight), + curvature = 0.15, + arrow = arrow(length = unit(0.5, "lines"), type = "closed") + ) + + geom_nodes(size = 3, colour = "grey50") + + scale_colour_gradient(low = "steelblue", high = "tomato") + + theme_blank() + + # draw only a subset of all edges + positive_weight <- function(x) { + x[ x$weight >= 0, ] + } + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(aes(colour = weight), data = positive_weight) + + geom_nodes(size = 4, colour = "grey50") + + scale_colour_gradient(low = "gold", high = "tomato") + + theme_blank() } + } diff --git a/man/geom_edgetext.Rd b/man/geom_edgetext.Rd index b183729..ad54032 100644 --- a/man/geom_edgetext.Rd +++ b/man/geom_edgetext.Rd @@ -7,14 +7,14 @@ \usage{ geom_edgetext(mapping = NULL, data = NULL, position = "identity", parse = FALSE, ..., nudge_x = 0, nudge_y = 0, - label.padding = unit(0.25, "lines"), label.r = ggplot2::unit(0.15, - "lines"), label.size = 0, na.rm = FALSE, show.legend = NA, + label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), + label.size = 0, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) geom_edgelabel(mapping = NULL, data = NULL, position = "identity", parse = FALSE, ..., nudge_x = 0, nudge_y = 0, - label.padding = unit(0.25, "lines"), label.r = ggplot2::unit(0.15, - "lines"), label.size = 0, na.rm = FALSE, show.legend = NA, + label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), + label.size = 0, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) } \arguments{ @@ -83,28 +83,29 @@ identical results. } \examples{ if (require(network) && require(sna)) { - -data(flo, package = "network") -n <- network(flo, directed = FALSE) - -# arbitrary categorical edge attribute -e <- sample(letters[ 1:4 ], network.edgecount(n), replace = TRUE) -set.edge.attribute(n, "type", e) - -# with labelled edges -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(colour = type)) + - geom_edgetext(aes(label = type, colour = type)) + - geom_nodes(size = 4, colour = "grey50") + - theme_blank() - -# label only a subset of all edges with arbitrary symbol -edge_type <- function(x) { x[ x$type == "a", ] } -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges() + - geom_edgetext(label = "=", data = edge_type) + - geom_nodes(size = 4, colour = "grey50") + - theme_blank() - + data(flo, package = "network") + n <- network(flo, directed = FALSE) + + # arbitrary categorical edge attribute + e <- sample(letters[ 1:4 ], network.edgecount(n), replace = TRUE) + set.edge.attribute(n, "type", e) + + # with labelled edges + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(aes(colour = type)) + + geom_edgetext(aes(label = type, colour = type)) + + geom_nodes(size = 4, colour = "grey50") + + theme_blank() + + # label only a subset of all edges with arbitrary symbol + edge_type <- function(x) { + x[ x$type == "a", ] + } + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges() + + geom_edgetext(label = "=", data = edge_type) + + geom_nodes(size = 4, colour = "grey50") + + theme_blank() } + } diff --git a/man/geom_edgetext_repel.Rd b/man/geom_edgetext_repel.Rd index 68de4e3..051539f 100644 --- a/man/geom_edgetext_repel.Rd +++ b/man/geom_edgetext_repel.Rd @@ -101,31 +101,36 @@ All arguments to both \code{\link{geom_edgetext_repel}} and } \examples{ if (require(network) && require(sna)) { - -data(flo, package = "network") -n <- network(flo, directed = FALSE) - -# arbitrary categorical edge attribute -e <- sample(1:4, network.edgecount(n), replace = TRUE) -set.edge.attribute(n, "day", e) - -# with repulsive edge labels -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges() + - geom_edgetext_repel(aes(label = day), box.padding = unit(0.5, "lines")) + - geom_nodes(size = 4, colour = "grey50") + - theme_blank() - -# repulsive edge labels for only a subset of all edges -edge_day <- function(x) { x[ x$day > 2, ] } -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(aes(colour = cut(day, (4:0)[ -3 ]))) + - geom_edgetext_repel(aes(label = paste("day", day), - colour = cut(day, (4:0)[ -3 ])), data = edge_day) + - geom_nodes(size = 4, colour = "grey50") + - scale_colour_manual("day", labels = c("old ties", "day 3", "day 4"), - values = c("grey50", "gold", "tomato")) + - theme_blank() - + data(flo, package = "network") + n <- network(flo, directed = FALSE) + + # arbitrary categorical edge attribute + e <- sample(1:4, network.edgecount(n), replace = TRUE) + set.edge.attribute(n, "day", e) + + # with repulsive edge labels + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges() + + geom_edgetext_repel(aes(label = day), box.padding = unit(0.5, "lines")) + + geom_nodes(size = 4, colour = "grey50") + + theme_blank() + + # repulsive edge labels for only a subset of all edges + edge_day <- function(x) { + x[ x$day > 2, ] + } + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(aes(colour = cut(day, (4:0)[ -3 ]))) + + geom_edgetext_repel(aes( + label = paste("day", day), + colour = cut(day, (4:0)[ -3 ]) + ), data = edge_day) + + geom_nodes(size = 4, colour = "grey50") + + scale_colour_manual("day", + labels = c("old ties", "day 3", "day 4"), + values = c("grey50", "gold", "tomato") + ) + + theme_blank() } + } diff --git a/man/geom_nodes.Rd b/man/geom_nodes.Rd index 0c4e0b7..5c3dff9 100644 --- a/man/geom_nodes.Rd +++ b/man/geom_nodes.Rd @@ -56,36 +56,35 @@ All arguments to this geom are identical to those of } \examples{ if (require(network) && require(sna)) { - -data(flo, package = "network") -n <- network(flo, directed = FALSE) - -# just nodes -ggplot(n, aes(x, y)) + - geom_nodes(size = 3, shape = 21, colour = "steelblue") + - theme_blank() - -# with edges -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodes(size = 3, shape = 21, colour = "steelblue", fill = "white") + - theme_blank() - -# with nodes sized according to degree centrality -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodes(size = degree(n), shape = 21, colour = "steelblue", fill = "white") + - theme_blank() - -# with nodes colored according to betweenness centrality - -n \%v\% "betweenness" <- betweenness(flo) -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "grey50") + - geom_nodes(aes(colour = betweenness), size = 3) + - scale_colour_gradient(low = "gold", high = "tomato") + - theme_blank() + - theme(legend.position = "bottom") - + data(flo, package = "network") + n <- network(flo, directed = FALSE) + + # just nodes + ggplot(n, aes(x, y)) + + geom_nodes(size = 3, shape = 21, colour = "steelblue") + + theme_blank() + + # with edges + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodes(size = 3, shape = 21, colour = "steelblue", fill = "white") + + theme_blank() + + # with nodes sized according to degree centrality + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodes(size = degree(n), shape = 21, colour = "steelblue", fill = "white") + + theme_blank() + + # with nodes colored according to betweenness centrality + + n \%v\% "betweenness" <- betweenness(flo) + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "grey50") + + geom_nodes(aes(colour = betweenness), size = 3) + + scale_colour_gradient(low = "gold", high = "tomato") + + theme_blank() + + theme(legend.position = "bottom") } + } diff --git a/man/geom_nodetext.Rd b/man/geom_nodetext.Rd index 01e2bec..b5fa7b2 100644 --- a/man/geom_nodetext.Rd +++ b/man/geom_nodetext.Rd @@ -82,58 +82,60 @@ All arguments to these geoms are identical to those of ## geom_nodetext examples if (require(network) && require(sna)) { - -n <- network(rgraph(10, tprob = 0.2), directed = FALSE) - -# just node labels -ggplot(n, aes(x, y)) + - geom_nodetext(aes(label = vertex.names)) + - theme_blank() - -# with nodes underneath -ggplot(n, aes(x, y)) + - geom_nodes(colour = "gold", size = 9) + - geom_nodetext(aes(label = vertex.names)) + - theme_blank() - -# with nodes and edges -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "gold") + - geom_nodes(colour = "gold", size = 9) + - geom_nodetext(aes(label = vertex.names)) + - theme_blank() - + n <- network(rgraph(10, tprob = 0.2), directed = FALSE) + + # just node labels + ggplot(n, aes(x, y)) + + geom_nodetext(aes(label = vertex.names)) + + theme_blank() + + # with nodes underneath + ggplot(n, aes(x, y)) + + geom_nodes(colour = "gold", size = 9) + + geom_nodetext(aes(label = vertex.names)) + + theme_blank() + + # with nodes and edges + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "gold") + + geom_nodes(colour = "gold", size = 9) + + geom_nodetext(aes(label = vertex.names)) + + theme_blank() } + ## geom_nodelabel examples if (require(network) && require(sna)) { - -data(flo, package = "network") -n <- network(flo, directed = FALSE) - -# with text labels -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "grey50") + - geom_nodelabel(aes(label = vertex.names)) + - theme_blank() - -# with text labels coloured according to degree centrality -n \%v\% "degree" <- degree(n) -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "grey50") + - geom_nodelabel(aes(label = vertex.names, fill = degree)) + - scale_fill_gradient(low = "gold", high = "tomato") + - theme_blank() - -# label only a subset of all nodes -high_degree <- function(x) { x[ x$degree > median(x$degree), ] } -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodes(aes(size = degree), colour = "steelblue") + - geom_nodelabel(aes(label = vertex.names), data = high_degree, - colour = "white", fill = "tomato") + - theme_blank() + data(flo, package = "network") + n <- network(flo, directed = FALSE) + + # with text labels + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "grey50") + + geom_nodelabel(aes(label = vertex.names)) + + theme_blank() + + # with text labels coloured according to degree centrality + n \%v\% "degree" <- degree(n) + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "grey50") + + geom_nodelabel(aes(label = vertex.names, fill = degree)) + + scale_fill_gradient(low = "gold", high = "tomato") + + theme_blank() + + # label only a subset of all nodes + high_degree <- function(x) { + x[ x$degree > median(x$degree), ] + } + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodes(aes(size = degree), colour = "steelblue") + + geom_nodelabel(aes(label = vertex.names), + data = high_degree, + colour = "white", fill = "tomato" + ) + + theme_blank() } } diff --git a/man/geom_nodetext_repel.Rd b/man/geom_nodetext_repel.Rd index f36c5ab..a7cd8eb 100644 --- a/man/geom_nodetext_repel.Rd +++ b/man/geom_nodetext_repel.Rd @@ -100,42 +100,45 @@ All arguments to these geoms are identical to those of ## geom_nodetext_repel example if (require(network) && require(sna)) { - -n <- network(rgraph(10, tprob = 0.2), directed = FALSE) -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodetext_repel(aes(label = paste("node", vertex.names)), - box.padding = unit(1, "lines")) + - geom_nodes(colour = "steelblue", size = 3) + - theme_blank() - + n <- network(rgraph(10, tprob = 0.2), directed = FALSE) + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodetext_repel(aes(label = paste("node", vertex.names)), + box.padding = unit(1, "lines") + ) + + geom_nodes(colour = "steelblue", size = 3) + + theme_blank() } + ## geom_nodelabel_repel examples if (require(network) && require(sna)) { - -data(flo, package = "network") -n <- network(flo, directed = FALSE) - -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodelabel_repel(aes(label = vertex.names), - box.padding = unit(1, "lines")) + - geom_nodes(colour = "steelblue", size = 3) + - theme_blank() - -# label only a subset of all nodes -n \%v\% "degree" <- degree(n) -low_degree <- function(x) { x[ x$degree < median(x$degree), ] } -ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges(colour = "steelblue") + - geom_nodelabel_repel(aes(label = vertex.names), - box.padding = unit(1.5, "lines"), - data = low_degree, - segment.colour = "tomato", - colour = "white", fill = "tomato") + - geom_nodes(aes(size = degree), colour = "steelblue") + - theme_blank() - + data(flo, package = "network") + n <- network(flo, directed = FALSE) + + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodelabel_repel(aes(label = vertex.names), + box.padding = unit(1, "lines") + ) + + geom_nodes(colour = "steelblue", size = 3) + + theme_blank() + + # label only a subset of all nodes + n \%v\% "degree" <- degree(n) + low_degree <- function(x) { + x[ x$degree < median(x$degree), ] + } + ggplot(n, aes(x, y, xend = xend, yend = yend)) + + geom_edges(colour = "steelblue") + + geom_nodelabel_repel(aes(label = vertex.names), + box.padding = unit(1.5, "lines"), + data = low_degree, + segment.colour = "tomato", + colour = "white", fill = "tomato" + ) + + geom_nodes(aes(size = degree), colour = "steelblue") + + theme_blank() } + } diff --git a/man/scale_safely.Rd b/man/scale_safely.Rd index f6c31fe..27dfe3b 100644 --- a/man/scale_safely.Rd +++ b/man/scale_safely.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/utilities.R \name{scale_safely} \alias{scale_safely} -\title{Rescale `x` to (0, 1), except if `x` is constant} +\title{Rescale x to (0, 1), except if x is constant} \usage{ scale_safely(x, scale = diff(range(x))) } @@ -13,9 +13,10 @@ scale_safely(x, scale = diff(range(x))) } \value{ The rescaled vector, coerced to a vector if necessary. +If the original vector was constant, all of its values are replaced by 0.5. } \description{ -Discussed in PR #32. +Discussed in PR #32: https://github.com/briatte/ggnetwork/pull/32 } \author{ Kipp Johnson diff --git a/man/unit.Rd b/man/unit.Rd new file mode 100644 index 0000000..5f07b6e --- /dev/null +++ b/man/unit.Rd @@ -0,0 +1,9 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities.R +\name{unit} +\alias{unit} +\title{unit} +\description{ +See \code{ggplot2::\link[ggplot2]{unit}} for details. +} +\keyword{internal} diff --git a/tests/testthat/test-fortify.R b/tests/testthat/test-fortify.R index 456711c..092d70f 100644 --- a/tests/testthat/test-fortify.R +++ b/tests/testthat/test-fortify.R @@ -1,38 +1,40 @@ -context("Test ggnetwork") +test_that("fortify.network", { + utils::data(flo, package = "network") + n <- network::network(flo, directed = FALSE) -data(flo, package = "network") - -library(network) -library(sna) - -test_that("fortify.network works", { - - n <- network(flo, directed = FALSE) - - expect_is(fortify(n), "data.frame") + expect_s3_class(fortify(n), "data.frame") expect_true(all(c("x", "y", "xend", "yend") %in% names(fortify(n)))) - ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_nodes() + - geom_edges() + - geom_nodetext(aes(label = vertex.names)) + - geom_edgetext(aes(label = 1)) + - theme_blank() - + expect_s3_class({ + ggplot2::ggplot(n, ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_nodes() + + geom_edges() + + geom_nodetext(aes(label = vertex.names)) + + geom_edgetext(aes(label = 1)) + + theme_blank() + }, class = "ggplot") + + expect_s3_class({ + utils::data(emon, package = "network") + ggplot2::ggplot(emon[[1]], ggplot2::aes(x = x, y = y, xend = xend, yend = yend)) + + geom_edges() + + geom_nodes(color = "tomato", size = 4) + + theme_blank() + }, class = "ggplot") }) -test_that("fortify.igraph works", { - +test_that("fortify.igraph", { n <- igraph::graph_from_adjacency_matrix(flo, mode = "undirected") - expect_is(fortify(n), "data.frame") + expect_s3_class(fortify(n), "data.frame") expect_true(all(c("x", "y", "xend", "yend") %in% names(fortify(n)))) - ggplot(n, aes(x, y, xend = xend, yend = yend)) + - geom_edges() + - geom_nodes() + - geom_nodetext(aes(label = vertex.names)) + - geom_edgetext(aes(label = 1)) + - theme_blank() - + expect_s3_class({ + ggplot2::ggplot(n, ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edges() + + geom_nodes() + + geom_nodetext(aes(label = name)) + + geom_edgetext(aes(label = 1)) + + theme_blank() + }, class = "ggplot") }) diff --git a/tests/testthat/test-geoms.R b/tests/testthat/test-geoms.R index 137454e..76a20d2 100644 --- a/tests/testthat/test-geoms.R +++ b/tests/testthat/test-geoms.R @@ -3,109 +3,126 @@ context("Test all geoms") data(emon, package = "network") test_that("geom_nodes works", { + expect_s3_class({ + ggplot(emon[[1]], aes(x, y)) + + geom_nodes() + }, class = "ggplot") - ggplot(emon[[1]], aes(x, y)) + - geom_nodes() - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodes() - + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodes() + }, class = "ggplot") }) test_that("geom_nodetext works", { + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodetext(aes(label = vertex.names)) + }, class = "ggplot") - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodetext(aes(label = vertex.names)) - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodetext(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1) + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodetext(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1) + }, class = "ggplot") expect_error( ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodetext(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1, - position = "identity"), + geom_nodetext(aes(label = Paid.Staff), + nudge_x = 1, nudge_y = 1, + position = "identity" + ), "Specify either" ) - }) test_that("geom_nodetext_repel works", { - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodetext_repel(aes(label = vertex.names)) - + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodetext_repel(aes(label = vertex.names)) + }, class = "ggplot") }) test_that("geom_nodelabel works", { + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodelabel(aes(label = vertex.names)) + }, class = "ggplot") - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodelabel(aes(label = vertex.names)) - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodelabel(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1) + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodelabel(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1) + }, class = "ggplot") expect_error( ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodelabel(aes(label = Paid.Staff), nudge_x = 1, nudge_y = 1, - position = "identity"), + geom_nodelabel(aes(label = Paid.Staff), + nudge_x = 1, nudge_y = 1, + position = "identity" + ), "Specify either" ) - }) test_that("geom_nodelabel_repel works", { - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_nodelabel_repel(aes(label = vertex.names)) - + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_nodelabel_repel(aes(label = vertex.names)) + }, class = "ggplot") }) test_that("geom_edges works", { # straight - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edges() + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_edges() + }, class = "ggplot") # curved - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edges(curvature = 0.1) - + expect_s3_class({ + ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + + geom_edges(curvature = 0.1) + }, class = "ggplot") }) test_that("geom_edgetext works", { + expect_s3_class({ + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgetext(aes(label = Frequency)) + }, class = "ggplot") - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgetext(aes(label = Frequency)) - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgetext(aes(label = Frequency), nudge_x = 1, nudge_y = 1) + expect_s3_class({ + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgetext(aes(label = Frequency), nudge_x = 1, nudge_y = 1) + }, class = "ggplot") expect_error( - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgetext(aes(label = Frequency), nudge_x = 1, nudge_y = 1, - position = "identity"), + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgetext(aes(label = Frequency), + nudge_x = 1, nudge_y = 1, + position = "identity" + ), "Specify either" ) - }) test_that("geom_edgelabel works", { - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgelabel(aes(label = Frequency)) - + expect_s3_class({ + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgelabel(aes(label = Frequency)) + }, class = "ggplot") }) test_that("geom_edgetext_repel works", { - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgetext_repel(aes(label = Frequency)) - + expect_s3_class({ + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgetext_repel(aes(label = Frequency)) + }, class = "ggplot") }) test_that("geom_edgelabel_repel works", { - - ggplot(emon[[1]], aes(x, y, xend = xend, yend = yend)) + - geom_edgelabel_repel(aes(label = Frequency)) - -}) \ No newline at end of file + expect_s3_class({ + ggplot2::ggplot(emon[[1]], ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edgelabel_repel(aes(label = Frequency)) + }, class = "ggplot") +}) diff --git a/tests/testthat/test-ggnetwork.R b/tests/testthat/test-ggnetwork.R index 9569a92..8e4aecb 100644 --- a/tests/testthat/test-ggnetwork.R +++ b/tests/testthat/test-ggnetwork.R @@ -1,27 +1,29 @@ -context("Test ggnetwork") - -data(emon, package = "network") - test_that("ggnetwork works", { - + data(emon, package = "network") expect_error(ggnetwork(-999, "could not coerce")) expect_error(ggnetwork(emon[[1]], layout = -999, "unsupported layout")) # facet by edge attribute - ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency") + expect_s3_class({ + ggnetwork(emon[[1]], arrow.gap = 0.02, by = "Frequency") + }, class = "data.frame") # user-provided layout - ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)) + expect_s3_class({ + ggnetwork(emon[[1]], layout = matrix(runif(28), ncol = 2)) + }, class = "data.frame") # edge weights in layout - ggnetwork(emon[[1]], layout = "kamadakawai", weights = "Frequency") + expect_s3_class({ + ggnetwork(emon[[1]], layout = "kamadakawai", weights = "Frequency") + }, class = "data.frame") # duplicated edges warning expect_warning( ggnetwork(rbind( matrix(c(1:2, 2:1), nrow = 2), - matrix(c(1:2, 2:1), nrow = 2))), + matrix(c(1:2, 2:1), nrow = 2) + )), "duplicated edges" ) - }) diff --git a/tests/testthat/test-utilities.R b/tests/testthat/test-utilities.R index 9fb00fd..3deb10e 100644 --- a/tests/testthat/test-utilities.R +++ b/tests/testthat/test-utilities.R @@ -1,19 +1,17 @@ -context("Test utilities") - test_that("theme_blank works", { - - ggplot(ggnetwork(emon[[1]]), aes(x, y, xend = xend, yend = yend)) + - geom_edges() + - geom_nodes() + - theme_blank() - + expect_s3_class({ + ggplot2::ggplot(ggnetwork(emon[[1]]), ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edges() + + geom_nodes() + + theme_blank() + }, class = "ggplot") }) test_that("theme_facet works", { - - ggplot(ggnetwork(emon[[1]]), aes(x, y, xend = xend, yend = yend)) + - geom_edges() + - geom_nodes() + - theme_facet() - + expect_s3_class({ + ggplot2::ggplot(ggnetwork(emon[[1]]), ggplot2::aes(x, y, xend = xend, yend = yend)) + + geom_edges() + + geom_nodes() + + theme_facet() + }, class = "ggplot") }) diff --git a/vignettes/ggnetwork.Rmd b/vignettes/ggnetwork.Rmd index 0501193..4874f63 100644 --- a/vignettes/ggnetwork.Rmd +++ b/vignettes/ggnetwork.Rmd @@ -25,10 +25,10 @@ Install the stable version from CRAN: install.packages("ggnetwork") ``` -Or use `devtools` to install the latest version of the package [from GitHub](https://github.com/briatte/ggnetwork): +Or use `remotes` to install the latest version of the package [from GitHub](https://github.com/briatte/ggnetwork): ```{r, eval=FALSE} -devtools::install_github("briatte/ggnetwork") +remotes::install_github("briatte/ggnetwork") ``` The package is meant to be used with `ggplot2` version 2.0.0 or above, so make sure that you update your version of `ggplot2` from CRAN before using `ggnetwork`: