Edgebundle with R – link colors based on network parameters

This post is based on a request my supervisor did, to make a network graph to use in a presentation. Idea was straightforward. Use Pearson correlation to calculate the relationship between every two variables and plot a network to visualize the relationship.
However, given that the number of variables in question is relatively low, I wanted to make sure that the visualization is of circos nature to make it more understandable.
Luckily, I got hold of an amazing library edgebundleR to draw an edgebundle network graph. However, to the best of my knowledge, changing the color of the link between two nodes are not straight forward if you need it to be based on an external variable other than the color of the originating node. However, I wanted the link color to represent the correlation between the nodes. So I followed a StackOverflow answer to achieve this. Here is the code below and I added comments on the way. Hope this helps.

library(RColorBrewer)
library(igraph)
library(edgebundleR)
library(Hmisc)

# Calculate Correlation.
# The data has missing values, this rcorr function omits those
# missing values to calculate the correlation.
X<-rcorr(as.matrix(Networks))
# Following section is done to extract the correlation values and normalize them to
# 0-1 range to use the full color scale. I have used a threshold of r > 0.35.
# This is completly up to the user to change as needed.
kr = X\$r
kr[kr < 0.35] <- 0
kr[kr == 1] <- 0
nr<-kr
nr<-(nr-0.35)/0.65
kr1000 <- nr*1000
kr1000 <- round(kr1000)
kr1000[kr1000<0]<-0
# Create the graph
## Optional: If you need some netwoek parameters.
V(g1)\$comm <- membership(optimal.community(g1))
V(g1)\$degree <- degree(g1)
V(g1)\$closeness <- centralization.closeness(g1)\$res
V(g1)\$betweenness <- centralization.betweenness(g1)\$res
V(g1)\$eigen <- centralization.evcent(g1)\$vector
# Create the edgebundle graph
edgebundle( g1, tension = 0.4 )->eb1
eb1
# Create the color pallet
pallet<-colorRampPalette(rev(brewer.pal(8, "Spectral")))
# Create a new color vector based on te lower traingle of the correlation matrix
cols<-pallet(1000)[kr1000[lower.tri(kr, diag = FALSE)]]
# Set colors in the edgebudle graph
E(g1)\$color <- cols
eb1\$x\$edges <- jsonlite::toJSON(get.data.frame(g1,what="edges"))
# This is the workaround to recolor the links on render. You can change the stroke styles
# to chnage their parameters here.
eb1 <- htmlwidgets::onRender(
eb1,
'
function(el,x){
// loop through each of our edges supplied
//  and change the color
x.edges.map(function(edge){
var source = edge.from;
var target = edge.to;
d3.select(el).select(".link.source-" + source + ".target-" + target)
.style("stroke",d3.rgb(edge.color));
d3.select(el).select(".link.source-" + source + ".target-" + target)
.style("stroke-opacity",0.7);
d3.select(el).select(".link.source-" + source + ".target-" + target)
.style("stroke-width",1.5);
})
}
'
)
## Thats it.
eb1

And the network will look like this.

3D slices using matplotlib and python.

This post is based on a figure I needed to make for a publication. I had to make a figure outlining a flow of data in an analysis. Data was extracted from PET images and was analyzed parallelly. I wanted to make a detailed enough figure that shows how the data is analyzed so I used ‘matplotlib’. The final figure had several different stages, but all stages were based on this image or the components of this image. So, if someone want to draw something similar, you can get help from the code I am posting.

Also, this post contains a python method directly taken from stack overflow. Thanks to the original author.
http://stackoverflow.com/a/31364297/480551

And of course, inputs/better implementations are welcome.

import numpy as np
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

def set_axes_equal(ax):
#Taken from http://stackoverflow.com/a/31364297/480551
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc..  This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.

Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''

x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()

x_range = x_limits[1] - x_limits[0]; x_mean = np.mean(x_limits)
y_range = y_limits[1] - y_limits[0]; y_mean = np.mean(y_limits)
z_range = z_limits[1] - z_limits[0]; z_mean = np.mean(z_limits)

# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.

def getFaceColors(colorsk, col):
'''This method return the color matrix for the surface. '''
(sx, sy, sc) = colorsk.shape
maxGridSize_d = sx
maxGridSize_h = sy
for i in range(sx):
for j in range(sy):
total = np.floor((np.floor(maxGridSize_h*1))-abs(np.floor(maxGridSize_h*0.6)-i)*maxGridSize_d/maxGridSize_h)
firstidx = np.floor((maxGridSize_h-total)/2)
lastidx = firstidx+total

