Overlay three curves in the same y axis - r

I want to overlay three curves of the normal distribution with three different sigma values. I have done this:
curve(dnorm(x, mean(x), sd(x)), col = 2, lwd = 2)
par(new = TRUE)
curve(dnorm(x, mean(x), sd(x)/2), col = 3, lwd = 2)
par(new = TRUE)
curve(dnorm(x, mean(x), sd(x)*2), col = 4, lwd = 2)
And the result is
But I want to use only one y axis scale, so the three plots are one under the other and not on the same y scale. Also I want to expand the range of the x axis, to be from -3 to 3 for example

Open a blank graphics device first by setting plot parameter type = "n". Then add = TRUE the 3 curves.
plot(c(0, 1), c(0, 3), type = "n", xlab = "", ylab = "")
curve(dnorm(x, mean(x), sd(x)), col = 2, lwd = 2, add = TRUE)
curve(dnorm(x, mean(x), sd(x)/2), col = 3, lwd = 2, add = TRUE)
curve(dnorm(x, mean(x), sd(x)*2), col = 4, lwd = 2, add = TRUE)

You can use yaxt and xaxt to remove the y axis from the graph.
curve(dnorm(x, mean(x), sd(x)), col = 2, lwd = 2,ylab="My y axis name")
par(new = TRUE)
curve(dnorm(x, mean(x), sd(x)/2), col = 3, lwd = 2,yaxt='n',xaxt='n',ylab="")
par(new = TRUE)
curve(dnorm(x, mean(x), sd(x)*2), col = 4, lwd = 2,yaxt='n',xaxt='n',ylab="")
gives,

Related

How to know if two lines intersect using r? is there an equation to check easily without looking at graph?

This code gets the data needed to make two different lines. I was wondering if there was a way to see if two lines intersected easily.
# generate data
red <- matrix(runif(n = 4, min = 0, max = 1), nrow = 2)# gets the 4 points for the first line
blue <- matrix(runif(n = 4, min = 0, max = 1), nrow = 2)# gets the 4 points for second line
# make a plot
plot(red, col = "red", pch = 16, cex = 2,
asp = 1, xlim = c(0,1), ylim = c(0,1),
xlab = "", ylab = "")##plots both points red
abline(v = c(0,1), col = "grey", lty = 2)
abline(h = c(0,1), col = "grey", lty = 2)
segments(red[1,1], red[1,2], red[2,1], red[2,2], lwd = 2, col = "red")#Makes the line segment
points(blue, col = "blue", pch = 16, cex = 2,
asp = 1, xlim = c(0,1), ylim = c(0,1))# does same thing for blue line
segments(blue[1,1], blue[1,2], blue[2,1], blue[2,2], lwd = 2, col = "blue")
##makes all of the plots and can see if the plot intersects.`
Here's a practical answer using simple algebra wrapped up in a function.
The process is to find the slope and y intercept of both lines, and solve simultaneous equations to find the intersection. If both line segments have the same gradient the answer is undefined so return NA.
Return the x, y co-ordinates of the intersection if it is within the x range of one of the line segments, otherwise return NA
check_intersect <- function(mat1, mat2)
{
dy1 <- mat1[,2][which.max(mat1[,1])] - mat1[,2][which.min(mat1[,1])]
dy2 <- mat2[,2][which.max(mat2[,1])] - mat2[,2][which.min(mat2[,1])]
dx1 <- max(mat1[,1]) - min(mat1[,1])
dx2 <- max(mat2[,1]) - min(mat2[,1])
m1 <- dy1/dx1
m2 <- dy2/dx2
if(m1 == m2) return(NA)
c1 <- mat1[1, 2] - m1 * mat1[1, 1]
c2 <- mat2[1, 2] - m2 * mat2[1, 1]
x <- (c2 - c1)/(m1 - m2)
y <- m1 * x + c1
if(x > min(mat1[,1]) & x < max(mat1[,1]))
return(c(x, y))
else
return(NA)
}
Now test this with a reprex:
set.seed(123)
red <- matrix(runif(n = 4, min = 0, max = 1), nrow = 2)# gets the 4 points for the first line
blue <- matrix(runif(n = 4, min = 0, max = 1), nrow = 2)# gets the 4 points for second line
# make a plot
plot(red, col = "red", pch = 16, cex = 2,
asp = 1, xlim = c(0,1), ylim = c(0,1),
xlab = "", ylab = "")##plots both points red
abline(v = c(0,1), col = "grey", lty = 2)
abline(h = c(0,1), col = "grey", lty = 2)
segments(red[1,1], red[1,2], red[2,1], red[2,2], lwd = 2, col = "red")#Makes the line segment
points(blue, col = "blue", pch = 16, cex = 2,
asp = 1, xlim = c(0,1), ylim = c(0,1))# does same thing for blue line
segments(blue[1,1], blue[1,2], blue[2,1], blue[2,2], lwd = 2, col = "blue")
p <- check_intersect(red, blue)
points(p[1], p[2], cex = 2)
p
#> [1] 0.5719010 0.6781469
Created on 2020-03-24 by the reprex package (v0.3.0)

