Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to plot a figure with specific distance between each line

Tags:

r

Actually I try to plot a figure but it puts and shows all the columns(lines) on each other so it is not representative. I try to make a simulated data and show you how I plot it, and also show you what I want

I don't know how to make a data like example i show below but here what I do

set.seed(1)
M <- matrix(rnorm(20),20,5)
x <- as.matrix(sort(runif(20, 5.0, 7.5)))
df <- as.data.frame(cbind(x,M))

After making the data frame, I will plot all columns versus the first one by melting it and using ggplot

require(ggplot2)
require(reshape)
dff <- melt(df ,  id.vars = 'V1')
b <- ggplot(dff, aes(V1,value)) + geom_line(aes(colour = variable))

I want to have specific distance between each line (in this case we have 6) something like below. in one dimension it is V1, in another dimension it is the number of column. I don't care about the function , I just want the photo

like image 625
nik Avatar asked Apr 06 '16 10:04

nik


2 Answers

This solution uses rgl and produces this plot:

enter image description here

It uses this function that accepts 3 arguments:

  • df : a data.frame just like your 'M' above
  • x : a numeric vector (or a 1-coldata.frame`) for the x-axis
  • cols : (optionnal) a vector of colours to repeat. If missing, black line are drawn

Here is the function:

nik_plot <- function(df, x, cols){
  require(rgl)
  # if a data.frame is
  if (is.data.frame(x) && ncol(x)==1)
    x <- as.numeric(x[, 1])
  # prepare a vector of colors
  if (missing(cols))
    cols <- rep_len("#000000", nrow(df))
  else
    cols <- rep_len(cols, nrow(df))
  # initialize an empty 3D plot
  plot3d(NA, xlim=range(x), ylim=c(1, ncol(df)-1), zlim=range(df), xlab="Mass/Charge (M/Z)", ylab="Time", zlab="Ion Spectra", box=FALSE)
  # draw lines, silently
  silence_please <- sapply(1:ncol(df), function(i) lines3d(x=x, y=i, z=df[, i], col=cols[i])) 
}

Note that you can remove require(rgl) from the function and library(rgl) somewhere in your script, eg at the beginning.

If you don't have rgl installed, then install.packages("rgl").

Black lines, the default, may produce some moiré effect, but a repeating color palette is worse. This may be brain-dependant. A single colour would also avoid introducing an artificial dimension (and a strong one).

An example below:

# black lines
nik_plot(M, x)
# as in the image above
nik_plot(M, x, "grey40")
# an unreadable rainbow
nik_plot(M, x, rainbow(12))

The 3D window can be navigated with the mouse.

Do you need something else?


EDIT

You can build your second plot with the function below. The range of your data is so large, and I think the whole idea behind shifting upwards every line, prevent having an y-axis with a reliable scale. Here I have normalized all signals (0 <= signal <= 1). Also the parameter gap can be use to play with this. We could disconnect the two behaviors but I think it's nice. Try different values of gap and see examples below.

  • df : a data.frame just like your 'M' above
  • x : a numeric vector (or a 1-coldata.frame`) for the x-axis
  • cols : (optionnal) a vector of colours to repeat. If missing, black line are drawn
  • gap : gap factor between individual lines
  • more_gap_each: every n lines, a bigger gap is produced...
  • more_gap_relative: ... and will be gap x more_gap_relative wide

Here is the function:

nik_plot2D <- function(df, x, cols, gap=10, more_gap_each=1, more_gap_relative=0){
  if (is.data.frame(x) && ncol(x)==1)
    x <- as.numeric(x[, 1])

  # we normalize ( 0 <= signal <= 1)
  df <- df-min(df)
  df <- (df/max(df))
  # we prepare a vector of colors
  if (missing(cols))
    cols <- rep_len("#00000055", nrow(df))
  else
    cols <- rep_len(cols, nrow(df))
  # we prepare gap handling. there is probably more elegant
  gaps <- 1
  for (i in 2:ncol(df))
    gaps[i] <- gaps[i - 1] + 1/gap + ifelse((i %% more_gap_each) == 0, (1/gap)*more_gap_relative, 0)
  # we initialize the plot
  plot(NA, xlim=range(x), ylim=c(min(df), 1+max(gaps)), xlab="Time", ylab="", axes=FALSE, mar=rep(0, 4))
  axis(1)
  # finally, the lines
  silent <- lapply(1:ncol(df), function(i) lines(x, df[, i] + gaps[i], col=cols[i]))
}

We can use it with (default):

nik_plot2D(M, x) # gap=10

And you obtain this plot:

enter image description here

or:

nik_plot2D(M, x, 50)

enter image description here

or, with colors:

nik_plot2D(M, x, gap=20, cols=1:3)
nik_plot2D(M, x, gap=20, cols=rep(1:3, each=5))

or, still with colours and but with larger gaps:

nik_plot2D(M, x, gap=20, cols=terrain.colors(10), more_gap_each = 1, more_gap_relative = 0) # no gap by default
nik_plot2D(M, x, gap=20, cols=terrain.colors(10), more_gap_each = 10, more_gap_relative = 4) # large gaps every 10 lines
nik_plot2D(M, x, gap=20, cols=terrain.colors(10), more_gap_each = 5, more_gap_relative = 2) # small gaps every 5 lines

enter image description here

like image 79
Vincent Bonhomme Avatar answered Nov 18 '22 23:11

Vincent Bonhomme


As other have pointed out, your data have very large peaks and it's not clear whether you want to allow some curves to overlap,

enter image description here

m <- read.table("~/Downloads/M.txt", head=T)

fudge <- 0.05
shifty <- function(m, fudge=1){
  shifts <- fudge * max(abs(apply(m, 2, diff))) * seq(0, ncol(m)-1)
  m + matrix(shifts, nrow=nrow(m), ncol=ncol(m), byrow=TRUE)
}
par(mfrow=c(1,2), mar=c(0,0,1,0))
cols <- colorRampPalette(blues9[4:9])(ncol(m))
matplot(shifty(m), t="l", lty=1, bty="n", yaxt="n", xaxt="n", ylab="", col=cols)
title("no overlap")
matplot(shifty(m, 0.05), t="l", lty=1, bty="n", yaxt="n", xaxt="n", ylab="", col=cols)
title("some overlap")

Alternatively, some outlier/peak detection scheme could be used to filter them out before calculating the shift between curves,

library(outliers)

shifty2 <- function(m, outliers = 10){
  tmp <- m
  for(ii in seq_len(outliers)) tmp <- rm.outlier(tmp, median = TRUE)
  shifts <- max(abs(apply(tmp, 2, diff))) * seq(0, ncol(m)-1)
  m + matrix(shifts, nrow=nrow(m), ncol=ncol(m), byrow=TRUE)
}

matplot(shifty2(m), t="l", lty=1, bty="n", yaxt="n", xaxt="n", ylab="", col=cols)

enter image description here

(there are probably good algorithms to decide which points to remove, but I don't know them)

like image 43
baptiste Avatar answered Nov 19 '22 00:11

baptiste