diff --git a/DESCRIPTION b/DESCRIPTION index 34a3b94..c679767 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,3 +40,33 @@ Suggests: testthat (>= 2.1.0) Depends: R (>= 2.10) +Collate: + 'bin.R' + 'class-ContingencyTable.R' + 'class-Node.R' + 'class-Link.R' + 'class-Network.R' + 'methods-Links.R' + 'methods-Nodes.R' + 'methods-KPartiteNetwork.R' + 'class-KPartiteNetwork.R' + 'class-plotdata-bar.R' + 'class-plotdata-beeswarm.R' + 'class-plotdata-box.R' + 'class-plotdata-heatmap.R' + 'class-plotdata-histogram.R' + 'class-plotdata-line.R' + 'class-plotdata-map-markers.R' + 'class-plotdata-mosaic.R' + 'class-plotdata-scatter.R' + 'class-plotdata.R' + 'group.R' + 'methods-ContingencyTable.R' + 'methods-network.R' + 'panel.R' + 'plot.data-package.R' + 'utils-bin.R' + 'utils-json.R' + 'utils-pipe.R' + 'utils-stats.R' + 'utils.R' diff --git a/NAMESPACE b/NAMESPACE index c24ee55..fe1daf4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,6 +12,16 @@ S3method(numBinsToBinWidth,Date) S3method(numBinsToBinWidth,default) export("%>%") export(ContingencyTable) +export(KPartiteNetwork) +export(Link) +export(LinkList) +export(Network) +export(Node) +export(NodeId) +export(NodeIdList) +export(NodeList) +export(Partition) +export(Partitions) export(TwoByTwoTable) export(adjustToViewport) export(allStats) @@ -60,6 +70,15 @@ export(smoothedMean) export(specificity) export(writeJSON) exportClasses(ContingencyTable) +exportClasses(KPartiteNetwork) +exportClasses(Link) +exportClasses(LinkList) +exportClasses(Network) +exportClasses(Node) +exportClasses(NodeId) +exportClasses(NodeIdList) +exportClasses(NodeList) +exportClasses(Partitions) exportClasses(TwoByTwoTable) exportMethods(allStats) exportMethods(chiSqResults) diff --git a/R/class-KPartiteNetwork.R b/R/class-KPartiteNetwork.R new file mode 100644 index 0000000..e8a3669 --- /dev/null +++ b/R/class-KPartiteNetwork.R @@ -0,0 +1,119 @@ +#' @include methods-KPartiteNetwork.R +check_partitions <- function(object) { + errors <- character() + + if (!!length(getAllNodeIds(object))) { + # Ensure that no node is in multiple partitions + if (length(getAllNodeIds(object)) > data.table::uniqueN(getAllNodeIds(object))) { + errors <- c(errors, 'Found a node in multiple partitions. Nodes can only exist in one partition.') + } + } + + return(if (length(errors) == 0) TRUE else errors) +} + +#' Partitions +#' +#' A class for representing partitions in a k-partite network +#' +#' @name Partitions-class +#' @rdname Partitions-class +#' @export +setClass("Partitions", + contains = "SimpleList", + prototype = prototype( + elementType = "NodeIdList" + ), + validity = check_partitions +) + +#' Create a Partition +#' +#' An alias to NodeIdList +#' +#' @name Partition-class +#' @rdname Partition-class +#' @export +Partition <- NodeIdList + +#' Create Partitions +#' +#' A list of Partition objects, each containing a list of node +#' ids that belong to a single partition +#' +#' @param partitions list of Partition (or NodeIdList) objects +#' @export +#' @rdname Partitions +Partitions <- function(partitions = list()) { + if (length(partitions) == 0) { + return(new("Partitions", S4Vectors:::SimpleList(list()))) + } + + if (length(partitions) == 1 && !is.list(partitions)) { + ## an edge case i suppose where we had a single partition w a single node + partitions <- list(Partition(partitions)) + } + + if (!is.list(partitions)) { + stop('Partitions must be a list') + } + + if (!all(unlist(lapply(partitions, inherits, "NodeIdList")))) { + stop('Partitions must be a list of NodeIdList objects') + } + + return(new("Partitions", S4Vectors:::SimpleList(partitions))) +} + +check_kpartite_network <- function(object) { + + errors <- character() + + # Check that all nodes are in at least one of the partitions + if (!all(getNodeIds(object@nodes) %in% getAllNodeIds(object@partitions))) { + errors <- c(errors, 'Found a node that is not in any partition. All nodes must be assigned to a partition.') + } + if (!all(getAllNodeIds(object@partitions) %in% getNodeIds(object@nodes))) { + errors <- c(errors, 'Found an id in a partition that is not in the nodes list. All partitions must must include ids in the nodes list.') + } + + # Check that linkColorScheme is one of the accepted values + if (!object@linkColorScheme %in% c('none', 'posneg')) { + errors <- c(errors, 'linkColorScheme must be one of "none" or "posneg"') + } + + return(if (length(errors) == 0) TRUE else errors) +} + +#' k-Partite Network +#' +#' The k-partite network class represents data in the form of a network with k distinct groups of nodes +#' in which nodes connect only with nodes from the other groups. In other words, there are only inter-group +#' links, no intra-group links. These k groups are commonly called partitions. +#' k-partite networks can have any property of a regular network, but they also designate the node ids +#' that belong to each partition (group). +#' +#' @slot links LinkList object defining the links in the network. +#' @slot nodes NodeList object defining the nodes in the network. Some nodes may not have any links. +#' @slot linkColorScheme string defining the type of coloring scheme the links follow. Options are 'none' (default) and 'posneg'. +#' Use a method assignLinkColors() to assign colors to links and set this slot's value. +#' @slot partitions list of node ids that belong to each partition +#' +#' @name KPartiteNetwork-class +#' @rdname KPartiteNetwork-class +#' @include class-Network.R +#' @export +KPartiteNetwork <- setClass("KPartiteNetwork", + contains = "Network", + slots = c( + partitions = "Partitions" + ), + prototype = prototype( + links = LinkList(), + nodes = NodeList(), + linkColorScheme = 'none', + variableMapping = VariableMetadataList(), + partitions = Partitions() + ), + validity = check_kpartite_network +) \ No newline at end of file diff --git a/R/class-Link.R b/R/class-Link.R new file mode 100644 index 0000000..425dc8b --- /dev/null +++ b/R/class-Link.R @@ -0,0 +1,92 @@ + + +check_link <- function(object) { + + errors <- character() + + # Link color must be a string or number + if (!is.null(object@color) & !is.character(object@color) & !is.numeric(object@color)) { + errors <- c(errors, "Link color must be a string or number that represents a color or can be mapped to a color.") + } + + #dont allow self-links for now + if (id(object@source) == id(object@target)) { + errors <- c(errors, "Links cannot be self-links. They must have a different source and target.") + } + + return(if (length(errors) == 0) TRUE else errors) +} + + +#' Link +#' +#' Represent one singular link. A link has a source, and a target. It may be directed or undirected. +#' It may have an associated weight, color, timestamp, or label (coming soon!) +#' +#' @name Link-class +#' @rdname Link-class +#' @include class-Node.R +#' @export +Link <- setClass("Link", + representation( + source = "Node", + target = "Node", + weight = "numeric", + color = 'ANY', + isDirected = "logical" + # label = "character" # coming soon + ), + prototype = prototype( + source = new("Node"), + target = new("Node"), + weight = 1, + isDirected = FALSE + ), + validity = check_link +) + + +check_link_list <- function(object) { + + errors <- character() + + if (any(unlist(lapply(object, function(x) {!is.null(color(x))})))) { + # If one link has a color, all must have colors + if (!all(unlist(lapply(object, function(x) {!is.null(color(x))})))) { + errors <- c(errors, "If one link has a color, all links must have a color") + } + + # Link colors must be all the same class + if (length(unique(unlist(lapply(object, function(x) {class(color(x))})))) > 1) { + errors <- c(errors, "Link colors must be all the same class") + } + } + + + # If one link has a weight, all must have weights + if (any(unlist(lapply(object, function(x) {!is.null(weight(x))})))) { + if (!all(unlist(lapply(object, function(x) {!is.null(weight(x))})))) { + errors <- c(errors, "If one link has a weight, all links must have a weight") + } + } + + + return(if (length(errors) == 0) TRUE else errors) + +} + +#' Link List +#' +#' A class for representing links in a network +#' +#' @name LinkList-class +#' @rdname LinkList-class +#' @importFrom S4Vectors SimpleList +#' @export +LinkList <- setClass("LinkList", + contains = "SimpleList", + prototype = prototype( + elementType = "Link" + ), + validity = check_link_list +) \ No newline at end of file diff --git a/R/class-Network.R b/R/class-Network.R new file mode 100644 index 0000000..5edc04f --- /dev/null +++ b/R/class-Network.R @@ -0,0 +1,51 @@ +# Network class +check_network <- function(object) { + + errors <- character() + + # Check that all nodes in links are in nodes + nodesInLinks <- NodeList(union(getSourceNodes(object@links), getTargetNodes(object@links))) # may become a method later if i find i use it elsewhere + if (!all(getNodeIds(nodesInLinks) %in% getNodeIds(object@nodes))) { + errors <- c(errors, 'Found a node in a link that is not in the node list. All nodes in links must also be in nodes.') + } + + # Check that linkColorScheme is one of the accepted values + if (!object@linkColorScheme %in% c('none', 'posneg')) { + errors <- c(errors, 'linkColorScheme must be one of "none" or "posneg"') + } + + + return(if (length(errors) == 0) TRUE else errors) +} + + +#' Network +#' +#' A class for representing networks. A network is composed of nodes and links (edges, connections, etc.). A link is represented +#' as a pair of nodes, with optional attributes such as weight (see Link). To represent a network, we need both the list of links in the network and a list of nodes +#' in case some nodes have no links. A network can also have properties such as directedness, levels, colors, etc. (coming soon). +#' +#' @slot links LinkList object defining the links in the network. +#' @slot nodes NodeList object defining the nodes in the network. Some nodes may not have any links. +#' @slot linkColorScheme string defining the type of coloring scheme the links follow. Options are 'none' (default) and 'posneg'. +#' @slot variableMapping veupathUtils::VariableMetadataList object defining the variable mappings in the network. +#' Use a method assignLinkColors() to assign colors to links and set this slot's value. +#' +#' @name Network-class +#' @rdname Network-class +#' @include class-Link.R +#' @export +Network <- setClass("Network", + representation( + links = "LinkList", + nodes = "NodeList", + linkColorScheme = "character", + variableMapping = "VariableMetadataList" + ), prototype = prototype( + links = LinkList(), + nodes = NodeList(), + linkColorScheme = 'none', + variableMapping = VariableMetadataList() + ), + validity = check_network +) diff --git a/R/class-Node.R b/R/class-Node.R new file mode 100644 index 0000000..dfa6fdb --- /dev/null +++ b/R/class-Node.R @@ -0,0 +1,207 @@ +check_node_id <- function(object) { + errors <- character() + + # node id must not be empty + if (length(object@value) == 0) { + errors <- c(errors, "Node id must not be empty") + } + + # must not be NA + if (is.na(object@value)) { + errors <- c(errors, "Node id must not be NA") + } + + # must not be '' + if (object@value == "") { + errors <- c(errors, "Node id must not be an empty string") + } + + return(if (length(errors) == 0) TRUE else errors) +} + +#' A Node Id +#' +#' A class for representing node ids +#' +#' @name NodeId-class +#' @rdname NodeId-class +#' @export +setClass("NodeId", + representation( + value = "character" + ), + prototype = prototype( + value = character() + ), + validity = check_node_id +) + + +#' Create a Node Id +#' +#' Because typing `NodeId(id = 'foo')` is annoying, this function is provided +#' to make things easier. Now you can do `NodeId('foo')` +#' +#' @param value string a unique identifier for the node +#' @export +NodeId <- function(value) { + new("NodeId", value = value) +} + +check_node_id_list <- function(object) { + errors <- character() + + # make sure all ids are unique + if (length(unique(unlist(lapply(object, id)))) != length(unlist(lapply(object, id)))) { + errors <- c(errors, "Node ids must be unique") + } + + return(if (length(errors) == 0) TRUE else errors) +} + + +#' A Node Id List +#' +#' A class for representing node id lists +#' +#' @name NodeIdList-class +#' @rdname NodeIdList-class +#' @export +setClass("NodeIdList", + contains = "SimpleList", + prototype = prototype( + elementType = "NodeId" + ), + validity = check_node_id_list +) + +#' Create a NodeIdList +#' +#' @param nodeIds list of node ids +#' @export +NodeIdList <- function(nodeIds) { + + if (length(nodeIds) == 0) { + stop("nodeIds must not be empty") + } + + if (length(nodeIds) == 1 && !is.list(nodeIds)) { + nodeIds <- list(nodeIds) + } + + if (!is.list(nodeIds)) { + stop("nodeIds must be a list") + } + + if (all(unlist(lapply(nodeIds, inherits, 'Node')))) { + nodeIds <- lapply(nodeIds, id) + nodeIds <- lapply(nodeIds, NodeId) + } else if (all(unlist(lapply(nodeIds, inherits, 'character')))) { + nodeIds <- lapply(nodeIds, NodeId) + } else if (!all(unlist(lapply(nodeIds, inherits, 'NodeId')))) { + stop("nodeIds must be a list of Node, NodeId or character objects") + } + + return(new("NodeIdList", S4Vectors::SimpleList(nodeIds))) +} + +check_node <- function(object) { + + errors <- character() + + # Node color must be a string or number + if (!is.null(object@color) & !is.character(object@color) & !is.numeric(object@color)) { + errors <- c(errors, "Node color must be a string or number") + } + + # If Node has x it must have y and vice versa + if (!is.null(object@x) & is.null(object@y)) { + errors <- c(errors, "If Node has x it must have y and vice versa") + } else if (is.null(object@x) & !is.null(object@y)) { + errors <- c(errors, "If Node has y it must have x and vice versa") + } + + # Node weight must be a number + if (!is.null(object@weight) & !is.numeric(object@weight)) { + errors <- c(errors, "Node weight must be a number") + } + + return(if (length(errors) == 0) TRUE else errors) +} + +# this could be made into a generic helper in veupathUtils +# it just generates random alpha-numeric strings +generate_node_id <- function(n = 5000) { + a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE)) + paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE)) +} + +#' Node +#' +#' A class for representing nodes in a network +#' +#' @slot id NodeId a unique identifier for the node +#' @slot x numeric value indicating the x coordinate of the node. Optional. +#' @slot y numeric value indicating the y coordinate of the node. Optional. +#' @slot color string or numeric that determines the color of the node. Optional. +#' @slot weight numeric value associated with the node, such as timestamp or other node-associated data. Optional. +#' +#' @name Node-class +#' @rdname Node-class +#' @export +Node <- setClass("Node", + slots = c( + id = "NodeId", + x = "numeric", + y = "numeric", + color = "ANY", + weight = "ANY" + ), + validity = check_node +) + +check_node_list <- function(object) { + + errors <- character() + + if (any(unlist(lapply(object, function(x) {!is.null(color(x))})))) { + # If one node has a color, all must have colors + if (!all(unlist(lapply(object, function(x) {!is.null(color(x))})))) { + errors <- c(errors, "If one node has a color, all nodes must have a color") + } + + # Node colors must be all the same class + if (length(unique(unlist(lapply(object, function(x) {class(color(x))})))) > 1) { + errors <- c(errors, "Node colors must be all the same class") + } + } + + # If one node has a weight, all must have weights + if (any(unlist(lapply(object, function(x) {!is.null(weight(x))})))) { + if (!all(unlist(lapply(object, function(x) {!is.null(weight(x))})))) { + errors <- c(errors, "If one node has a weight, all nodes must have a weight") + } + } + + + return(if (length(errors) == 0) TRUE else errors) +} + + +#' NodeList +#' +#' A class for representing a list of nodes. +#' +#' @name NodeList-class +#' @rdname NodeList-class +#' @importFrom S4Vectors SimpleList +#' @export +NodeList <- setClass("NodeList", + contains = "SimpleList", + prototype = prototype( + elementType = "Node" + ), + validity = check_node_list +) + + diff --git a/R/methods-KPartiteNetwork.R b/R/methods-KPartiteNetwork.R new file mode 100644 index 0000000..d646fbd --- /dev/null +++ b/R/methods-KPartiteNetwork.R @@ -0,0 +1,4 @@ +#' @include methods-Nodes.R +## Methods for Partitions +setGeneric("getAllNodeIds", function(object) standardGeneric("getAllNodeIds")) +setMethod("getAllNodeIds", "Partitions", function(object) unlist(lapply(as.list(object), getNodeIds))) \ No newline at end of file diff --git a/R/methods-Links.R b/R/methods-Links.R new file mode 100644 index 0000000..40f2dfb --- /dev/null +++ b/R/methods-Links.R @@ -0,0 +1,34 @@ +# Methods for Link and LinkList objects + +# Accessors for fanciness +setGeneric("source", function(object) standardGeneric("source")) +setGeneric("source<-", function(object, value) standardGeneric("source<-")) +setGeneric("target", function(object) standardGeneric("target")) +setGeneric("target<-", function(object, value) standardGeneric("target<-")) +setGeneric("weight", function(object) standardGeneric("weight")) +setGeneric("weight<-", function(object, value) standardGeneric("weight<-")) +setGeneric("color", function(object) standardGeneric("color")) +setGeneric("color<-", function(object, value) standardGeneric("color<-")) + +setMethod("source", "Link", function(object) object@source) +setMethod("source<-", "Link", function(object, value) {object@source <- value; validObject(object); object}) +setMethod("target", "Link", function(object) object@target) +setMethod("target<-", "Link", function(object, value) {object@target <- value; validObject(object); object}) +setMethod("weight", "Link", function(object) object@weight) +setMethod("weight<-", "Link", function(object, value) {object@weight <- value; validObject(object); object}) +setMethod("color", "Link", function(object) object@color) +setMethod("color<-", "Link", function(object, value) {object@color <- value; validObject(object); object}) + + +# Additional methods +# Link properties such as color are returned as vectors, while grabbing particular nodes from the +# LinkList returns lists of nodes. +setGeneric("getSourceNodes", function(object) standardGeneric("getSourceNodes")) +setMethod("getSourceNodes", "LinkList", function(object) lapply(object, function(x) source(x))) +setGeneric("getTargetNodes", function(object) standardGeneric("getTargetNodes")) +setMethod("getTargetNodes", "LinkList", function(object) lapply(object, function(x) target(x))) +setGeneric("getWeights", function(object) standardGeneric("getWeights")) +setMethod("getWeights", "LinkList", function(object) unlist(lapply(object, function(x) weight(x)))) +setGeneric("getColors", function(object) standardGeneric("getColors")) +setMethod("getColors", "LinkList", function(object) unlist(lapply(object, function(x) color(x)))) + diff --git a/R/methods-Nodes.R b/R/methods-Nodes.R new file mode 100644 index 0000000..c6e363b --- /dev/null +++ b/R/methods-Nodes.R @@ -0,0 +1,42 @@ +# Methods for Node and NodeList objects + + +# Accessors for fanciness +setGeneric("id", function(object) standardGeneric("id")) +setGeneric("id<-", function(object, value) standardGeneric("id<-")) +setGeneric("x", function(object) standardGeneric("x")) +setGeneric("x<-", function(object, value) standardGeneric("x<-")) +setGeneric("y", function(object) standardGeneric("y")) +setGeneric("y<-", function(object, value) standardGeneric("y<-")) + +#' @include methods-Links.R +## Methods for Nodes +setMethod("id", "Node", function(object) id(object@id)) +setMethod("x", "Node", function(object) object@x) +setMethod("y", "Node", function(object) object@y) +setMethod("color", "Node", function(object) object@color) +setMethod("weight", "Node", function(object) object@weight) +setMethod("id<-", "Node", function(object, value) {object@NodeId@id <- value; validObject(object); object}) +setMethod("x<-", "Node", function(object, value) {object@x <- value; validObject(object); object}) +setMethod("y<-", "Node", function(object, value) {object@y <- value; validObject(object); object}) +setMethod("color<-", "Node", function(object, value) {object@color <- value; validObject(object); object}) +setMethod("weight<-", "Node", function(object, value) {object@weight <- value; validObject(object); object}) + +## Methods for NodeId +setMethod("id", "NodeId", function(object) object@value) +setMethod("id<-", "NodeId", function(object, value) {object@id <- value; validObject(object); object}) +#alias for the id methods for NodeId +setGeneric("value", function(object) standardGeneric("value")) +setGeneric("value<-", function(object, value) standardGeneric("value<-")) +setMethod("value", "NodeId", function(object) id(object)) +setMethod("value<-", "NodeId", function(object, value) {id(object) <- value; validObject(object); object}) + +## Methods for NodeLists +setGeneric("getNodeIds", function(object) standardGeneric("getNodeIds")) +setMethod("getNodeIds", "NodeList", function(object) unlist(lapply(as.list(object), id))) +setMethod("getWeights", "NodeList", function(object) unlist(lapply(as.list(object), weight))) +setMethod("getColors", "NodeList", function(object) unlist(lapply(as.list(object), color))) + +## Methods for NodeIdList +setMethod("getNodeIds", "NodeIdList", function(object) unlist(lapply(as.list(object), id))) +setMethod("getNodeIds", "Network", function(object) getNodeIds(object@nodes)) \ No newline at end of file diff --git a/R/methods-network.R b/R/methods-network.R new file mode 100644 index 0000000..ac5bf86 --- /dev/null +++ b/R/methods-network.R @@ -0,0 +1,82 @@ +# Methods for the Network class + + +# Fancy accessors +setGeneric("getNodes", function(object) standardGeneric("getNodes")) +setMethod("getNodes", "Network", function(object) object@nodes) +setGeneric("getLinks", function(object) standardGeneric("getLinks")) +setMethod("getLinks", "Network", function(object) object@links) +setGeneric("getLinkColorScheme", function(object) standardGeneric("getLinkColorScheme")) +setMethod("getLinkColorScheme", "Network", function(object) object@linkColorScheme) +# No setters! Once created, a network should only be updated via network methods + +# Remove isolated nodes +# Get isolated nodes +# Remove redundant links +# Remove redundant nodes +# Get Degree list +# Get Weighted Degree list +# etc. +# Threshold network by edge weight +# Assign color scheme + + + +# #' Write json to local tmp file +# #' +# #' This function returns the name of a json file which it has +# #' written a network object out to. +# #' @param net a data.table to convert to json and write to a tmp file +# #' @param pattern optional tmp file prefix +# #' @param verbose boolean that declares if logging is desired +# #' @return character name of a tmp file w ext *.json +# #' @importFrom jsonlite toJSON +# #' @export +# writeNetworkToJSON <- function(net, pattern=NULL, verbose = c(TRUE, FALSE) ) { +# verbose <- veupathUtils::matchArg(verbose) + +# outJson <- getNetworkJSON(net, verbose) +# if (is.null(pattern)) { +# pattern <- attr(net, 'class')[1] +# if (is.null(pattern)) { +# pattern <- 'file' +# } +# } +# outFileName <- basename(tempfile(pattern = pattern, tmpdir = tempdir(), fileext = ".json")) +# write(outJson, outFileName) +# veupathUtils::logWithTime(paste('New output file written:', outFileName), verbose) + +# return(outFileName) +# } + +# # Write a network to a json string +# getNetworkJSON <- function(net, verbose = c(TRUE, FALSE)) { + +# networkAttributes <- attributes(net) + +# # Covert all columns to character +# netChar <- data.frame(lapply(net, as.character)) + +# # Whenever a node is referenced, it should be in the form {id: nodeid}. Update this +# # for both the list of nodes, and the source + target columns +# nodeList <- data.frame(id = networkAttributes$nodes) +# netChar$source <- lapply(netChar$source, function(node) { return(list(id=jsonlite::unbox(node)))}) +# netChar$target <- lapply(netChar$target, function(node) { return(list(id=jsonlite::unbox(node)))}) + +# obj <- list( +# nodes = nodeList, +# links = netChar +# ) + +# # Add additional properties for other network classes +# if ('column1NodeIDs' %in% names(networkAttributes)) obj$column1NodeIDs <- networkAttributes$column1NodeIDs +# if ('column2NodeIDs' %in% names(networkAttributes)) obj$column2NodeIDs <- networkAttributes$column2NodeIDs + + +# # Covert to json string +# json <- jsonlite::toJSON(obj, na=NULL) + + +# return(json) +# } + diff --git a/man/KPartiteNetwork-class.Rd b/man/KPartiteNetwork-class.Rd new file mode 100644 index 0000000..c998bdd --- /dev/null +++ b/man/KPartiteNetwork-class.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-KPartiteNetwork.R +\docType{class} +\name{KPartiteNetwork-class} +\alias{KPartiteNetwork-class} +\alias{KPartiteNetwork} +\title{k-Partite Network} +\description{ +The k-partite network class represents data in the form of a network with k distinct groups of nodes +in which nodes connect only with nodes from the other groups. In other words, there are only inter-group +links, no intra-group links. These k groups are commonly called partitions. +k-partite networks can have any property of a regular network, but they also designate the node ids +that belong to each partition (group). +} +\section{Slots}{ + +\describe{ +\item{\code{links}}{LinkList object defining the links in the network.} + +\item{\code{nodes}}{NodeList object defining the nodes in the network. Some nodes may not have any links.} + +\item{\code{linkColorScheme}}{string defining the type of coloring scheme the links follow. Options are 'none' (default) and 'posneg'. +Use a method assignLinkColors() to assign colors to links and set this slot's value.} + +\item{\code{partitions}}{list of node ids that belong to each partition} +}} + diff --git a/man/Link-class.Rd b/man/Link-class.Rd new file mode 100644 index 0000000..78c8efa --- /dev/null +++ b/man/Link-class.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Link.R +\docType{class} +\name{Link-class} +\alias{Link-class} +\alias{Link} +\title{Link} +\description{ +Represent one singular link. A link has a source, and a target. It may be directed or undirected. +It may have an associated weight, color, timestamp, or label (coming soon!) +} diff --git a/man/LinkList-class.Rd b/man/LinkList-class.Rd new file mode 100644 index 0000000..e5b8502 --- /dev/null +++ b/man/LinkList-class.Rd @@ -0,0 +1,10 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Link.R +\docType{class} +\name{LinkList-class} +\alias{LinkList-class} +\alias{LinkList} +\title{Link List} +\description{ +A class for representing links in a network +} diff --git a/man/Network-class.Rd b/man/Network-class.Rd new file mode 100644 index 0000000..ab77309 --- /dev/null +++ b/man/Network-class.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Network.R +\docType{class} +\name{Network-class} +\alias{Network-class} +\alias{Network} +\title{Network} +\description{ +A class for representing networks. A network is composed of nodes and links (edges, connections, etc.). A link is represented +as a pair of nodes, with optional attributes such as weight (see Link). To represent a network, we need both the list of links in the network and a list of nodes +in case some nodes have no links. A network can also have properties such as directedness, levels, colors, etc. (coming soon). +} +\section{Slots}{ + +\describe{ +\item{\code{links}}{LinkList object defining the links in the network.} + +\item{\code{nodes}}{NodeList object defining the nodes in the network. Some nodes may not have any links.} + +\item{\code{linkColorScheme}}{string defining the type of coloring scheme the links follow. Options are 'none' (default) and 'posneg'.} + +\item{\code{variableMapping}}{veupathUtils::VariableMetadataList object defining the variable mappings in the network. +Use a method assignLinkColors() to assign colors to links and set this slot's value.} +}} + diff --git a/man/Node-class.Rd b/man/Node-class.Rd new file mode 100644 index 0000000..f62ec59 --- /dev/null +++ b/man/Node-class.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\docType{class} +\name{Node-class} +\alias{Node-class} +\alias{Node} +\title{Node} +\description{ +A class for representing nodes in a network +} +\section{Slots}{ + +\describe{ +\item{\code{id}}{NodeId a unique identifier for the node} + +\item{\code{x}}{numeric value indicating the x coordinate of the node. Optional.} + +\item{\code{y}}{numeric value indicating the y coordinate of the node. Optional.} + +\item{\code{color}}{string or numeric that determines the color of the node. Optional.} + +\item{\code{weight}}{numeric value associated with the node, such as timestamp or other node-associated data. Optional.} +}} + diff --git a/man/NodeId-class.Rd b/man/NodeId-class.Rd new file mode 100644 index 0000000..409fa90 --- /dev/null +++ b/man/NodeId-class.Rd @@ -0,0 +1,9 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\docType{class} +\name{NodeId-class} +\alias{NodeId-class} +\title{A Node Id} +\description{ +A class for representing node ids +} diff --git a/man/NodeId.Rd b/man/NodeId.Rd new file mode 100644 index 0000000..f8d1b16 --- /dev/null +++ b/man/NodeId.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\name{NodeId} +\alias{NodeId} +\title{Create a Node Id} +\usage{ +NodeId(value) +} +\arguments{ +\item{value}{string a unique identifier for the node} +} +\description{ +Because typing `NodeId(id = 'foo')` is annoying, this function is provided +to make things easier. Now you can do `NodeId('foo')` +} diff --git a/man/NodeIdList-class.Rd b/man/NodeIdList-class.Rd new file mode 100644 index 0000000..1720a1b --- /dev/null +++ b/man/NodeIdList-class.Rd @@ -0,0 +1,9 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\docType{class} +\name{NodeIdList-class} +\alias{NodeIdList-class} +\title{A Node Id List} +\description{ +A class for representing node id lists +} diff --git a/man/NodeIdList.Rd b/man/NodeIdList.Rd new file mode 100644 index 0000000..6ddb4a3 --- /dev/null +++ b/man/NodeIdList.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\name{NodeIdList} +\alias{NodeIdList} +\title{Create a NodeIdList} +\usage{ +NodeIdList(nodeIds) +} +\arguments{ +\item{nodeIds}{list of node ids} +} +\description{ +Create a NodeIdList +} diff --git a/man/NodeList-class.Rd b/man/NodeList-class.Rd new file mode 100644 index 0000000..e02ead8 --- /dev/null +++ b/man/NodeList-class.Rd @@ -0,0 +1,10 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-Node.R +\docType{class} +\name{NodeList-class} +\alias{NodeList-class} +\alias{NodeList} +\title{NodeList} +\description{ +A class for representing a list of nodes. +} diff --git a/man/Partition-class.Rd b/man/Partition-class.Rd new file mode 100644 index 0000000..9ad0e46 --- /dev/null +++ b/man/Partition-class.Rd @@ -0,0 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-KPartiteNetwork.R +\name{Partition-class} +\alias{Partition-class} +\alias{Partition} +\title{Create a Partition} +\usage{ +Partition(nodeIds) +} +\description{ +An alias to NodeIdList +} diff --git a/man/Partitions-class.Rd b/man/Partitions-class.Rd new file mode 100644 index 0000000..3745ab7 --- /dev/null +++ b/man/Partitions-class.Rd @@ -0,0 +1,9 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-KPartiteNetwork.R +\docType{class} +\name{Partitions-class} +\alias{Partitions-class} +\title{Partitions} +\description{ +A class for representing partitions in a k-partite network +} diff --git a/man/Partitions.Rd b/man/Partitions.Rd new file mode 100644 index 0000000..1d49f22 --- /dev/null +++ b/man/Partitions.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-KPartiteNetwork.R +\name{Partitions} +\alias{Partitions} +\title{Create Partitions} +\usage{ +Partitions(partitions = list()) +} +\arguments{ +\item{partitions}{list of Partition (or NodeIdList) objects} +} +\description{ +A list of Partition objects, each containing a list of node +ids that belong to a single partition +} diff --git a/tests/testthat/test-kpartite-network.R b/tests/testthat/test-kpartite-network.R new file mode 100644 index 0000000..9c569b1 --- /dev/null +++ b/tests/testthat/test-kpartite-network.R @@ -0,0 +1,74 @@ +test_that("k-partite networks can be created", { + # Create nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create links + link1 <- Link(source = nodeA, target = nodeB, color = 1, weight = 10) + link2 <- Link(source = nodeB, target = nodeC, color = 2, weight = 20) + link3 <- Link(source = nodeC, target = nodeA, color = 3, weight = 30) + + # Create partitions + partition1 <- Partition(list(nodeA, nodeB)) + partition2 <- Partition(nodeC) + + # Create k-partite network + bpnet <- KPartiteNetwork( + links = LinkList(list(link1, link2, link3)), + nodes = NodeList(list(nodeA, nodeB, nodeC)), + partitions = Partitions(list(partition1, partition2)) + ) + + expect_equal(getNodes(bpnet), NodeList(c(nodeA, nodeB, nodeC))) + expect_equal(getLinks(bpnet), LinkList(c(link1, link2, link3))) + expect_equal(getLinkColorScheme(bpnet), 'none') + +}) + +test_that("k-partite networks cannot be created from nonsensical inputs", { + + # Create nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create links + link1 <- Link(source = nodeA, target = nodeB, color = 1, weight = 10) + link2 <- Link(source = nodeB, target = nodeC, color = 2, weight = 20) + link3 <- Link(source = nodeC, target = nodeA, color = 3, weight = 30) + + # Create columns + partition1 <- Partition(list(nodeA, nodeB, nodeC)) + partition2 <- Partition(nodeC) + + + # Nodes can't be in both columns + expect_error(KPartiteNetwork( + links = LinkList(list(link1, link2, link3)), + nodes = NodeList(list(nodeA, nodeB, nodeC)), + partitions = list(partition1, partition2) + )) + + # All nodes must be in one of the columns + partition1 <- Partition(nodeA) + partition2 <- Partition(nodeC) + expect_error(KPartiteNetwork( + links = LinkList(c(link1, link2, link3)), + nodes = NodeList(c(nodeA, nodeB, nodeC)), + partitions = list(partition1IDs, partition2IDs) + )) + +}) diff --git a/tests/testthat/test-links.R b/tests/testthat/test-links.R new file mode 100644 index 0000000..383dc5e --- /dev/null +++ b/tests/testthat/test-links.R @@ -0,0 +1,116 @@ +test_that("Links work", { + # Make a link + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + link <- Link(source = nodeA, target = nodeB) + expect_equal(class(link)[1], 'Link') +}) + +test_that("Link methods work", { + + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + + link <- Link(source = nodeA, target = nodeB) + expect_equal(source(link), nodeA) + expect_equal(target(link), nodeB) + expect_equal(weight(link), 1) + expect_true(is.null(color(link))) + + link <- Link(source = nodeA, target = nodeB, color = 'red', weight = 10) + expect_equal(source(link), nodeA) + expect_equal(target(link), nodeB) + expect_equal(color(link), 'red') + expect_equal(weight(link), 10) + +}) + +test_that("LinkList methods work", { + + # Create some nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create some links + link1 <- Link(source = nodeA, target = nodeB) + link2 <- Link(source = nodeB, target = nodeC) + link3 <- Link(source = nodeC, target = nodeA) + + linkList <- LinkList(S4Vectors::SimpleList(c(link1, link2, link3))) + expect_equal(length(linkList), 3) + expect_equal(getSourceNodes(linkList), list(nodeA, nodeB, nodeC)) + expect_equal(getTargetNodes(linkList), list(nodeB, nodeC, nodeA)) + expect_equal(getWeights(linkList), c(1, 1, 1)) + expect_equal(getColors(linkList), c(NULL, NULL, NULL)) + + + # Create some more links + link1 <- Link(source = nodeA, target = nodeB, weight = 2, color = 'red') + link2 <- Link(source = nodeB, target = nodeC, weight = 0.1, color = 'blue') + link3 <- Link(source = nodeC, target = nodeA, weight = 3, color = 'green') + + linkList <- LinkList(S4Vectors::SimpleList(c(link1, link2, link3))) + expect_equal(length(linkList), 3) + expect_equal(getSourceNodes(linkList), list(nodeA, nodeB, nodeC)) + expect_equal(getTargetNodes(linkList), list(nodeB, nodeC, nodeA)) + expect_equal(getWeights(linkList), c(2, 0.1, 3)) + expect_equal(getColors(linkList), c('red', 'blue', 'green')) +}) + +test_that("Links cannot be created from nonsensical inputs", { + + # Create nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + + expect_error(Link(source = nodeA, target = nodeB, color = false, weight = 10)) +}) + +test_that("LinkLists cannot be created from nonsensical inputs", { + + # Create nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create links + link1 <- Link(source = nodeA, target = nodeB) + link2 <- Link(source = nodeB, target = nodeC) + link3 <- Link(source = nodeC, target = nodeA, color='red') + + # If one link has a color, all must have colors + expect_error(LinkList(S4Vectors::SimpleList(c(link1, link2, link3)))) + + # Link colors must be of the same class + color(link2) <- 2 + expect_error(LinkList(S4Vectors::SimpleList(c(link1, link2, link3)))) + + # If one link has a weight, all must have weights + weight(link3) <- 100 + expect_error(LinkList(S4Vectors::SimpleList(c(link1, link2, link3)))) +}) diff --git a/tests/testthat/test-network.R b/tests/testthat/test-network.R new file mode 100644 index 0000000..5a3c12b --- /dev/null +++ b/tests/testthat/test-network.R @@ -0,0 +1,52 @@ +test_that("Networks can be created and their properties accessed", { + + # Create some nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create some edges + link1 <- Link(source = nodeA, target = nodeB, color = 1, weight = 10) + link2 <- Link(source = nodeB, target = nodeC, color = 2, weight = 20) + link3 <- Link(source = nodeC, target = nodeA, color = 3, weight = 30) + + # Create a network + net <- Network(links = LinkList(c(link1, link2, link3)), nodes = NodeList(c(nodeA, nodeB, nodeC))) + + expect_equal(getNodes(net), NodeList(c(nodeA, nodeB, nodeC))) + expect_equal(getLinks(net), LinkList(c(link1, link2, link3))) + expect_equal(getLinkColorScheme(net), 'none') + + +}) + +test_that("We cannot make inappropriate networks", { + + # Create some nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + # Create links + link1 <- Link(source = nodeA, target = nodeB, color = 1, weight = 10) + link2 <- Link(source = nodeB, target = nodeC, color = 2, weight = 20) + + # Create a network with a node in links that isn't in nodes + expect_error(Network(links = LinkList(c(link1, link2)), nodes = NodeList(c(nodeB, nodeC)))) + + # Create a network with an invalid linkColorScheme + expect_error(Network(links = LinkList(c(link1, link2)), nodes = NodeList(c(nodeA, nodeB)), linkColorScheme = 'nope')) + +}) diff --git a/tests/testthat/test-nodes.R b/tests/testthat/test-nodes.R new file mode 100644 index 0000000..52bdf4d --- /dev/null +++ b/tests/testthat/test-nodes.R @@ -0,0 +1,114 @@ +test_that("NodeId works", { + expect_equal(class(NodeId('A'))[1], 'NodeId') +}) + +test_that("NodeIdList works", { + expect_equal(class(NodeIdList(list(NodeId('A'), NodeId('B'))))[1], 'NodeIdList') + expect_equal(class(NodeIdList(list('A', 'B')))[1], 'NodeIdList') + expect_equal(class(NodeIdList(list(Node(id=NodeId('A')))))[1], 'NodeIdList') + expect_equal(class(NodeIdList(Node(id=NodeId('A'))))[1], 'NodeIdList') + expect_equal(class(NodeIdList(list(Node(id=NodeId('A')), Node(id=NodeId('B')))))[1], 'NodeIdList') + + expect_error(NodeIdList(S4Vectors::SimpleList(c(NodeId('A'), 'B')))) + expect_error(NodeIdList(S4Vectors::SimpleList(c('A', 'B')))) +}) + +test_that("Node methods work", { + + # Create a node + nodeA <- Node( + id = NodeId('A') + ) + expect_equal(id(nodeA), 'A') + expect_equal(color(nodeA), NULL) + expect_equal(weight(nodeA), NULL) + + nodeB <- Node( + id = NodeId('B'), + color = 'red', + weight = 10 + ) + + expect_equal(id(nodeB), 'B') + expect_equal(color(nodeB), 'red') + expect_equal(weight(nodeB), 10) +}) + +test_that("NodeList methods work", { + + # Create some nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B') + ) + nodeC <- Node( + id = NodeId('C') + ) + + nodeList <- NodeList(S4Vectors::SimpleList(c(nodeA, nodeB, nodeC))) + expect_equal(length(nodeList), 3) + expect_equal(getNodeIds(nodeList), c('A', 'B', 'C')) + expect_equal(getWeights(nodeList), c(NULL, NULL, NULL)) + expect_equal(getColors(nodeList), c(NULL, NULL, NULL)) + + + # Create more interesting nodes + nodeA <- Node( + id = NodeId('A'), + color = 'red', + weight = 10 + ) + nodeB <- Node( + id = NodeId('B'), + color = 'blue', + weight = 20 + ) + nodeC <- Node( + id = NodeId('C'), + color = 'green', + weight = 30 + ) + + nodeList <- NodeList(S4Vectors::SimpleList(c(nodeA, nodeB, nodeC))) + expect_equal(length(nodeList), 3) + expect_equal(getNodeIds(nodeList), c('A', 'B', 'C')) + expect_equal(getWeights(nodeList), c(10, 20, 30)) + expect_equal(getColors(nodeList), c('red', 'blue', 'green')) + + + +}) + +test_that("We cannot make nonsensical nodes", { + + expect_error(Node(id = FALSE)) + expect_error(Node(id = 10)) + expect_error(Node(id = 'A', color = FALSE)) + expect_error(Node(id = 'A', weight = '10')) +}) + + +test_that("We cannot make nonsensical NodeLists", { + + # Create some nodes + nodeA <- Node( + id = NodeId('A') + ) + nodeB <- Node( + id = NodeId('B'), + color = 'red' + ) + + # If one node has a color, all must have colors + expect_error(NodeList(S4Vectors::SimpleList(c(nodeA, nodeB)))) + + # Nodes must have the same class of colors + color(nodeA) <- 1 + expect_error(NodeList(S4Vectors::SimpleList(c(nodeA, nodeB)))) + + # If one node has a weight, all much have weights + weight(nodeA) <- 100 + expect_error(NodeList(S4Vectors::SimpleList(c(nodeA, nodeB)))) +})