Creating a secondary y-axis in histogram

I have found multiple ways to create a secondary y-axis in plot but I couldn't find a way to create a secondary y-axis in histogram.
Here is a sample code:
a <- sample(90:110, 50, replace=TRUE)
b <- runif(50, min=0, max=1)
hist(a)
lines(b)
b is too small to show in hist(a) so is there any way that I can see both in the histogram?
Technically a solution may be quite an identical to the approach proposed for the plots in this answer. The idea is to use overlapping of two plots as proposed by #r2evans.
It makes sense to use color coding:
# set color rules
col_a <- "red"
col_b <- "darkblue"
col_common <- "black"
Then let's draw the histogram and the plot:
# draw a histogram first
par(mar = c(5, 5, 5, 5) + 0.3)
hist(a, col = col_a, axes = FALSE, xlab = "", ylab = "", main = "")
# add both axes with the labels
axis(side = 1, xlim = seq(along.with = b), col = col_a, col.axis = col_a)
mtext(side = 1, text = "a_value", col = col_a, line = 2.5)
axis(side = 2, col = col_a, col.axis = col_a, ylab = "")
mtext(side = 2, text = "a_Frequency", col = col_a, line = 2.5)
# ... and add an overlaying plot
par(new=TRUE)
plot(b, ylim = c(0, 1), axes = FALSE, col = col_b, type = "l", xlab = "", ylab = "")
points(b, col = col_b, pch = 20, xlab = "", ylab = "")
axis(side = 3, xlim = seq(along.with = b), col = col_b, col.axis = col_b)
mtext(side = 3, text = "b_index", col = col_b, line = 2.5)
axis(side = 4, ylim = c(0, 1), col = col_b, col.axis = col_b)
mtext(side = 4, text = "b_value", col = col_b, line = 2.5)
box(col = col_common)

How can I plot vertical and horizontal lines even when xpd=TRUE?

