Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bipartite network graph with ggplot2

I have the following data frame:

structure(list(X1 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 4L, 4L, 
4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L
), .Label = c("1", "2", "3", "4", "5", "6"), class = "factor"), 
    X2 = structure(c(1L, 6L, 8L, 10L, 12L, 13L, 3L, 4L, 1L, 6L, 
    7L, 9L, 10L, 12L, 13L, 3L, 4L, 5L, 10L, 12L, 13L, 4L, 1L, 
    6L, 12L, 13L, 3L, 1L, 6L, 7L, 8L, 10L, 11L, 12L, 13L, 2L, 
    3L, 11L, 12L, 13L), .Label = c("I1", "I10", "I11", "I12", 
    "I13", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9"), class = "factor")), .Names = c("X1", 
"X2"), row.names = c(NA, -40L), class = "data.frame")

Where in X1 is the person number and in X2 the group to which the person belongs to. One person can be in different groups. Now I want to draw a line from each person to each group he belongs to. With plot() I solved it this way:

plot(0, xlim=c(0,1), ylim=c(0,1), type="n", axes=FALSE, xlab="", ylab="")

factor.to.int <- function(f) {
  (as.integer(f) - 1) / (length(levels(f)) - 1)
}

segments(factor.to.int(data$X1), 0, factor.to.int(data$X2), 1, col=data$X1)
axis(1, at = seq(0, 1, by = 1 / (length(levels(data$X1)) - 1)), labels = levels(data$X1))
axis(3, at = seq(0, 1, by = 1 / (length(levels(data$X2)) - 1)), labels = levels(data$X2))

The result looks like this: bipartite graph

Now I wondering how I can do this with ggplot2?

Thanks for your help!

like image 461
Dominik Avatar asked Nov 05 '12 16:11

Dominik


1 Answers

A simple ggplot2 version of your graph is possible by using geom_segment() and transforming the data much like you did for the base graphics version. I've also included an arguably more polished version, using some of the more advanced customization options in ggplot2.

# Using ggplot2 version 0.9.2.1
library(ggplot2)

dat$x1_norm = rangeTransform(as.integer(dat$X1))
dat$x2_norm = rangeTransform(as.integer(dat$X2))

dat$y1 = 0
dat$y2 = 1

# Simple version.
p1 = ggplot(dat, aes(x=x1_norm, xend=x2_norm, y=y1, yend=y2, colour=X1)) +
     geom_segment(size=1.2) +
     scale_colour_brewer(palette="Set1", name="Person")

ggsave(plot=p1, filename="plot_1.png", height=3.5, width=6)

enter image description here

# Fancy version.
# Create separate data.frames to manually specify axis ticks and axis text.
axis_1 = data.frame(x=rangeTransform(as.integer(unique(dat$X1))),
                    y=0, label=as.character(unique(dat$X1)))

axis_2 = data.frame(x=rangeTransform(as.integer(unique(dat$X2))),
                    y=1, label=as.character(unique(dat$X2)))

p2 = ggplot(data=dat) +
     theme_bw() +
     theme(axis.title=element_blank()) +
     theme(axis.text=element_blank()) +
     theme(axis.ticks=element_blank()) +
     theme(panel.grid=element_blank()) +
     geom_segment(aes(x=x1_norm, xend=x2_norm, y=y1, yend=y2, colour=X1),
                  size=1.2) +
     geom_segment(x=0, xend=1, y=0, yend=0, size=0.7) +
     geom_segment(x=0, xend=1, y=1, yend=1, size=0.7) +
     scale_colour_brewer(palette="Set1", name="Person") +
     scale_y_continuous(limits=c(-0.2, 1.2), expand=c(0, 0)) +
     geom_segment(data=axis_1, aes(x=x, xend=x, y=y, yend=y-0.025), size=0.7) +
     geom_segment(data=axis_2, aes(x=x, xend=x, y=y, yend=y+0.025), size=0.7) +
     geom_text(data=axis_1, aes(label=label, x=x, y=y - 0.075)) +
     geom_text(data=axis_2, aes(label=label, x=x, y=y + 0.075))

ggsave(plot=p2, filename="plot_2.png", height=3.5, width=6)

enter image description here

like image 157
bdemarest Avatar answered Sep 22 '22 13:09

bdemarest