Featured image of post cytoscape

cytoscape

11 min read

lifecycle

A HTMLWidget wrapper for the Cytoscape.js graph / network visualisation and analysis library. There are already a number of existing R packages for Cytoscape such as the Bioconductor package and the official Cytoscape Widget. The purpose of this package is to make the process of producing a basic chart as simple as possible.

Installation

devtools::install_github('mrjoh3/cytoscape')

A Minimal Example

library(cytoscape)
nodes <- data.frame(id = c('a','b'))
edges <- data.frame(id = 'ab', source = 'a', target = 'b')
cytoscape(nodes = nodes, edges = edges) 

Styling Edges and Nodes

Nodes and edges can all be styled using CSS.

ct <- cytoscape(nodes = nodes, edges = edges)
ct %>% node_style('background-color' = '#ff0000')
ct %>% edge_style('line-color' = '#ff0000')

Creating data

Every network chart requires two data.frames one for the nodes and another for the edges. Often you will already have a data.frame with the edge, or source, target. In the example below we have a data.frame of Comtrade waste plastic exports for December 2017 where the source counrty and the destination country are deliniated. These fields need to be renamed to source and target for cytoscape to use it. For the nodes data.frame you need to create an id field that contains all of the unique values found in the source and target fields of the edges data.frame.

df <- cytoscape::comtrade
edges <- df %>%
    dplyr::select(source = reporter,
                  target = partner) %>%
    dplyr::mutate(id = paste(source, '_', target))
nodes <- data.frame(id = unique(c(edges$source, edges$target)), stringsAsFactors = FALSE)
           
cytoscape(nodes = nodes, edges = edges) %>% 
  layout('grid', rows = 4)

With Geographic Location

You can use coordinates to fix the location of nodes. But the package is not spatially enabled and does not check or transform the coordinates you pass.

coords <- cytoscape::coords %>%
  rename(x = lon,
         y = lat) %>%
  filter(id != 'World') %>%
  mutate(y = -y) # this will depend on coordinates used
edges <- filter(edges, 
                source %in% coords$id,
                target %in% coords$id)
cytoscape(nodes = coords, edges = edges) %>% 
  layout('preset') %>%
  node_style(width = 3, height = 3, 'font-size' = 8) %>%
  edge_style(width = 1)

Image Node Example

Replicate the Cytograph.js images example. This example does not currently include the touch effects.

img_nodes <- data.frame(id = c('cat','bird','ladybug','aphid','rose','grasshopper','plant','wheat'),
                        images = c('https://farm2.staticflickr.com/1261/1413379559_412a540d29_b.jpg',
                                   'https://farm8.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg',
                                   'https://farm4.staticflickr.com/3063/2751740612_af11fb090b_b.jpg',
                                   'https://farm9.staticflickr.com/8316/8003798443_32d01257c8_b.jpg',
                                   'https://farm6.staticflickr.com/5109/5817854163_eaccd688f5_b.jpg',
                                   'https://farm7.staticflickr.com/6098/6224655456_f4c3c98589_b.jpg',
                                   'https://farm1.staticflickr.com/231/524893064_f49a4d1d10_z.jpg',
                                   'https://farm3.staticflickr.com/2660/3715569167_7e978e8319_b.jpg'),
                        stringsAsFactors = FALSE)
img_edges <- data.frame(source = c('cat','bird','bird','grasshopper','grasshopper','ladybug','aphid'),
                        target = c('bird','ladybug','grasshopper','plant','wheat','aphid','rose'),
                        stringsAsFactors = FALSE)
cytoscape(nodes = img_nodes,
          edges = img_edges) %>%
  layout(name = 'breadthfirst',
         directed = TRUE,
         padding = 10) %>%
  node_style('height' = 80,
             'width' = 80,
             'background-fit' = 'cover',
             'border-color' = '#000',
             'border-width' = 3,
             'border-opacity' = 0.5,
             'label' = NULL) %>%
  edge_style('curve-style' = 'bezier',
             'width' = 6,
             'target-arrow-shape' = 'triangle',
             'line-color' = '#ffaaaa',
             'target-arrow-color' = '#ffaaaa') %>%
  node_images()

Node Gradient Background

Since cytoscape version 3.3.0 it has been possible to include background gradients in node styles. This is included in this package as of version 0.0.2.

