How to plot three vectors/line charts on one figure? - r

How to draw one line chart with 3 lines in R?
min<-c(1,1,4,5)
max<-c(8,9,8,10)
d<-c(-2,3,4,3)

We can use matplot after cbinding the vectors to create a matrix
matplot(cbind(min, max, d), type='l')
To change the 'x axis' labels, we can plot with xaxt=n and change the labels with axis
matplot(cbind(min, max, d), type='l', xaxt='n', col=2:4)
axis(1, at=1:4, labels=letters[1:4])
legend('topright', legend=c('min', 'max', 'd'), col=2:4, pch=1)

Another solution to complete #akrun's very good answer, and based on this page:
require(ggplot2)
require(reshape2)
require(directlabels)
min <- c( 1, 1, 4, 5)
max <- c( 8, 9, 8, 10)
d <- c(-2, 3, 4, 3)
df <- data.frame(min=min, max=max, d=d, x=1:4)
df.m <- melt(df,id.vars="x")
p <- ggplot(df.m, aes(x=x, y=value, color=variable)) + geom_line()
direct.label(p)

Related

adding horizontal "separating" lines in a single boxplot in Base R plotting

I need to add a "separating" line in Base R boxplot to separate difference groups. In the example below, I want to separate groups A and B (each having 2 levels) using a horizontal line (in red). R codes for reproducible results:
dat = data.frame(A1 = rnorm(1000, 0, 1), A2 = rnorm(1000, 1, 2),
B1 = rnorm(1000, 0.5, 0.5), B2 = rnorm(1000, 1.5, 1.5))
boxplot(dat, horizontal = T, outline=F)
Is there an easy way to do in Base R?
Also, is there an easy way to color the y-axis labels? I want to have A1 and B1 shown as red, and A2 and B2 shown as blue in the axis.
Thanks!
Use abline. To get the right position take the mean of the axTicks of the y-axis.
To get the colored labels, first omit yaxt and rebuild axis ticks and mtext, also using axTicks.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n")
ats <- axTicks(2)
axis(2, labels=F)
mtext(b$names, 2, 1, col=c(2, 4), at=ats)
abline(h=mean(ats), lwd=2, col=2)
If you want axis tick label colors corresponding to the labels, use segments instead.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n")
ats <- axTicks(2)
abline(h=mean(ats), lwd=2, col=2)
pu <- par()$usr
Map(function(x, y) segments(pu[1] - .2, x, pu[1], x, xpd=T, col=y), ats, c(2, 4))
mtext(b$names, 2, 1, col=c(2, 4), at=ats)
Edit: To adjust the space a little more use at=option in boxplot and leave out the middle axTicks.
b <- boxplot(dat, horizontal=T, outline=F, yaxt="n", at=c(1, 2, 4, 5))
ats <- axTicks(2)[-3]
abline(h=mean(ats), lwd=2, col=2)
pu <- par()$usr
Map(function(x, y) segments(pu[1] - .2, x, pu[1], x, xpd=T, col=y), ats, c(2, 4))
mtext(b$names, 2, 1, col=c(2, 4), at=ats)

Calculate and plot multiple densities?

I have a matrix with multiple columns and I'd like do calculate the density of each column, and then plot those densities in one single R base plot. Also It would be easier if the plot had a corrected scale automatically.
m <- matrix(rnorm(10), 5, 10))
Create a list of densities d, compute the xlim and ylim values and use those to create an empty plot. Finally draw each of the densities on that plot and optionally draw a legend. As requested, this uses only base R.
set.seed(123)
m <- matrix(rnorm(50), 5, 10) # test data
d <- apply(m, 2, density)
xlim <- range(sapply(d, "[[", "x"))
ylim <- range(sapply(d, "[[", "y"))
plot(NA, xlim = xlim, ylim = ylim, ylab = "density")
nc <- ncol(m)
cols <- rainbow(nc)
for(i in 1:nc) lines(d[[i]], col = cols[i])
legend("topright", legend = 1:nc, lty = 1, col = cols, cex = 0.7)
It can also be done with ggplot2:
library(reshape2)
library(ggplot2)
#Data
set.seed(123)
m <- as.data.frame(matrix(rnorm(50), 5, 10))
#Melt
meltdata <- melt(m)
#Plot 1
ggplot(meltdata,aes(value,color=variable))+
geom_density()+ggtitle('Plot 1')
#Plot 2
ggplot(meltdata,aes(value,fill=variable))+
geom_density(alpha=0.6)+ggtitle('Plot 2')

