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
}
Related
how can I create this in R?:
I can't seem to get the data.frame right in order to display the variables correctly.
It can be created step by step with "base R" functions. It can be done with data frames, but simple vectors will also do here.
create data sets for A and B
plot both with steps type="s"
optionally: suppress default axes and make your own, with labels from 0.95 to 1.00 only
add a legend
optionally: add the two hand-drawn red points
x <- seq(0.94, 1.0, 0.01)
A <- seq(0, 1, length.out = length(x))
x2 <- c(0.94, 1, 1.01)
B <- c(0, 1, 1)
plot(x, A, type = "s", xlim = c(0.94, 1.01), ylab = "F(x)", axes = FALSE)
lines(x2, B, type = "s", col = "blue")
axis(1, at=pretty(c(0.95, 1)))
axis(2)
box()
legend("topleft", lty=1,
legend = c("Lotterie A", "Lotterie B"),
col = c("black", "blue"))
points(c(1, 1), c(0, 1), col = "red")
Something like this?
df1 <- data.frame(x = rep(seq(0.95:1, by = 0.01), 2), cat = "A")
df1 <- df1[order(df1$x), ]
y <- c(rep(seq(0, 1, by = 1/6), 2))
df1$y <- sort(y)[2:13]
df2 <- data.frame(x = c(0, 1, 1, 1.5), y = c(0, 0, 1, 1), cat = "B")
df <- rbind(df1, df2)
library(ggplot2)
ggplot(df, aes(x, y, colour = cat)) +
geom_path() +
coord_cartesian(xlim = c(0.95, 1)) +
scale_y_continuous(breaks = seq(0, 1, by = 0.2))
I'm using a concatenated version of frankbi's Price of weed data (https://github.com/frankbi/price-of-weed for the original, https://github.com/Travis-Barton/Github_code for the cleaned version). I turn the histograms of HighQ/MedQ/Lowq into density lines. Then I try to plot those density lines on top of one another in a ggplot environment, but my axes are coming out all wrong.
This is what I should have:
This is what I end up getting:
Why is ggplot scaling my x-axis? Below is my code
dat <- marijuana.street.price.clean
hist(dat$HighQ)
hist(dat$MedQ)
hist(dat$LowQ)
plot(1, ylim = c(0, .02), xlim = c(0, 800))
lines(density(dat$HighQ), lwd = 3, col = 'green', lty = 2)
lines(density(dat$MedQ), lwd = 3, col = 'yellow', lty = 2)
lines(density(dat$LowQ, na.rm = T), lwd = 3, col = 'red', lty = 2)
dat2 <- data.frame(indexH = density(dat$LowQ, na.rm = T)$x,
propH = density(dat$HighQ)$y,
indexM = density(dat$MedQ)$x,
propM = density(dat$MedQ)$y,
indexL = density(dat$LowQ, na.rm = T)$x,
propL = density(dat$LowQ, na.rm = T)$y
)
ggplot(dat2) +
geom_line(aes(y = propH, x = indexH, colour = "High")) +
geom_line(aes(y = propL, x= indexL, colour = "Low")) +
geom_line(aes(y = propM, x = indexM, colour = "Medium"))
It looks like something simple I am missing but have no idea how to deal with this.
So I used a layout() function and I managed to get the layout as I wanted as below picture. Iris data was used in my coding.
Problem is, it does not show me the x label and y label on the output when I use plot() functions after this. And xaxis and yaxis for plot() looks overlapping. I am not sure how to deal with this problem.
There was no problem for x and y labelling before introducing plot.new() and par() to set up the main name of my diagram. (i.e. before I use the code from plot.new() to title(), xlab and ylab were shown)
I used 6 different plots in my original code, including, the plot.new() for title(), but I omitted the rest of them for convenience
Here is my code below,
x <- iris$Sepal.Length
y <- iris$Species
x_min <- min(iris$Sepal.Length)
x_max <- max(iris$Sepal.Length)
y_min <- min(iris$Sepal.Width)
y_max <- max(iris$Sepal.Width)
layout(matrix(c(1,1,1,1,1,1,
2,2,3,3,4,4,
5,5,5,6,6,6), nc=6, byrow = TRUE), heights=c(lcm(1),1,1,1,1))
layout.show(6)
par("mar"=c(1,1,1,1,1,1))
plot.new()
plot.window(xlim=c(0,1), ylim=c(0,1))
text(x=0.5,y=0.5,"scatter and density plots for Sepal and Length and Sepal Width" ,font=2, cex=1.5)
plot(...)
You can use the xlab and ylab arguments in title. However, the way you have constructed the plot means that when you reset par at the end, these are drawn off the page due ti their position relative to your custom axis. If you simply leave par alone, you get:
den1 = density(CDE1$V1)
den2 = density(CDE1$V2)
col1 = hsv(h = 0.65, s = 0.6, v = 0.8, alpha = 0.5)
col2 = hsv(h = 0.85, s = 0.6, v = 0.8, alpha = 0.5)
plot.new()
plot.window(xlim = c(25,65), ylim = c(0, 0.14))
axis(side = 1, pos = 0, at = seq(from = 25, to = 65, by = 5), col = "gray20",
lwd.ticks = 0.25, cex.axis = 1, col.axis = "gray20", lwd = 1.5)
axis(side = 2, pos = 25, at = seq(from = 0, to = 0.14, by = 0.02),
col = "gray20", las = 2, lwd.ticks = 0.5, cex.axis = 1,
col.axis = "gray20", lwd = 1.5)
polygon(den1$x, den1$y, col = col1, border ="black",lwd = 2)
polygon(den2$x, den2$y, col = col2, border ="black",lwd = 2)
text(52, 0.10, labels ="CDET", col =col1, cex = 1.25,font=2)
text(35, 0.03, labels ="SDFT", col =col2, cex = 1.25,font=2)
title(main = "Gestational Day 100/283",
xlab = "Fibril Diameter (nm)",
ylab = "density")
Of course, you could get a similar plot with less code and much easier adjustments using ggplot:
library(ggplot2)
ggplot(tidyr::pivot_longer(CDE1, 1:2), aes(value, fill = name)) +
geom_density() +
scale_fill_manual(values = c(col1, col2), labels = c("CDET", "SDFT")) +
scale_x_continuous(breaks = seq(25, 65, 5), limits = c(25, 65)) +
scale_y_continuous(breaks = seq(0, 0.14, 0.02), limits = c(0, 0.14)) +
theme_classic(base_size = 16) +
labs(title = "Gestational Day 100/283", x = "Fibril Diameter (nm)",
fill = NULL) +
theme(plot.title = element_text(hjust = 0.5))
Data used
Obviously, we don't have your data, so I had to create a reproducible approximation:
set.seed(123)
CDE1 <- data.frame(V1 = rnorm(20, 47.5, 4), V2 = rnorm(20, 44, 5))
Any tips to remove the zero labels in between the histogram bars?
hist(links$Survey_Duration, breaks = seq(0,50,5), main = "Survey Duration",
labels = TRUE, border = "black",
xlab = "Survey", ylim = c(0, 15), col = "gray", las = 1, xaxt='n')
axis(side=1, at=seq(0,50,5), labels=seq(0,50,5))
abline(v = mean(links$Survey_Duration), col = "royalblue", lwd = 1.5)
abline(v = median(links$Survey_Duration), col = "red", lwd = 1.5)
legend(x = "topright", c("Mean", "Median"), col = c("royalblue","red"),
lwd = c(1.5,1.5))
How about this?
# modify data so there's zero in one of the bins
mtcars$mpg <- ifelse(mtcars$mpg >= 25 & mtcars$mpg <= 30, NA, mtcars$mpg)
# save plot parameters
h <- hist(mtcars$mpg, plot = FALSE)
# produce plot
plot(h, ylim = c(0, 14))
# add labels manually, recoding zeros to nothing
text(h$mids, h$counts + 1, ifelse(h$counts == 0, "", h$counts))
A slightly different answer using the labeling in hist instead of adding text afterwards.
You do not provide your data, so I will use some data that is handy to illustrate.
The labels argument can specify the individual labels
H1 = hist(iris$Sepal.Length, breaks = 3:8, plot=FALSE)
BarLabels = H1$counts
BarLabels[BarLabels == 0] = ""
hist(iris$Sepal.Length, breaks = 3:8, labels = BarLabels)
Thanks #Daniel Anderson, it Ok now (Thumbs Up)
links$Survey_Duration <- ifelse(links$Survey_Duration > 15 &
links$Survey_Duration <= 25,
NA,
links$Survey_Duration)
h <- hist(links$Survey_Duration, breaks = seq(0,50,5), plot = FALSE)
plot(h, ylim = c(0, 14), main = "Survey Duration", xlab = "Time", col = "gray", las = 1)
text(h$mids, h$counts + 1, ifelse(h$counts == 0, "", h$counts))
axis(side=1, at=seq(0,50,5), labels=seq(0,50,5))
abline(v = mean(links$Survey_Duration), col = "royalblue", lwd = 1.5)
abline(v = median(links$Survey_Duration), col = "red", lwd = 1.5)
legend(x = "topright",
c("Mean", "Median"),
col = c("royalblue","red"),
lwd = c(1.5,1.5))
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))