Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

plotting modified point and line plot - variability as "spike" plot in r

Tags:

graph

r

ggplot2

Before explaining details, here is my data:

set.seed (1234) 
datas <- data.frame (Indv = 1:20, Xvar = rnorm (20, 50, 10),
Yvar = rnorm (20, 30,5), Yvar1 = rnorm (20, 10, 2),
Yvar2 = rnorm (20, 5, 1), Yvar3 = rnorm (20, 100, 20),
Yvar4 = rnorm (20, 15, 3))

I want to prepare a graph (Metroglymph ) which is essentially point plot however points (of Xvar and Yvar) with spikes (lines) orignated from the point scaled to rest of variables (Yvar1, Yvar2, Yvar3, Yvar4). Each spike are ordered and preferably color coded.

require(ggplot2)
ggplot(datas, aes(x=Xvar, y=Yvar)) +
    geom_point(shape=1, size = 10) + theme_bw()

enter image description here

like image 902
jon Avatar asked Dec 26 '22 21:12

jon


2 Answers

Here is one possible approach that may be helpful to you. It uses stat_spoke() from ggplot2. Each of your y-variables is mapped to the spoke radius in 4 separate calls to stat_spoke.

plot_1 = ggplot(datas, aes(x=Xvar, y=Yvar)) +
         stat_spoke(aes(angle=(1/8)*pi, radius=Yvar1), colour="#E41A1C",size=1) +
         stat_spoke(aes(angle=(3/8)*pi, radius=Yvar2), colour="#377EB8",size=1) +
         stat_spoke(aes(angle=(5/8)*pi, radius=Yvar3), colour="#4DAF4A",size=1) +
         stat_spoke(aes(angle=(7/8)*pi, radius=Yvar4), colour="#984EA3",size=1) +
         geom_point(shape=1, size = 10)

ggsave("plot_1.png", plot_1)

enter image description here

Depending on your data and specific needs, it may make sense to transform the variables so they fit better on the plot.

normalize = function(x) {
    new_x = (x - mean(x)) / sd(x)
    new_x = new_x + abs(min(new_x))
    return(new_x)
}

plot_2 = ggplot(datas, aes(x=Xvar, y=Yvar)) +
         stat_spoke(aes(angle=(1/8)*pi, radius=normalize(Yvar1)), colour="#E41A1C", size=1) +
         stat_spoke(aes(angle=(3/8)*pi, radius=normalize(Yvar2)), colour="#377EB8", size=1) +
         stat_spoke(aes(angle=(5/8)*pi, radius=normalize(Yvar3)), colour="#4DAF4A", size=1) +
         stat_spoke(aes(angle=(7/8)*pi, radius=normalize(Yvar4)), colour="#984EA3", size=1) +
         geom_point(shape=1, size = 10)

ggsave("plot_2.png", plot_2)

enter image description here

Important caveat: For the same spoke radius value, the magnitude of the plotted line will be greater if the line is more vertical, and less if the line is more horizontal. This is because the range of x is around twice the range of y for your data set. The plotted angles also become distorted as the x-to-y axis ratio changes. Adding coord_equal(ratio=1) solves this issue, but may introduce other problems. enter image description here

Edit: Plotting without a loop

This was fun and educational to figure out. Possibly it would have been more time-efficient to type the repetitive code! If anyone can offer advice to improve this code, please comment.

library(reshape2)

dat2 = melt(datas, id.vars=c("Indv", "Xvar", "Yvar"), 
            variable.name="spoke_var", value.name="spoke_value")

# Apply normalization in a loop. Can plyr do this more gracefully?.
for (var_name in levels(dat2$spoke_var)) {
    select_rows = dat2$spoke_var == var_name
    norm_dat = normalize(dat2[select_rows, "spoke_value"])
    dat2[select_rows, "spoke_value"] = norm_dat
}

# Pick an angle for each Yvar, then add angle column to dat2.
tmp = data.frame(spoke_var=unique(dat2$spoke_var))
tmp$spoke_angle = seq(from=pi/8, by=pi/4, length.out=nrow(tmp))
dat2 = merge(dat2, tmp)

plot_4 = ggplot(dat2, aes(x=Xvar, y=Yvar)) +
         stat_spoke(data=dat2, size=1,
                    aes(colour=spoke_var, angle=spoke_angle, radius=spoke_value)) +
         geom_point(data=datas, aes(x=Xvar, y=Yvar), shape=1, size=7) +
         coord_equal(ratio=1) +
         scale_colour_brewer(palette="Set1")
like image 61
bdemarest Avatar answered Jan 21 '23 03:01

bdemarest


Here is more manual approach:

set.seed (1234)
datas <- data.frame (Indv = 1:20, Xvar = rnorm (20, 50, 10),
Yvar = rnorm (20, 30,5), Yvar1 = rnorm (20, 10, 2),
Yvar2 = rnorm (20, 5, 1), Yvar3 = rnorm (20, 100, 20),
Yvar4 = rnorm (20, 15, 3))
datas$SYvar1 <- 2 + scale (datas$Yvar1)
datas$SYvar2 <- 2 +  scale (datas$Yvar2)
datas$SYvar3 <- 2 + scale (datas$Yvar3)
datas$SYvar4 <- 2 +  scale (datas$Yvar4)

    require(ggplot2)
    p <- ggplot(datas, aes(x=Xvar, y=Yvar)) +  
   geom_point(size = 10, pch = 19, col = "yellow2")
    p + geom_segment(aes(x = Xvar, y = Yvar, xend = Xvar + SYvar1, 
   yend = Yvar), col = "red4", size = 1) +
    geom_segment(aes(x = Xvar, y = Yvar, xend = Xvar, 
    yend = Yvar + SYvar2), col = "green4", size = 1) +
    geom_segment(aes(x = Xvar, y = Yvar, xend = Xvar-2.5,
    yend = Yvar + SYvar3), col = "darkblue", size = 1) +
    geom_segment(aes(x = Xvar, y = Yvar, xend = 
     Xvar - SYvar4, yend = Yvar ), col = "red", size = 1) +
    theme_bw()

enter image description here

like image 21
SHRram Avatar answered Jan 21 '23 04:01

SHRram