Is it possible to add mathematical symbols in x-axis values?

Instead of adding mathematical symbols in x-labels, I'm trying to add $t_[1,n]$, $t_[2,n]$ and $t_[3,n]$ symbols at 0, 40 and 85 points in x-axis values, respectively. For doing so, my codes are
m=c(rnorm(40,0,.5),rnorm(45,5,.5));
plot(rep(1:85,1), m, type="l", lty=1, xaxt='n', yaxt='n',ann=FALSE, col=4);
windowsFonts(script=windowsFont("Script MT Bold"));
title(xlab=c(expression(t[1,n]), expression(t[2,n]), expression(t[3,n])), family="script");
Maybe try with ggplot2, like here:
library("ggplot2")
x <- 1:85
y <- c(rnorm(40,0,.5), rnorm(45,5,.5));
dane <- data.frame(x=x, y=y)
ggplot(dane, aes(x=x, y=y))+
geom_line()+
theme_bw()+
scale_x_discrete(breaks=c(1, 40, 85),
labels=c(expression(t[paste("[", 1, ",", n, "]")]),
expression(t[paste("[", 2, ",", n, "]")]),
expression(t[paste("[", 3, ",", n, "]")])))
Use axis instead of title.
axis(side = 1, at = c(0, 40, 85),
labels = c(expression(t["1,n"]),
expression(t["2,n"]),
expression(t["3,n"])))

Plot two time series with different y-axes: one as a dot plot (or a bar plot) and the other as a line

I have two time series of data, each with a different range of values. I would like to plot one as a dotplot and the other as a line over the dotplot. (I would settle for a decent-looking barplot and a line over the barplot, but my preference is a dotplot.)
#make some data
require(lubridate)
require(ggplot)
x1 <- sample(1990:2010, 10, replace=F)
x1 <- paste(x1, "-01-01", sep="")
x1 <- as.Date(x1)
y1 <- sample(1:10, 10, replace=T)
data1 <- cbind.data.frame(x1, y1)
year <- sample(1990:2010, 10, replace=F)
month <- sample(1:9, 10, replace=T)
day <- sample(1:28, 10, replace=T)
x2 <- paste(year, month, day, sep="-")
x2 <- as.Date(x2)
y2 <- sample(100:200, 10, replace=T)
data2 <- cbind.data.frame(x2, y2)
data2 <- data2[with(data2, order(x2)), ]
# frequency data for dot plot
x3 <- sample(1990:2010, 25, replace=T)
data2 <- as.data.frame(x3)
I can make a dotplot or barplot with one data set in ggplot:
ggplot() + geom_dotplot(data=data2, aes(x=x3))
ggplot() + geom_bar(data=data, aes(x=x1, y=y1), stat="identity")
But I can't overlay the second data set because ggplot doesn't permit a second y-axis.
I can't figure out how to plot a time series using barplot().
I can plot the first set of data as an "h" type plot, using plot(), and add the second set of data as a line, but I can't make the bars any thicker because each one corresponds to a single day over a stretch of many years, and I think it's ugly.
plot(data$x1, data$y1, type="h")
par(new = T)
plot(data2$x2, data2$y2, type="l", axes=F, xlab=NA, ylab=NA)
axis(side=4)
Any ideas? My only remaining idea is to make two separate plots and overlay them in a graphics program. :/
An easy workaround is to follow your base plotting instinct and beef up lwd for type='h'. Be sure to set lend=1 to prevent rounded lines:
par(mar=c(5, 4, 2, 5) + 0.1)
plot(data1, type='h', lwd=20, lend=1, las=1, xlab='Date', col='gray',
xlim=range(data1$x1, data2$x2))
par(new=TRUE)
plot(data2, axes=FALSE, type='o', pch=20, xlab='', ylab='', lwd=2,
xlim=range(data1$x1, data2$x2))
axis(4, las=1)
mtext('y2', 4, 3.5)
I removed the original answer.
To answer your question about making a dot plot, you can rearrange your data so that you can use the base plotting function. An example:
use the chron package for plotting:
library(chron)
dummy data:
count.data <- data.frame("dates" = c("1/27/2000", "3/27/2000", "6/27/2000", "10/27/2000"), "counts" = c(3, 10, 5, 1), stringsAsFactors = F)
replicate the dates in a list:
rep.dates <- sapply(1:nrow(count.data), function(x) rep(count.data$dates[x], count.data$counts[x]))
turn the counts into a sequence:
seq.counts <- sapply(1:nrow(count.data), function(x) seq(1, count.data$counts[x], 1))
plot it up:
plot(as.chron(rep.dates[[1]]), seq.counts[[1]], xlim = c(as.chron("1/1/2000"), as.chron("12/31/2000")),
ylim = c(0, 20), pch = 20, cex = 2)
for(i in 2:length(rep.dates)){
points(as.chron(rep.dates[[i]]), seq.counts[[i]], pch = 20, cex = 2)
}

