I'm plotting data with colored error bars in R. I'd like to show "sample error bars" (with the colour used in the plot) in the legend, but how?
library("Hmisc")
d1=data.frame(x=c(1,2,3,4,5), meanY=c(1,2,3,4,5), sdY=c(1,1,1,1,1))
d2=data.frame(x=c(1,2,3,4,5), meanY=c(2.1,3.3,4.1,5.2,6.1), sdY=c(1.3,1.2,1.4,1.1,1.2))
plot(1, 1, type="n", xlab="X values", ylab="Y values", xlim=c(1,5), ylim=c(0,7))
with ( data = d1, expr = Hmisc::errbar(x, meanY, meanY+sdY, meanY-sdY, pch=1, cex=.5, cap=.0025, add=T, errbar.col="red") )
with ( data = d2, expr = Hmisc::errbar(x, meanY, meanY+sdY, meanY-sdY, pch=1, cex=.5, cap=.0025, add=T, errbar.col="green") )
legend(x="bottomright", legend=c("d1", "d2"), pch=1, pt.cex=.5)
Somewhat manual build of legend...
# bind data together to simplify plot code
df <- rbind(d1, d2)
# plot
with(df,
errbar(x = x + c(rep(0.05, nrow(d1)), rep(-0.05, nrow(d2)), # dodge points to avoid overplotting
y = meanY,
yplus = meanY + sdY,
yminus = meanY - sdY,
pch = 1, cex = 0.5, cap = .0025,
errbar.col = rep(c("red", "green"), times = c(nrow(d1), nrow(d2))),
xlab = "X values", ylab = "Y values",
xlim = c(1, 5), ylim = c(0, 7)))
# create data for legend
df_legend <- data.frame(x <- c(4.5, 4.5),
y <- c(1, 2),
sdy <- c(0.3, 0.3))
# add symbols to legend
with(df_legend,
errbar(x = x,
y = y,
yplus = y + sdy,
yminus = y - sdy,
pch = 1, cex =.5, cap = .0025,
errbar.col = c("red", "green"),
add = TRUE))
# add text to legend
with(df_legend,
text(x = x + 0.2,
y = y,
labels = c("d2", "d1")))
# add box
with(df_legend,
rect(xleft = x - 0.2,
ybottom = y[1] - 0.5,
xright = x + 0.4,
ytop = y[2] + 0.5))
Related
I have a 2 x 2 figure where the columns represent two different variables and the rows represent two different locations. How can I add the names of the two locations to the two rows?
Example Data
library(biwavelet)
par(mfrow = c(2,2),
oma = c(3,3,0,0) + 0.1,
mar = c(1,1,1,1) + 0.1)
dat <- as.data.frame(matrix(nrow = 500, ncol = 2))
dat[1] <- seq(1,500)
dat[2] <- sin(dat[1])
# top-left figure
plot(wt(dat),
xaxt = 'n',
xlab = "",
cex.axis = 1.5,
cex.lab = 1.5)
axis(1, at = seq(0, 500, by = 100), cex.axis = 0.1, col.axis = 'NA')
title("Variable 1", line = 0.1, cex.main = 1.5)
# top-right figure
dat[2] <- sin(dat[1]*.5)
plot(wt(dat),
xaxt = 'n',
col.axis = 'NA',
xlab = "",
ylab = "")
axis(1, at = seq(0, 500, by = 100), cex.axis = 0.1, col.axis = 'NA')
title("Variable 2", line = 0.1, cex.main = 1.5)
# bottom-left figure
dat[2] <- sin(dat[1]*.25)
plot(wt(dat),
cex.axis = 1.5)
# bottom-right figure
dat[2] <- sin(dat[1]*.125)
plot(wt(dat),
col.axis = 'NA',
ylab = "",
xlab = "")
axis(1, at = seq(0, 500, by = 100), cex.axis = 1.5)
title(xlab = "Time (hours)",
ylab = "Period",
outer = TRUE,
line = 1.5,
cex.lab = 1.5)
The ideal figure would look like this
Neither of the suggested solutions so far have put the right-hand labels in the orientation requested. You cannot do so with mtext, but rather need to use text, first allowing text to display outside the plot region with par(xpd=NA). (See ?text and ?par, where one reads that the srt,rotation, parameter only applies to text):
par(mfrow=c(2,2), xpd=FALSE)
par(mar = c(1,1,1,1) + 2)
plot(disp ~ mpg, data = mtcars)
mtext("disp 1", side=3)
plot(I(2*disp) ~ mpg, data = mtcars)
mtext("disp 2", side=3); par(xpd=NA)
text("mpg 1", x=36, y=500, srt=270)
plot(disp ~ I(2*mpg), data = mtcars); par(xpd=NA)
plot(I(2*disp) ~ I(2*mpg), data = mtcars)
text("mpg 2", x=72, y=500, srt=270)
It is not as automatic as mtext in the sense that one needs to look at each plotting figure separately to derive a estimate for the x and y positions in the plotting coordinates.
Here's one method:
par(mfrow=c(2,2))
par(mar = c(1,1,1,1) + 0.1)
plot(disp ~ mpg, data = mtcars)
mtext("disp 1", side=3)
plot(I(2*disp) ~ mpg, data = mtcars)
mtext("disp 2", side=3)
mtext("mpg 1", side=4)
plot(disp ~ I(2*mpg), data = mtcars)
plot(I(2*disp) ~ I(2*mpg), data = mtcars)
mtext("mpg 2", side=4)
Unfortunately, mtext does not support rotating text, so you're stuck with the right labels being oriented as they are.
In case anyone stumbles across this page, the code I ended up using was based on #IRTFM answer, see below. It was challenging finding the appropriate y = in the text() function. I used an iterative approach and found the y = to be much lower than I anticipated.
library(biwavelet)
par(mfrow = c(2,2), xpd=F)
par(mar = c(1,1,1.1,1.1) + 0.1,
oma = c(3,3,0.5,0.5) + 0.1)
dat <- as.data.frame(matrix(nrow = 500, ncol = 2))
dat[1] <- seq(1,500)
dat[2] <- sin(dat[1])
# top-left figure
plot(wt(dat),
xaxt = 'n',
xlab = "",
cex.axis = 1.5,
cex.lab = 1.5)
axis(1, at = seq(0, 500, by = 100), cex.axis = 1.5, col.axis = 'NA')
mtext("Variable 1", side = 3, cex = 1.5, line = 0.1)
box(lty = "solid", col = 'black')
# top-right figure
dat[2] <- sin(dat[1]*.5)
plot(wt(dat),
xaxt = 'n',
col.axis = 'NA',
xlab = "",
ylab = "")
axis(1, at = seq(100, 500, by = 100), cex.axis = 0.1, col.axis = 'NA')
mtext("Variable 2", side = 3, cex = 1.5, line = 0.1)
text("Location 1", x = 520, y = 4.1, srt = 270, cex = 1.5, xpd=NA)
box(lty = "solid", col = 'black')
# bottom-left figure
dat[2] <- sin(dat[1]*.25)
plot(wt(dat),
cex.axis = 1.5,
xlab = "",
ylab = "")
axis(1, at = seq(100, 500, by = 100), cex.axis = 1.5)
box(lty = "solid", col = 'black')
# bottom-right figure
dat[2] <- sin(dat[1]*.125)
plot(wt(dat),
col.axis = 'NA',
ylab = "",
xlab = "")
axis(1, at = seq(100, 500, by = 100), cex.axis = 1.5)
text("Location 2", x = 520, y = 4.5, srt = 270, cex = 1.5, xpd=NA)
box(lty = "solid", col = 'black')
title(xlab = "Time (hours)",
ylab = "Period",
outer = TRUE,
line = 1.5,
cex.lab = 1.5)
I have a data frame with 3 columns:
A date time column
water level of a pond over 2 years (hourly)
daily precipitation over 2 years (daily)
I want to plot the date time on the x axis and the other two as two separate y axis.
I've tried with ggplot2, but this seems like quite a tricky thing to do. Does anyone know of any solutions or of any other methods.
Thankyou.
This is my current code. I don't know how to add in the data for a second axis however. (It is in a for loop as I have multiple locations within the pond. They all have the same data layout.)
scaleFUN <- function(x) sprintf("%.2f", x)
plotlist_mAODdate <- list()
j = 1 # counter for plot title and index
for (datTime in mAODdata){
plotName <- names(mAODdata)[j]
j = j+1
plot <-
datTime %>%
ggplot() +
geom_point(aes_string(x='DateTime', y='Rel_mAOD'), col='grey') +
geom_smooth(aes_string(x='DateTime', y='Rel_mAOD')) +
theme_classic() +
labs(y='Water Depth (mAOD)', x=NULL) +
ggtitle(plotTitles[[plotName]][1]) +
scale_x_datetime(
breaks=seq(min(datTime$DateTime), max(datTime$DateTime),
by= "6 months"), date_labels="%b-%y") +
scale_y_continuous(labels=scaleFUN) +
geom_vline(xintercept=as.POSIXct('2020-11-03 01:00:00'), col='red') +
geom_vline(xintercept=as.POSIXct('2021-11-01 01:00:00'), col='red', linetype='dashed') +
theme(text=element_text(size=20, family='Calibri Light')) +
theme(plot.margin = unit(c(1, 1, 1, 1), 'cm')) +
theme(axis.title.y=element_text(margin=margin(t=0, r=20, b=0, l=0))) +
theme(axis.title.x=element_text(margin=margin(t=20, r=0, b=0, l=0)))
plotlist_mAODdate[[plotName]] <- plot
}
Since you mentioned other methods, you can do this in base R by:
Data
df <- data.frame(date = seq(as.Date('2021-01-01'),as.Date('2022-12-31'), by = 1),
water = 250 + 1:730*2,
precipitation = 0 + (1:730)^2)
Code
par(mar = c(5.1, 4.1, 4.1, 4.1))
plot(x = df$date, y = df$water, type = "l", col = "red", bty = "n", ylab = "", xlab = "")
par(new = TRUE)
plot(x = df$date, y = df$precipitation, type = "l", col = "blue", axes = FALSE, xlab = "", ylab = "")
axis(side = 4)
mtext("Year", side = 1, padj = 4)
mtext("Water", side = 2, padj = -4)
mtext("Precipitation", side = 4,, padj = 4, srt = -90)
legend("topleft", c("Water","Precipitation"), lty = 1, col = c("red","blue"), bty = "n")
Output
Multiple locations
If you have multiple locations, you can do:
Data
set.seed(123)
df2 <- data.frame(date = seq(as.Date('2021-01-01'),as.Date('2022-12-31'), by = 1),
water = 250 + 1:730*2,
precipitation = 0 + (1:730)^2,
location = sample(LETTERS[1:4], 730, replace = TRUE))
Code
par(mfrow = c(floor(length(unique(df2$location))/2), 2), mar = c(5.1, 4.1, 4.1, 4.1))
for(i in sort(unique(df2$location))){
plot(x = df2[df2$location == i, "date"], y = df2[df2$location == i, "water"], type = "l", col = "red", bty = "n", ylab = "", xlab = "")
par(new = TRUE)
plot(x = df2[df2$location == i, "date"], y = df2[df2$location == i, "precipitation"], type = "l", col = "blue", axes = FALSE, xlab = "", ylab = "")
axis(side = 4)
mtext(paste0("Location: ", i), side = 3, adj = 0)
mtext("Year", side = 1, padj = 4)
mtext("Water", side = 2, padj = -4)
mtext("Precipitation", side = 4,, padj = 4, srt = -90)
legend("topleft", c("Water","Precipitation"), lty = 1, col = c("red","blue"), bty = "n")
}
Output
I used the sec.axis method with
geom_point(aes(y=(prcp_amt/150)+72), col='blue')
and
scale_y_continuous(sec.axis = sec_axis(~(.-72)*150, name='Precipitation (mm)')
and rescaling the data, so they both fit on the same scale. I know this isn't the best method, but I needed the rest of the ggplot2 to make the appearance of the graphs. This is how it's incorporated into the full code.
scaleFUN <- function(x) sprintf("%.2f", x)
plotlist_mAODdate <- list()
j = 1 # counter for plot title and index
for (datTime in mAODdata){
plotName <- names(mAODdata)[j]
j = j+1
plot <-
datTime %>%
ggplot(aes(x=DateTime)) +
geom_point(aes(y=Rel_mAOD), col='grey') +
geom_smooth(aes(y=Rel_mAOD), col='black') +
geom_point(aes(y=(prcp_amt/150)+72), col='blue') +
theme_classic() +
labs(y='Water Depth (mAOD)', x=NULL) +
ggtitle(plotTitles[[plotName]][1]) +
scale_x_datetime(
breaks=seq(min(datTime$DateTime), max(datTime$DateTime),
by= "6 months"), date_labels="%b-%y") +
scale_y_continuous(labels=scaleFUN, sec.axis = sec_axis(~(.-72)*150, name='Precipitation (mm)')) +
geom_vline(xintercept=as.POSIXct('2020-11-03 01:00:00'), col='red') +
geom_vline(xintercept=as.POSIXct('2021-11-01 01:00:00'), col='red', linetype='dashed') +
theme(text=element_text(size=20, family='Calibri Light')) +
theme(plot.margin = unit(c(1, 1, 1, 1), 'cm')) +
theme(axis.title.y=element_text(margin=margin(t=0, r=20, b=0, l=0))) +
theme(axis.title.x=element_text(margin=margin(t=20, r=0, b=0, l=0)))
plotlist_mAODdate[[plotName]] <- plot
}
I want to plot density lines without showing the histogram, I used this code:
hist(www, prob=TRUE, xlab = "X", main = "Plot",xlim=c(0,11), ylim=c(0,1), breaks =100)
lines(density(x, adjust=5), col="red", lwd=2)
lines(density(y, adjust=5), col="blue", lwd=2)
lines(density(z, adjust=5), col="green", lwd=2)
And the result is showing in the the picture.
How can I remove the Histogram? Thank you in advance!
You could use plot(density(...)) instead of hist:
set.seed(123)
x <- rnorm(100, 0, 1)
y <- rnorm(100, 0.5, 2)
z <- rnorm(100, 1, 1)
dens <- lapply(list(x=x, y=y, z=z), density)
ran <- apply(do.call(rbind, sapply(dens, function(i) list(data.frame(x=range(i$x), y=range(i$y))))), 2, range)
plot(dens[[1]], xlim=ran[,1], ylim=ran[,2], type = 'n', main="Density")
lapply(seq_along(dens), function(i) lines(dens[[i]], col=i))
legend("topright", names(dens), col=seq_along(dens), lty=1)
Created on 2021-01-31 by the reprex package (v1.0.0)
Even easier is plotting with the ggplot2 package:
library(ggplot2)
dat <-data.frame(group=unlist(lapply(c("x", "y", "z"), function(i) rep(i, length(get(i))))),
value=c(x, y, z))
ggplot(dat, aes(x=value, colour=group))+
geom_density()
Using three toy vectors, try this:
x <- rnorm(100, 0, 1)
y <- rnorm(100, 0.5, 2)
z <- rnorm(100, 1, 1)
plot(density(x, adjust = 5), col = "red", lwd = 2,
xlim = c(-20, 20), ylim = c(0, 0.25), xlab = "X")
par(new=T)
plot(density(y, adjust = 5), col = "blue", lwd = 2,
xlim = c(-20, 20), ylim = c(0, 0.25), xlab = "")
par(new=T)
plot(density(z, adjust = 5), col = "green", lwd = 2,
xlim = c(-20, 20), ylim = c(0, 0.25), xlab = "")
You will need to adjust xlim and ylim in the right way
I am trying to draw multiple plots in the same graph in R (Rstudio, mac), using:
plot(
X,
Y1,
pch = 0,
ylim = c(min_v, max_v),
col = "red"
)
par(new = T)
plot(
X,
Y2,
pch = 1,
ylim = c(min_v, max_v),
col = "blue"
)
par(new = F)
plot(
X,
Y3,
pch = 2,
ylim = c(min_v, max_v),
col = "green"
)
par(new = F)
However, it draws only the third plot.
what am I missing?
points
I think it is best to use points for the last two instead of plot.
min_v <- min(Y1, Y2, Y3)
max_v <- max(Y1, Y2, Y3)
xr <- range(X)
plot(X, Y1, pch = 21, ylim = c(min_v, max_v),
xlim = xr, bg = "red", ,
ylab = expression(paste(Y[i],', i = {1, 2, 3}')), xlab ="X")
points(X, Y2, pch = 22, bg = "blue")
points(X, Y3, pch = 23, bg = "green")
plot
If the OP really wants to use the plot functions, then the following could be useful. (The main error OP made is using the second new=F, but there will be other problems as well since the y-axis labels being on top of each other etc.)
plot(
X, Y1, pch = 21, ylim = c(min_v, max_v),
xlim = xr,
bg = "red",
ylab = "", xlab ="",
)
par(new = T)
plot(
X, Y2, pch = 22, ylim = c(min_v, max_v),
xlim = xr,
bg = "blue",
ylab = "", xlab ="",
)
par(new = T)
plot(
X, Y3, pch = 23, ylim = c(min_v, max_v),
xlim = xr,
bg = "green",
ylab = expression(paste(Y[i],', i = {1, 2, 3}')),
xlab ="X",
)
par(new = F)
ggplot2
While I'm at it, here's ggplot2 version of it too.
library(ggplot2)
df <- data.frame(X=X, Y1=Y1, Y2=Y2, Y3=Y3)
p1 <- ggplot(df, aes(x = X, y=Y1)) + geom_point(color = "red")
p1 <- p1 + geom_point(color = "blue", aes(y=Y2))
p1 <- p1 + geom_point(color = "black", aes(y=Y3))
p1 + xlab("X") + ylab("Y")
p1
Data used:
set.seed(1984)
X <- rnorm(10)
Y1 <- rnorm(10)
Y2 <- rnorm(10)
Y3 <- rnorm(10)
I have been practicing the graphs and how to plot in R with the following code
theta = 1:100
x = sin(theta)
y = cos(theta)
op = par(bg = 'white', mar = rep(1, 4))
plot.new()
plot(x,y)
plot.window(xlim = c(-1, 1), ylim = c(-1, 1))
lines(x, y, col = hsv(0.95, 1, 1))
to get the following output
Now I wanted to trace just how exactly the lines connect to form this pattern and so used the following code.
theta = 1:3
x = sin(theta)
y = cos(theta)
op = par(bg = 'white', mar = rep(1, 4))
plot(x,y, xlab = "Sin", ylab = "Cos", type = "p")
plot.window(xlim = c(-1, 1), ylim = c(-1, 1))
lines(x, y, col = hsv(0.95, 1, 1))
And I get the following output
Shouldn't the lines connect the dots? I get the output where the lines connect the dots using this code
theta = 1:3
x = sin(theta)
y = cos(theta)
op = par(bg = 'white', mar = rep(1, 4))
plot(x,y, xlab = "Sin", ylab = "Cos", type = "l")
And if I add the points later, they don't work as well.
theta = 1:3
x = sin(theta)
y = cos(theta)
op = par(bg = 'white', mar = rep(1, 4))
plot(x,y, xlab = "Sin", ylab = "Cos", type = "l")
plot.window(xlim = c(-1, 1), ylim = c(-1, 1))
points(x, y)
Here is the output.
Why is there such difference in output?
You are setting plot.window limits after you have plotted your points plot(...) and before plotting your lines lines(...) causing the mismatch. Try the following:
theta = 1:3
x = sin(theta)
y = cos(theta)
op = par(bg = 'white', mar = rep(1, 4))
plot.window(xlim = c(-1, 1), ylim = c(-1, 1))
plot(x,y, xlab = "Sin", ylab = "Cos", type = "p")
lines(x, y, col = hsv(0.95, 1, 1))