if firstidx < j & j < lastidx:
if col == 'r':
colorsk[i, j, :] = np.array([np.random.random(size=1), 0, 0, 1], dtype=np.float64)
if col == 'g':
colorsk[i, j, :] = np.array([0, np.random.random(size=1), 0, 1], dtype=np.float64)
if col == 'b':
colorsk[i, j, :] = np.array([0, 0, np.random.random(size=1), 1], dtype=np.float64)

else:
colorsk[i, j, :] = np.array([1, 1, 1, 1], dtype=np.float64)
return colorsk

def drawImage(xsize, ysize, zsize):

'''This method draws the main figure. This method draws the meshes one

fig = pyplot.figure(figsize=(5, 5))

u = np.linspace(0, xsize, xsize + 1)
v = np.linspace(0, ysize, ysize + 1)

x, y = np.meshgrid(u, v)
z = np.ones_like(x)

# This section is commented as I only need x surfaces. But if someone need y and z surfaces, uncomment.
# for i in range(zsize + 1):
#     u = np.linspace(0, xsize, xsize + 1)
#     v = np.linspace(0, ysize, ysize + 1)
#     x, y = np.meshgrid(u, v)
#     z = np.ones((ysize + 1, xsize + 1), dtype=np.int)
#
#     # Draw surface on this grid. Included if needed to draw a structur with cubes.
#     surf = ax.plot_surface(x, y, z*i, rstride=1, cstride=1, color='w', alpha=0.7, shade=False)
#
# for i in range(ysize + 1):
#     u = np.linspace(0, xsize, xsize + 1)
#     v = np.linspace(0, zsize, zsize + 1)
#     x, z = np.meshgrid(u, v)
#     y = np.ones((zsize + 1, xsize + 1), dtype=np.int)
#
#     # Draw surface on this grid. Included if needed to draw a structur with cubes.
#     surf = ax.plot_surface(x, y*i, z, rstride=1, cstride=1, color='w', alpha=0.7, shade=False)

for i in range(xsize + 1):
u = np.linspace(0, zsize, zsize + 1)
v = np.linspace(0, ysize, ysize + 1)
z, y = np.meshgrid(u, v)
x = np.ones((ysize + 1, zsize + 1), dtype=np.int)
colors = np.ones((ysize, zsize, 4), np.float64)

# Draw surface on the grid.s
surf = ax.plot_surface(x * i, y, z, rstride=1, cstride=1, facecolors=getFaceColors(colors, 'r'), alpha=0.7,
surf.set_edgecolor('k')
surf = ax.plot_surface(x * i + (xsize + 5), y, z, rstride=1, cstride=1, facecolors=getFaceColors(colors, 'g'),
surf.set_edgecolor('k')
surf = ax.plot_surface(x * i + 2 * (xsize + 5), y, z, rstride=1, cstride=1,
surf.set_edgecolor('k')

ax.axis('off')
set_axes_equal(ax)
ax.view_init(elev=20, azim=-45)

# Modify these values for the align the plot on the figure.

if __name__ == '__main__':
drawImage(10, 25, 20)

Changing the colouring method, you can achieve multiple results. Changing the colouring method to

def getFaceColors (colorsk, col):
'''This method return the color matrix for the surface. '''
(sx, sy, sc) = colorsk.shape
maxGridSize_d = sx
maxGridSize_h = sy
for i in range(sx):
for j in range(sy):
total = np.floor((np.floor(maxGridSize_h*1))-abs(np.floor(maxGridSize_h*0.6)-i)*maxGridSize_d/maxGridSize_h)
firstidx = np.floor((maxGridSize_h-total)/2)
lastidx = firstidx+total

if col == 'r':
colorsk[i, j, :] = np.array([np.random.random(size=1), 0, 0, 1], dtype=np.float64)
if col == 'g':
colorsk[i, j, :] = np.array([0, np.random.random(size=1), 0, 1], dtype=np.float64)
if col == 'b':
colorsk[i, j, :] = np.array([0, 0, np.random.random(size=1), 1], dtype=np.float64)
return colorsk

will result in

Hope this helps.

The very first!!!

Here we are.. After several years of procrastinating, thought of writing. Well, writing may not be the correct word. I am not planning to write anything that will be interesting or useful to a general reader, but somethings I have figured out through my work. I believe this will be useful to people trying to solve the same problems.

Lets see how things go!!!. By the way, Welcome to the_Cardboard.