Plot a function with several arguments in R

Suppose I want to plot an R function:
weibull <- function(ALPHA, LAMBDA, T){
ALPHA*LAMBDA*(T^(ALPHA-1))
}
So the function takes the arguments alpha, lambda and T. I want to generate a plot where in one plot alpha =0.5, time ranges from 0 to 2 and lambda=1, 2, 4, 8, 16 and in another, alpha=1, time ranges from 0 to 2 and lambda=1, 2, 4, 8, 16.
In the past for plotting functions with just one argument, I've used curve and then done ADD=TRUE if I wanted another curve on the same plot. So for instance, in the past I've used:
lambda <- 0.5
pdf <- function(x){
lambda*exp(-lambda*x)
}
survival <- function(x){
exp(-lambda*x)
}
plot(curve(pdf, 0, 6), type="l", ylim=c(0, 1), lwd=3, ylab="", xlab="", xaxs="i", yaxs="i", main=expression(paste("Exponential Distribution ", lambda, "=0.5")), cex.main=2, cex.axis=2, cex.lab=2)
curve(survival, 0, 6, add=TRUE, col="plum4", lwd=3)
But in this example the functions just have one argument, which is x. Whereas, now I want to vary LAMBDA, T and ALPHA. The curve function does not work and I am not sure how else to approach this.
If you use curve, you can specify an expression with a free variable x that will get replaced by the range of values specified in your from=/to= parameters. For example you can do
weibull <- function(ALPHA, LAMBDA, T){
ALPHA*LAMBDA*(T^(ALPHA-1))
}
lambda<-c(1, 2, 4, 8, 16)
col<-rainbow(length(lambda))
layout(matrix(1:2, nrow=1))
for(i in seq_along(lambda)) {
curve(weibull(.5, lambda[i], x), from=0, to=2, add=i!=1, col=col[i], ylim=c(0,50), main="alpha=.5")
}
legend(1,50,lambda, col=col, lty=1)
for(i in seq_along(lambda)) {
curve(weibull(1, lambda[i], x), from=0, to=2, add=i!=1, col=col[i], ylim=c(0,20), main="alpha=1")
}
which will produce a plot like
I'd do it with plyr and ggplot2,
weibull <- function(alpha, lambda, time){
data.frame(time = time, value = alpha*lambda*(time^(alpha-1)))
}
library(plyr)
library(ggplot2)
params <- expand.grid(lambda = c(1, 2, 4, 8, 16), alpha = c(0.5, 1))
all <- mdply(params, weibull, time = seq(0, 2, length=100))
ggplot(all, aes(time, value, colour=factor(lambda)))+
facet_wrap(~alpha,scales="free", ncol=2) + geom_line()
A tidyverse alternative,
weibull <- function(alpha, lambda, time){
data.frame(time = time, value = alpha*lambda*(time^(alpha-1)))
}
library(ggplot2)
library(tidyverse)
params <- tidyr::crossing(lambda = c(1, 2, 4, 8, 16), alpha = c(0.5, 1))
params %>%
dplyr::mutate(purrr::pmap(., .f = weibull, time = seq(0, 2, length=100))) %>%
tidyr::unnest() %>%
ggplot(aes(time, value, colour=factor(lambda)))+
facet_wrap(~alpha,scales="free", ncol=2) + geom_line()
This is similar to MrFlick's answer but shorter:
par(mfrow=1:2)
lapply(0:4, function(l) curve(weibull(0.5, 2^l, x), col=l+1, add=l!=0, ylim=c(0,50), xlim=c(0,2)))
lapply(0:4, function(l) curve(weibull(1, 2^l, x), col=l+1, add=l!=0, ylim=c(0,50), xlim=c(0,2)))
Ok if you're a big fan of nested lapply's you can also do:
lapply(c(0.5,1), function(a) lapply(0:4, function(l) curve(weibull(a, 2^l, x), col=l+1, add=l!=0, ylim=c(0,50), xlim=c(0,2))))

Resources