cytoscape(nodes = img_nodes,
          edges = img_edges) %>%
  layout(name = 'breadthfirst',
         directed = TRUE,
         padding = 10) %>%
  node_style("content" = "data(id)",
              "text-valign" = "center",
              "text-halign" = "center",
              'height' = 80,
              'width' = 80,
              "background-fill" = "radial-gradient",
              'background-fit' = 'cover',
              "background-gradient-stop-colors" = "cyan magenta yellow", 
              "background-gradient-stop-positions" = "25 75 80"
             ) 

Grouping Nodes

Compound graphs where nodes are grouped are also possible. Add parent and node_color columns to the nodes data.frame. The final step is to add nodes for each of the groups, note the bind_rows() function in the example below. Thanks to gilhornung for this example.

df <- cytoscape::comtrade
gp_nodes <- tibble(id = unique(c(df$reporter, df$partner))) %>% 
  # Define node groups
  mutate(parent = ifelse(id %in% c("Nigeria", "Kenya", "South Africa"),
                         "Africa",
                         "Not Africa")) %>% 
  # Define node colors
  mutate(node_color = ifelse(parent == "Africa",
                             "forestgreen",
                             "darkorange")) %>%
  # Need to add the nodes for the two groups, Africa and "Not Africa"
  bind_rows(data_frame(id = c("Africa", "Not Africa"), 
                       node_color="whitesmoke"))
  
gp_edges <- df %>%
    dplyr::select(source = reporter,
                  target = partner) %>%
    dplyr::mutate(id = paste0(source, '-', target))
cytoscape(nodes = gp_nodes, edges = gp_edges) %>% 
  node_style('background-color' = 'data(node_color)') %>% 
  cola_layout(avoidOverlap = T)

Cytoscape-Cola Layout

Constraint based layouts provided through (cola.js) can be used via the cytoscape-cola plugin. All options available in the plugin API are available but have not yet been tested.

Basic Cola Layout

cytoscape(nodes = nodes, edges = edges) %>% 
  cola_layout()

Cola Node Allignment

To use the Cola alignment variable you need to pass a javascript object or function. You can do this using htmltools::JS().

aln_nodes <- data.frame(id = c('cat','bird','ladybug','grasshopper','wheat'),
                        stringsAsFactors = FALSE)
aln_edges <- data.frame(source = c('cat', 'bird',   'bird',       'grasshopper'),
                        target = c('bird','ladybug','grasshopper','wheat'      ),
                        stringsAsFactors = FALSE)
opts = htmlwidgets::JS('function( node ){ ',
                       'console.log(node._private.data.id);',
                       'if (node._private.data.id === "cat") {return { x: 0} }',
                       'else if (node._private.data.id === "bird") {return { x: 0, y: 0 } }',
                       'else if (node._private.data.id === "ladybug") {return { x: 0} }',
                       'else if (node._private.data.id === "wheat") {return {y: 0 } }',
                       'else if (node._private.data.id === "grasshopper") {return {y: 0 } }',
                       '}')
cytoscape(nodes = aln_nodes, edges = aln_edges) %>% 
  cola_layout(alignment = opts)

Shiny Example

A minimal shiny example can be run from the cytoscape package of from inst/shiny/minimum_shiny

shiny::runApp(system.file('shiny/minimum_shiny', package = 'cytoscape'))

Pass JSON Object

If you already have a complete JSON this can be passed through directly as a character string. The JSON character is parsed in javascript using JSON.parse() so it is good practice to first test your JSON is properly formed. On the R side you can use jsonlite::fromJSON() or the web-service http://json.parser.online.fr/ can sometimes give more meaningfull error messages.

json <- '{
"elements":[
    {
      "data": { "id": "a" } 
    },
    {
      "data": { "id": "b" }
    },
    {
      "data": { "id": "ab", "source": "a", "target": "b" }
    }
  ],
"style": [ 
  {
    "selector": "node",
    "style": {
      "background-color": "#666",
      "label": "data(id)"
    }
  },
  {
    "selector": "edge",
    "style": {
      "width": 3,
      "line-color": "#ccc",
      "target-arrow-color": "#ccc",
      "target-arrow-shape": "triangle"
    }
  }
],
"layout": {
  "name": "grid",
  "rows": 1
}
}'
cytoscape(json = json)
Built with Hugo
Theme Stack designed by Jimmy