Here is a simplified plot to work with:
env <- data.frame(site = c('BLK','DUC','WHP','BLK','DUC','WHP','BLK','DUC','WHP'),
sal = c(5,6,3,2,4,5,6,8,4),
date = c(2013,2013,2013,2015,2015,2015,2017,2017,2017))
sitelist <- c('BLK','DUC','WHP')
par(mar=c(3,5,3,6), xpd = T)
plot(sal~date, data = env, type = 'n', ylim = c(0,10), ylab = 'Salinity',
bty = 'n', xlab = '')
abline(v=2016, col = 'khaki', lwd = 20)
abline(mean(env$sal), 0, lty = 3)
for (ii in seq_along(sitelist)) {
i <- sitelist[ii]; lines(sal[site==i] ~ date[site==i], data = env,
col = c(4,2,5)[ii], lwd = 2,
lty = c(1,2,3)[ii]);
points(sal[site==i] ~ date[site==i], data = env,
pch = c(0,1,2)[ii], col = c(4,2,5)[ii])}
legend('topright', title = 'sites', inset=c(-0.2,0), lty = c(1,2,3),
col = c(4,2,5), lwd = 2, sitelist,
pch = c(0,1,2))
As written, this code yields a plot where the abline functions create lines that go outside of the boundaries of the plot, thanks to xpd=T. However, I don't want to set xpd=F, because I won't be able to plot my legend outside of the boundaries. The solution must either be a way to plot a legend outside of the boundaries with xpd=F or a way to plot lines that stop at the boundaries. Ideally, the solution would use the base program and be fairly standard, so I could drop it into each of my ~20 plots without too much customization.
I tried using segments but was not happy with the rounded edges of the segment, as my vertical line is supposed to be a sort of shaded area to indicate a certain time period.
You can either set xpd to FALSE in the par call and insert xpd = TRUE in the legend call like this:
env <- data.frame(site = c('BLK','DUC','WHP','BLK','DUC','WHP','BLK','DUC','WHP'),
sal = c(5,6,3,2,4,5,6,8,4),
date = c(2013,2013,2013,2015,2015,2015,2017,2017,2017))
sitelist <- c('BLK','DUC','WHP')
par(mar=c(3,5,3,6), xpd = F)
plot(sal~date, data = env, type = 'n', ylim = c(0,10), ylab = 'Salinity',
bty = 'n', xlab = '')
abline(v=2016, col = 'khaki', lwd = 20)
abline(mean(env$sal), 0, lty = 3)
for (ii in seq_along(sitelist)) {
i <- sitelist[ii]; lines(sal[site==i] ~ date[site==i], data = env,
col = c(4,2,5)[ii], lwd = 2,
lty = c(1,2,3)[ii]);
points(sal[site==i] ~ date[site==i], data = env,
pch = c(0,1,2)[ii], col = c(4,2,5)[ii])}
legend('topright', title = 'sites', inset=c(-0.2,0), lty = c(1,2,3),
col = c(4,2,5), lwd = 2, sitelist,
pch = c(0,1,2),
xpd = T)
Or keep xpd = TRUE in the par call and set xpd to FALSE in the abline calls like this:
env <- data.frame(site = c('BLK','DUC','WHP','BLK','DUC','WHP','BLK','DUC','WHP'),
sal = c(5,6,3,2,4,5,6,8,4),
date = c(2013,2013,2013,2015,2015,2015,2017,2017,2017))
sitelist <- c('BLK','DUC','WHP')
par(mar=c(3,5,3,6), xpd = T)
plot(sal~date, data = env, type = 'n', ylim = c(0,10), ylab = 'Salinity',
bty = 'n', xlab = '')
abline(v=2016, col = 'khaki', lwd = 20,xpd=F)
abline(mean(env$sal), 0, lty = 3,xpd=F)
for (ii in seq_along(sitelist)) {
i <- sitelist[ii]; lines(sal[site==i] ~ date[site==i], data = env,
col = c(4,2,5)[ii], lwd = 2,
lty = c(1,2,3)[ii]);
points(sal[site==i] ~ date[site==i], data = env,
pch = c(0,1,2)[ii], col = c(4,2,5)[ii])}
legend('topright', title = 'sites', inset=c(-0.2,0), lty = c(1,2,3),
col = c(4,2,5), lwd = 2, sitelist,
pch = c(0,1,2))
This should solve your issue.
Replace
abline(v=2016, col = 'khaki', lwd = 20)
abline(mean(env$sal), 0, lty = 3)
with
lines(c(2013, 2017), rep(mean(env$sal), 2), col="black", lwd = 2, lty = 2)
lines(rep(2016, 2), c(0, 10), col="khaki", lwd = 20)
Source: https://stackoverflow.com/a/24741885/5874001
par(mar=c(3,5,3,6), xpd = T)
plot(sal~date, data = env, type = 'n', ylim = c(0,10), ylab = 'Salinity', bty = 'n', xlab = '')
lines(c(2013, 2017), rep(mean(env$sal), 2), col="black", lwd = 2, lty = 2)
lines(rep(2016, 2), c(0, 10), col="khaki", lwd = 20)
for (ii in seq_along(sitelist)) {
i <- sitelist[ii]; lines(sal[site==i] ~ date[site==i],
data = env,
col = c(4,2,5)[ii],
lwd = 2,
lty = c(1,2,3)[ii]);
points(sal[site==i] ~ date[site==i], data = env,
pch = c(0,1,2)[ii], col = c(4,2,5)[ii])}
legend('topright', title = 'sites', inset=c(-0.2,0),
lty = c(1,2,3), col = c(4,2,5), lwd = 2,
sitelist, pch = c(0,1,2))
If you have 20+ plots, I'd look to see if you can write a loop to perform that task.

