Plotting multiple curves same graph and same scale - r

This is a follow-up of this question.
I wanted to plot multiple curves on the same graph but so that my new curves respect the same y-axis scale generated by the first curve.
Notice the following example:
y1 <- c(100, 200, 300, 400, 500)
y2 <- c(1, 2, 3, 4, 5)
x <- c(1, 2, 3, 4, 5)
# first plot
plot(x, y1)
# second plot
par(new = TRUE)
plot(x, y2, axes = FALSE, xlab = "", ylab = "")
That actually plots both sets of values on the same coordinates of the graph (because I'm hiding the new y-axis that would be created with the second plot).
My question then is how to maintain the same y-axis scale when plotting the second graph.

(The typical method would be to use plot just once to set up the limits, possibly to include the range of all series combined, and then to use points and lines to add the separate series.) To use plot multiple times with par(new=TRUE) you need to make sure that your first plot has a proper ylim to accept the all series (and in another situation, you may need to also use the same strategy for xlim):
# first plot
plot(x, y1, ylim=range(c(y1,y2)))
# second plot EDIT: needs to have same ylim
par(new = TRUE)
plot(x, y2, ylim=range(c(y1,y2)), axes = FALSE, xlab = "", ylab = "")
This next code will do the task more compactly, by default you get numbers as points but the second one gives you typical R-type-"points":
matplot(x, cbind(y1,y2))
matplot(x, cbind(y1,y2), pch=1)

points or lines comes handy if
y2 is generated later, or
the new data does not have the same x but still should go into the same coordinate system.
As your ys share the same x, you can also use matplot:
matplot (x, cbind (y1, y2), pch = 19)
(without the pch matplopt will plot the column numbers of the y matrix instead of dots).

You aren't being very clear about what you want here, since I think #DWin's is technically correct, given your example code. I think what you really want is this:
y1 <- c(100, 200, 300, 400, 500)
y2 <- c(1, 2, 3, 4, 5)
x <- c(1, 2, 3, 4, 5)
# first plot
plot(x, y1,ylim = range(c(y1,y2)))
# Add points
points(x, y2)
DWin's solution was operating under the implicit assumption (based on your example code) that you wanted to plot the second set of points overlayed on the original scale. That's why his image looks like the points are plotted at 1, 101, etc. Calling plot a second time isn't what you want, you want to add to the plot using points. So the above code on my machine produces this:
But DWin's main point about using ylim is correct.

My solution is to use ggplot2. It takes care of these types of things automatically. The biggest thing is to arrange the data appropriately.
y1 <- c(100, 200, 300, 400, 500)
y2 <- c(1, 2, 3, 4, 5)
x <- c(1, 2, 3, 4, 5)
df <- data.frame(x=rep(x,2), y=c(y1, y2), class=c(rep("y1", 5), rep("y2", 5)))
Then use ggplot2 to plot it
library(ggplot2)
ggplot(df, aes(x=x, y=y, color=class)) + geom_point()
This is saying plot the data in df, and separate the points by class.
The plot generated is

I'm not sure what you want, but i'll use lattice.
x = rep(x,2)
y = c(y1,y2)
fac.data = as.factor(rep(1:2,each=5))
df = data.frame(x=x,y=y,z=fac.data)
# this create a data frame where I have a factor variable, z, that tells me which data I have (y1 or y2)
Then, just plot
xyplot(y ~x|z, df)
# or maybe
xyplot(x ~y|z, df)

Related

How to plot multiple columns at the same time? [duplicate]

I would like to plot y1 and y2 in the same plot.
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
plot(x, y1, type = "l", col = "red")
plot(x, y2, type = "l", col = "green")
But when I do it like this, they are not plotted in the same plot together.
In Matlab one can do hold on, but does anyone know how to do this in R?
lines() or points() will add to the existing graph, but will not create a new window. So you'd need to do
plot(x,y1,type="l",col="red")
lines(x,y2,col="green")
You can also use par and plot on the same graph but different axis. Something as follows:
plot( x, y1, type="l", col="red" )
par(new=TRUE)
plot( x, y2, type="l", col="green" )
If you read in detail about par in R, you will be able to generate really interesting graphs. Another book to look at is Paul Murrel's R Graphics.
When constructing multilayer plots one should consider ggplot package. The idea is to create a graphical object with basic aesthetics and enhance it incrementally.
ggplot style requires data to be packed in data.frame.
# Data generation
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
df <- data.frame(x,y1,y2)
Basic solution:
require(ggplot2)
ggplot(df, aes(x)) + # basic graphical object
geom_line(aes(y=y1), colour="red") + # first layer
geom_line(aes(y=y2), colour="green") # second layer
Here + operator is used to add extra layers to basic object.
With ggplot you have access to graphical object on every stage of plotting. Say, usual step-by-step setup can look like this:
g <- ggplot(df, aes(x))
g <- g + geom_line(aes(y=y1), colour="red")
g <- g + geom_line(aes(y=y2), colour="green")
g
g produces the plot, and you can see it at every stage (well, after creation of at least one layer). Further enchantments of the plot are also made with created object. For example, we can add labels for axises:
g <- g + ylab("Y") + xlab("X")
g
Final g looks like:
UPDATE (2013-11-08):
As pointed out in comments, ggplot's philosophy suggests using data in long format.
You can refer to this answer in order to see the corresponding code.
I think that the answer you are looking for is:
plot(first thing to plot)
plot(second thing to plot,add=TRUE)
Use the matplot function:
matplot(x, cbind(y1,y2),type="l",col=c("red","green"),lty=c(1,1))
use this if y1 and y2 are evaluated at the same x points. It scales the Y-axis to fit whichever is bigger (y1 or y2), unlike some of the other answers here that will clip y2 if it gets bigger than y1 (ggplot solutions mostly are okay with this).
Alternatively, and if the two lines don't have the same x-coordinates, set the axis limits on the first plot and add:
x1 <- seq(-2, 2, 0.05)
x2 <- seq(-3, 3, 0.05)
y1 <- pnorm(x1)
y2 <- pnorm(x2,1,1)
plot(x1,y1,ylim=range(c(y1,y2)),xlim=range(c(x1,x2)), type="l",col="red")
lines(x2,y2,col="green")
Am astonished this Q is 4 years old and nobody has mentioned matplot or x/ylim...
tl;dr: You want to use curve (with add=TRUE) or lines.
I disagree with par(new=TRUE) because that will double-print tick-marks and axis labels. Eg
The output of plot(sin); par(new=T); plot( function(x) x**2 ).
Look how messed up the vertical axis labels are! Since the ranges are different you would need to set ylim=c(lowest point between the two functions, highest point between the two functions), which is less easy than what I'm about to show you---and way less easy if you want to add not just two curves, but many.
What always confused me about plotting is the difference between curve and lines. (If you can't remember that these are the names of the two important plotting commands, just sing it.)
Here's the big difference between curve and lines.
curve will plot a function, like curve(sin). lines plots points with x and y values, like: lines( x=0:10, y=sin(0:10) ).
And here's a minor difference: curve needs to be called with add=TRUE for what you're trying to do, while lines already assumes you're adding to an existing plot.
Here's the result of calling plot(0:2); curve(sin).
Behind the scenes, check out methods(plot). And check body( plot.function )[[5]]. When you call plot(sin) R figures out that sin is a function (not y values) and uses the plot.function method, which ends up calling curve. So curve is the tool meant to handle functions.
if you want to split the plot into two columns (2 plots next to each other), you can do it like this:
par(mfrow=c(1,2))
plot(x)
plot(y)
Reference Link
As described by #redmode, you may plot the two lines in the same graphical device using ggplot. In that answer the data were in a 'wide' format. However, when using ggplot it is generally most convenient to keep the data in a data frame in a 'long' format. Then, by using different 'grouping variables' in the aesthetics arguments, properties of the line, such as linetype or colour, will vary according to the grouping variable, and corresponding legends will appear.
In this case, we can use the colour aessthetics, which matches colour of the lines to different levels of a variable in the data set (here: y1 vs y2). But first we need to melt the data from wide to long format, using e.g. the function 'melt' from reshape2 package. Other methods to reshape the data are described here: Reshaping data.frame from wide to long format.
library(ggplot2)
library(reshape2)
# original data in a 'wide' format
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
df <- data.frame(x, y1, y2)
# melt the data to a long format
df2 <- melt(data = df, id.vars = "x")
# plot, using the aesthetics argument 'colour'
ggplot(data = df2, aes(x = x, y = value, colour = variable)) + geom_line()
If you are using base graphics (i.e. not lattice/ grid graphics), then you can mimic MATLAB's hold on feature by using the points/lines/polygons functions to add additional details to your plots without starting a new plot. In the case of a multiplot layout, you can use par(mfg=...) to pick which plot you add things to.
You can use points for the overplot, that is.
plot(x1, y1,col='red')
points(x2,y2,col='blue')
Idiomatic Matlab plot(x1,y1,x2,y2) can be translated in R with ggplot2 for example in this way:
x1 <- seq(1,10,.2)
df1 <- data.frame(x=x1,y=log(x1),type="Log")
x2 <- seq(1,10)
df2 <- data.frame(x=x2,y=cumsum(1/x2),type="Harmonic")
df <- rbind(df1,df2)
library(ggplot2)
ggplot(df)+geom_line(aes(x,y,colour=type))
Inspired by Tingting Zhao's Dual line plots with different range of x-axis Using ggplot2.
Rather than keeping the values to be plotted in an array, store them in a matrix. By default the entire matrix will be treated as one data set. However if you add the same number of modifiers to the plot, e.g. the col(), as you have rows in the matrix, R will figure out that each row should be treated independently. For example:
x = matrix( c(21,50,80,41), nrow=2 )
y = matrix( c(1,2,1,2), nrow=2 )
plot(x, y, col("red","blue")
This should work unless your data sets are of differing sizes.
You could use the ggplotly() function from the plotly package to turn any of the gggplot2 examples here into an interactive plot, but I think this sort of plot is better without ggplot2:
# call Plotly and enter username and key
library(plotly)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
plot_ly(x = x) %>%
add_lines(y = y1, color = I("red"), name = "Red") %>%
add_lines(y = y2, color = I("green"), name = "Green")
You can also create your plot using ggvis:
library(ggvis)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
df <- data.frame(x, y1, y2)
df %>%
ggvis(~x, ~y1, stroke := 'red') %>%
layer_paths() %>%
layer_paths(data = df, x = ~x, y = ~y2, stroke := 'blue')
This will create the following plot:
Using plotly (adding solution from plotly with primary and secondary y axis- It seems to be missing):
library(plotly)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
df=cbind.data.frame(x,y1,y2)
plot_ly(df) %>%
add_trace(x=~x,y=~y1,name = 'Line 1',type = 'scatter',mode = 'lines+markers',connectgaps = TRUE) %>%
add_trace(x=~x,y=~y2,name = 'Line 2',type = 'scatter',mode = 'lines+markers',connectgaps = TRUE,yaxis = "y2") %>%
layout(title = 'Title',
xaxis = list(title = "X-axis title"),
yaxis2 = list(side = 'right', overlaying = "y", title = 'secondary y axis', showgrid = FALSE, zeroline = FALSE))
Screenshot from working demo:
we can also use lattice library
library(lattice)
x <- seq(-2,2,0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
xyplot(y1 + y2 ~ x, ylab = "y1 and y2", type = "l", auto.key = list(points = FALSE,lines = TRUE))
For specific colors
xyplot(y1 + y2 ~ x,ylab = "y1 and y2", type = "l", auto.key = list(points = F,lines = T), par.settings = list(superpose.line = list(col = c("red","green"))))
Use curve for mathematical functions.
And use add=TRUE to use the same plot and axis.
curve( log2 , to=5 , col="black", ylab="log's(.)")
curve( log , add=TRUE , col="red" )
curve( log10, add=TRUE , col="blue" )
abline( h=0 )

How to make logarithmic axes in plot3d (library("rgl")) in R?

I am having extreme difficulty in making my axes logarithmic/have custom tick marks in plot3d using the rgl package. I've tried using the "log='xy'" command in my code just like you would in the basic plot function, and I have tried to create custom tick marks using rgl.bbox. My y axis is plotting fine but my x and z are not cooperating. I cannot get anything to work. Any ideas? Below is my data, code, and a picture of the result I'm getting. I should also add that I'm basically plotting multiple 2d scatterplots in 3d using an arbitrary z value to separate the individual 2d plots.
https://www.dropbox.com/s/wv24rmnyalm3vvc/scattertest.csv?dl=0
#!/usr/bin/env Rscript
library("rgl")
data <- read.csv("~/Desktop/scattertest.csv", header=TRUE, fill=TRUE, sep=',')
x <- names(data[2])
y <- names(data[3])
z <- names(data[4])
plot3d(data[[x]], data[[z]], data[[y]], type="s", size=0.75, lit=FALSE, axes=FALSE,
xlab="rpmn", ylab="round", zlab="rpmt", log="xz",
xmin=c(0.1, 10^6), ymin=c(1,4), zmin=c(0.1, 10^6))
rgl.bbox(color="grey50", emission="grey50",
xat = c(0.1, 1, 10, 100, 10^3, 10^4, 10^5, 10^6), yat = c(1, 2, 3, 4), zat = c(0.1, 1, 10, 100, 10^3, 10^4, 10^5, 10^6),
xlen=8, ylen=4, zlen=8)
There's no support for log="xy" in plot3d(), you'll need to do the transformation yourself.
Your code asks for logarithmic labels, but you aren't doing the logarithmic transformation, so it's not working. You need to rescale the data as well.
You didn't post a reproducible example, but it's easy to create one:
x <- rlnorm(20, 2, 6)
y <- runif(20, 1, 4)
z <- rlnorm(20, 2, 6)
xyz <- cbind(log(x), y, log(z))
plot3d(xyz, axes = FALSE)
ticks <- 10^((-1):6)
bbox3d(xat = log(ticks), xlab = ticks, yat = pretty(1:4),
zat = log(ticks), zlab = ticks,
color="grey50", emission="grey50")

Combining 2 datasets in a single plot in R

I have two columns of data, f.delta and g.delta that I would like to produce a scatter plot of in R.
Here is how I am doing it.
plot(f.delta~x, pch=20, col="blue")
points(g.delta~x, pch=20, col="red")
The problem is this: the values of f.delta vary from 0 to -7; the values of g.delta vary from 0 to 10.
When the plot is drawn, the y axis extends from 1 to -7. So while all the f.delta points are visible, any g.delta point that has y>1 is cut-off from view.
How do I stop R from automatically setting the ylims from the data values. Have tried, unsuccessfully, various combinations of yaxt, yaxp, ylims.
Any suggestion will be greatly appreciated.
Thanks,
Anjan
In addition to Gavin's excellent answer, I also thought I'd mention that another common idiom in these cases is to create an empty plot with the correct limits and then to fill it in using points, lines, etc.
Using Gavin's example data:
with(df,plot(range(x),range(f.delta,g.delta),type = "n"))
points(f.delta~x, data = df, pch=20, col="blue")
points(g.delta~x, data = df, pch=20, col="red")
The type = "n" causes plot to create only the empty plotting window, based on the range of x and y values we've supplied. Then we use points for both columns on this existing plot.
You need to tell R what the limits of the data are and pass that as argument ylim to plot() (note the argument is ylim not ylims!). Here is an example:
set.seed(1)
df <- data.frame(f.delta = runif(10, min = -7, max = 0),
g.delta = runif(10, min = 0, max = 10),
x = rnorm(10))
ylim <- with(df, range(f.delta, g.delta)) ## compute y axis limits
plot(f.delta ~ x, data = df, pch = 20, col = "blue", ylim = ylim)
points(g.delta ~ x, data = df, pch = 20, col = "red")
Which produces

cooks distance plot with R

Does anybody know, how to grab the single cooks distance plot that you get from this code:
treatment <- factor(rep(c(1, 2), c(43, 41)), levels = c(1, 2), labels = c("placebo","treated"))
improved <- factor(rep(c(1, 2, 3, 1, 2, 3), c(29, 7, 7, 13, 7, 21)), levels = c(1, 2,3),labels = c("none", "some", "marked"))
numberofdrugs <- rpois(84, 5)+1
healthvalue <- rpois(84,5)
y <- data.frame(healthvalue, numberofdrugs, treatment, improved)
test <- glm(healthvalue~numberofdrugs+treatment+improved, y, family=poisson)
par(mfrow=c(2,2))
plot(test) # how to grab plot 2.1 ?
What I don't like to have is this
par(mfrow=c(1, 1))
plot(test, which=c(4))
because it doesn't have residuals on the y axis and leverage on the x axis!
Thanks guys
I'm not quite sure what your problem is. You seem to want the plot with residuals on the y axis and leverage on the x axis. Isn't that just the 5th (of 6) plot generated:
plot(test,which=5)
You can read more about this at ?plot.lm
Edit to address OP's question about setting y axis labels:
Usually, simply adding ylab="My Label" to the plot() call would work, but these graphs are designed to be produced "automatically" and so certain graphical parameters are 'hard coded'. If you pass your own ylab value, you'll get an error, as plot.lm() will be presented with two ylab's and won't know which one to use. If you really don't like the y axis label, your only option here is to grab the plot.lm code (just type 'plot.lm' at the console and hit enter) copy and paste it into a text file and look for this section:
if (show[5L]) {
ylab5 <- if (isGlm)
"Std. Pearson resid."
else "Standardized residuals"
r.w <- residuals(x, "pearson")
if (!is.null(w))
r.w <- r.w[wind]
rsp <- dropInf(r.w/(s * sqrt(1 - hii)), hii)
ylim <- range(rsp, na.rm = TRUE)
if (id.n > 0) {
ylim <- extendrange(r = ylim, f = 0.08)
show.rsp <- order(-cook)[iid]
}
and modify it with your own y axis label. Rename the function (say, plotLMCustomY, or something) and it should work.

Lattice: multiple plots in one window?

I'm trying to put multiple lattice plots in one window using levelplot by setting par(mfrow=c(2,1)) but it seems to be ignoring this.
Is there a particular function for setting multiple plots in lattice?
The 'lattice' package is built on the grid package and attaches its namespace when 'lattice' loaded. However, in order to use the grid.layout function, you need to explicitly load() pkg::grid. The other alternative, that is probably easier, is the grid.arrange function in pkg::gridExtra:
install.packages("gridExtra")
require(gridExtra) # also loads grid
require(lattice)
x <- seq(pi/4, 5 * pi, length.out = 100)
y <- seq(pi/4, 5 * pi, length.out = 100)
r <- as.vector(sqrt(outer(x^2, y^2, "+")))
grid <- expand.grid(x=x, y=y)
grid$z <- cos(r^2) * exp(-r/(pi^3))
plot1 <- levelplot(z~x*y, grid, cuts = 50, scales=list(log="e"), xlab="",
ylab="", main="Weird Function", sub="with log scales",
colorkey = FALSE, region = TRUE)
plot2 <- levelplot(z~x*y, grid, cuts = 50, scales=list(log="e"), xlab="",
ylab="", main="Weird Function", sub="with log scales",
colorkey = FALSE, region = TRUE)
grid.arrange(plot1,plot2, ncol=2)
The Lattice Package often (but not always) ignores the par command, so i just avoid using it when plotting w/ Lattice.
To place multiple lattice plots on a single page:
create (but don't plot) the lattice/trellis plot objects, then
call print once for each plot
for each print call, pass in arguments for (i) the plot; (ii)
more, set to TRUE, and which is only passed in for the initial call to print, and (iii) pos, which gives the position of each plot on the page specified as x-y coordinate pairs for the plot's lower left-hand corner and upper right-hand
corner, respectively--ie, a vector with four numbers.
much easier to show than to tell:
data(AirPassengers) # a dataset supplied with base R
AP = AirPassengers # re-bind to save some typing
# split the AP data set into two pieces
# so that we have unique data for each of the two plots
w1 = window(AP, start=c(1949, 1), end=c(1952, 1))
w2 = window(AP, start=c(1952, 1), end=c(1960, 12))
px1 = xyplot(w1)
px2 = xyplot(w2)
# arrange the two plots vertically
print(px1, position=c(0, .6, 1, 1), more=TRUE)
print(px2, position=c(0, 0, 1, .4))
This is simple to do once you read ?print.trellis. Of particular interest is the split parameter. It may seem complicated at first sight, but it's quite straightforward once you understand what it means. From the documentation:
split: a vector of 4 integers, c(x,y,nx,ny), that says to position the current plot at the x,y position in a regular array of nx by ny plots. (Note: this has origin at top left)
You can see a couple of implementations on example(print.trellis), but here's one that I prefer:
library(lattice)
# Data
w <- as.matrix(dist(Loblolly))
x <- as.matrix(dist(HairEyeColor))
y <- as.matrix(dist(rock))
z <- as.matrix(dist(women))
# Plot assignments
pw <- levelplot(w, scales = list(draw = FALSE)) # "scales..." removes axes
px <- levelplot(x, scales = list(draw = FALSE))
py <- levelplot(y, scales = list(draw = FALSE))
pz <- levelplot(z, scales = list(draw = FALSE))
# Plot prints
print(pw, split = c(1, 1, 2, 2), more = TRUE)
print(px, split = c(2, 1, 2, 2), more = TRUE)
print(py, split = c(1, 2, 2, 2), more = TRUE)
print(pz, split = c(2, 2, 2, 2), more = FALSE) # more = FALSE is redundant
The code above gives you this figure:
As you can see, split takes four parameters. The last two refer to the size of your frame (similar to what mfrow does), whereas the first two parameters position your plot into the nx by ny frame.

Resources