Changing the size of the text in the axis labels of an Ecdf plot

I've constructed an Ecdf plot from the Hmisc package with the following call:
require(Hmisc)
Ecdf(latency_targ1, group = CONDITION, lty = c(1, 2, 3, 4),
lwd = 4, label.curves = list(method = 'arrow',
keys = "lines", lwd = 2), xlim = c(0,500),
subtitles = FALSE, xlab = "Latency",
ylab = "Proportion latency <= x")
I have been unable to find how to change the size of the axis labels of the plot and the default size is rather small.
Try this:
Ecdf(latency_targ1, group = CONDITION, lty = c(1, 2, 3, 4),
lwd = 4, label.curves = list(method = 'arrow',
keys = "lines", lwd = 2), xlim = c(0,500),
subtitles = FALSE, xlab = "Latency",
ylab = "Proportion latency <= x",
cex.lab=1.5, xaxt="n", yaxt="n")
axis(2, cex.axis=1.5)
axis(1, cex.axis=1.5)

How do I draw gridlines using abline() that are behind the data?

When I draw grid lines on a plot using abline() the grid lines are drawn over the data.
Is there a way to draw the abline() lines behind the data? I feel this would look better.
Example:
x <- seq(0, 10)
y <- x
plot(x, y, col = 'red', type = 'o', lwd = 3, pch = 15)
abline(h = seq(0, 10, .5), col = 'lightgray', lty = 3)
abline(v = seq(0, 10, .5), col = 'lightgray', lty = 3)
The plot produced has the gray grid lines going over the data (red line). I would like the red line to be on top of the gray lines.
The panel.first argument of plot() can take a list or vector of functions so you can put your abline() calls in there.
plot(1:4, panel.first =
c(abline(h = 1:4, lty = 2, col = 'grey')
,abline(v = 1:4, lty = 2, col = 'grey')))
Use plot() to set up the plotting window, but use type = "n" to not plot any data. Then do your abline() calls, or use grid(), and then plot the data using whatever low-level function is appropriate (here points() is fine).
x <- seq(0, 10)
y <- x
plot(x, y, type = "n")
abline(h = seq(0, 10, .5), col = 'lightgray', lty = 3)
abline(v = seq(0, 10, .5), col = 'lightgray', lty = 3)
points(x, y, col = 'red', type = 'o', lwd = 3, pch = 15)
or
## using `grid()`
plot(x, y, type = "n")
grid()
points(x, y, col = 'red', type = 'o', lwd = 3, pch = 15)
See ?grid for details of how to specify the grid as per your abline() version.
Plot first with type="n" to establish coordinates. Then put in the grid lines, then plot again with your regular plot type:
plot(x, y, col = 'red', type = 'n', lwd = 3, pch = 15)
abline(h = seq(0, 10, .5), col = 'lightgray', lty = 3)
abline(v = seq(0, 10, .5), col = 'lightgray', lty = 3)
par(new=TRUE)
plot(x, y, col = 'red', type = 'o', lwd = 3, pch = 15)
I admit that I have always thought the name for that par parameter was "backwards."
Another way of creating grid lines is to set tck=1 when plotting or in the axis function (you may still want to plot the points using points after creating the grid lines.

Resources