I am trying to make one figure by stacking two graphs (a) and (b) vertically (i.e., a multiple plot with 2 rows, 1 column).
While I can do this for other plots I have tried, the following two plots generated from data using the ODE solver package deSolve refuse to be combined. On the screen, plot (a) flashes by and I am left looking at plot (b). Saving the plots results in 1 pdf file with 2 pages (each plot on a separate page) rather than 1 pdf file and 1 page (with both plots stacked into one figure as I am seeking).
As you can see from the code I have tried both mfrow and layout approaches to no avail. Any help would be greatly appreciated.
Thanks,
Carey
df1 <-function(t,y,mu)( list(c(y[2], mu*y[1]^3 - y[1] + 0.005 * cos(t))))
library(deSolve)
yini <- c(y1=0, y2=0)
df2 <-ode(y = yini, func = df1, times = 0:1050, parms = 0.1667)
t <- seq(0, 1050, length=10000)
x <- 0.24 * (1 - cos(0.012 * t)) * cos(t + sin(0.012 * t))
pdf("c:/users/name/Desktop/figure2.pdf", height = 3, width=8)
# par(mfrow = c(2, 1))
layout(matrix(c(1, 2), 2, 1, byrow = TRUE))
plot(df2, type="l", which="y1", ylab="x", xlab="t", main="(a)")
plot(t, x, type="l", main="(b)")
dev.off()
The problem is that deSolve objects have their own plot method, and that overrides what happens with layout. As soon as your first plot is run, the layout set up is completely undone and the display is configured in terms of the defaults for deSolve objects. That's why there's a flash when the second plot writes over the first since there is no longer a multi-panel display.
The plot method is ?plot.deSolve - this function takes mfrow/mfcol arguments, so you can work with your layout as intended.
layout(matrix(c(1, 1, 2, 2), 2, 2, byrow = TRUE))
plot(df2, type="l", which = "y1", ylab = "x", xlab = "t", main = "(a)", mfrow = c(2, 1))
plot(t, x, type = "l", main = "(b)")
Related
I am trying to visualize the trajectory of multiple participants in a virtual room using R. I have a participant entering from the right (black square) and moving toward the left, where there is an exit door (red square). Sometimes there is an obstacle right in the middle of the room (circle), and the participant goes around it.
To visualize multiple participants’ trajectories on the same graph (i.e., multiple lines), I have used the function plot to set up the plot itself (and the first line) and then I have used the function lines to add other trajectories after that.
Below you can see an example with two lines; in the experiment, I have many more (as now I have collected data from about 20 participants.)
library(shape)
# black line
pos_x <- c(5.04,4.68,4.39,4.09,3.73,3.37,3.07,2.77,2.47,2.11)
pos_z <- c(0.74,0.69,0.64,0.60,0.56,0.52,0.50,0.50,0.50,0.51)
df1 <- cbind.data.frame(pos_x,pos_z)
x.2 <- df1$pos_x
z.2 <- df1$pos_z
plot(x.2,z.2,type="l", xlim=range(x.2), ylim=c(-1,3.5), xlab="x", ylab="z", main = "Two trajectories")
filledrectangle(wx = 0.2, wy = 0.2,col = "black", mid = c(5.16, 1), angle = 0)
filledrectangle(wx = 0.2, wy = 0.2,col = "red", mid = c(2, 1), angle = 0)
plotcircle(mid = c(3.4, 1), r = 0.05)
# red line
pos_x <- c(5.14,4.84,4.24,3.64,3.34,2.74,2.15)
pos_z <- c(0.17,0.13,0.01,-0.2,0.01,0.10,0.17)
df2 <- cbind.data.frame(pos_x,pos_z)
x.3 <- df2$pos_x
z.3 <- df2$pos_z
lines(x.3, z.3, xlim=range(x.3), ylim=c(-1,3.5), pch=16, col="red")
What I would like to do now is to find the average between these two lines. Ideally, I would like to be able to average multiple lines and add an interval for the standard deviation.
The first thing I have tried is to build an interpolation; the problem is that the start and end point are different, so I cannot average the points:
plot(x.2, z.2, xlim=range(x.2), ylim=c(-1,3.5), xlab="x", ylab="z", main = "Interpolation")
points(approx(x.2, z.2), col = 2, pch = "*")
points(x.3, z.3)
points(approx(x.3, z.3), col = 2, pch = "*")
I have then found a suggestion here: use the R library dtw.
I have looked up the library and the companion paper.
This is a typical example from the paper, in which "two non-overlapping windows" are extracted from a reference electrocardiogram. The dataset "aami3a" is a time series object.
library("dtw")
data("aami3a")
ref <- window(aami3a, start = 0, end = 2)
test <- window(aami3a, start = 2.7, end = 5)
alignment <- dtw(test, ref)
alignment$distance
The problem is that in all these examples the data is either structured as a time series object or the two lines are functions of a common matrix (see also the R quickstart example in the documentation and this other tutorial.)
How can I reorganize my data to make the function work? Or do you know of any other way to create an average?
You could map equivalent points from the start to the end of each path (i.e. find the midpoint between the two lines at the start of each path, the midpoint between the two lines after a quarter of each path is complete, after a half, at the end, etc.
The way to do that is to use interpolation (via approx):
pos_x_a <- c(5.04,4.68,4.39,4.09,3.73,3.37,3.07,2.77,2.47,2.11)
pos_z_a <- c(0.74,0.69,0.64,0.60,0.56,0.52,0.50,0.50,0.50,0.51)
pos_x_b <- c(5.14,4.84,4.24,3.64,3.34,2.74,2.15)
pos_z_b <- c(0.17,0.13,0.01,-0.2,0.01,0.10,0.17)
pos_t_a <- seq(0, 1, length.out = length(pos_x_a))
pos_t_b <- seq(0, 1, length.out = length(pos_x_b))
a_x <- approx(pos_t_a, pos_x_a, seq(0, 1, 0.01))$y
a_y <- approx(pos_t_a, pos_z_a, seq(0, 1, 0.01))$y
b_x <- approx(pos_t_b, pos_x_b, seq(0, 1, 0.01))$y
b_y <- approx(pos_t_b, pos_z_b, seq(0, 1, 0.01))$y
plot(a_x, a_y, type = "l", ylim = c(-1, 3))
lines(b_x, b_y, col = "red")
lines((a_x + b_x)/2, (a_y + b_y)/2, col = "blue", lty = 2)
We get a better idea of how this averaging has occurred by joining the points on each line that were used to get the average:
for(i in seq_along(a_x)) segments(a_x[i], a_y[i], b_x[i], b_y[i], col = "gray")
I've successfully produced NMDS plots (monoMDS, bray-curtis, 3 dimensions, local model). Each point represents an animal and their diet composition.
I have two questions:
(1) how do I change the symbology of points to show 2 levels (a or j) within 1 column (Life stage) on the NMDS plot?!
(2) How should I show 3D NMDS, I can't get the 3D orgl- functions to work on the 3D plot. Should I just make a few plots showing different dimensions in 2D? Looking for thoughtful ideas.
The code used:
plot((BC.NMDS.length.corr), choices = c(1, 2), type = "points",
xlim = c(-2.0, 2.0),las = 1, ylim = c(-1, 1),
xlab = "NMDS Axis 1", ylab = "NMDS Axis 2",mgp = c(3.25, 1, 0),
cex.lab = 1.35, cex.axis = 1.25)
with(DATA,
points(BC.NMDS.length.corr, Class, draw = "points",col = "gray0",
show.groups = "Adult",label = TRUE, lty = 1, lwd = 2))
Using an example of what you want with the default example of the package:
# Load library
library(vegan)
# Load data
data(dune)
# Compute the distance
dis <- vegdist(dune)
Specify if you want a 3D plot, the representation of the three dimensions
# Run monoMDS
m <- monoMDS(dis, model = "loc", k=3)
# The 3D representation
plot(m)
# Load library for 3D representation
library(scatterplot3d)
Coordinates are in m$points; each column referring to each dimension.
# Graphical representation
scatterplot3d(x=m$points[,1], y=m$points[,2], z=m$points[,3])
Additionally, if you want to colour the plots depending on a factor, you can specify color=A, where A is a numeric value where groups are codified.
I'm trying to plot two curves in the same graph, but it doesn't work. I want to plot the function f(x) = 3x + 2 if x<=3 and f(x) = 2x-0.5x^2 if x>3 on the interval [0,6]. I thought I had to do
curve(3*x+2, 0,3)
and
curve(2*x-0.5*x^2,3,6, add = TRUE)
What could I do to plot such function?
Use xlim and ylim in the first curve to set the limits of the plot.
curve(3*x+2, 0,3, xlim = c(0, 6), ylim = c(-5, 12))
curve(2*x-0.5*x^2,3,6, add = TRUE)
As the second curve still gets cut off a little bit, you might want to use c(-7 12) for the y limits.
Another option, if you want the lines connected and which removes the need to set manual limits is to encode both functions in one with ifelse:
curve(ifelse(x <= 3, 3 * x + 2, 2 * x - 0.5 * x^2), 0, 6, ylab = "f(x)")
You can do this for instance, you'll get 2 curves together.
c1 <- curve(2*x-0.6*x^2,3,6)
c2 <- curve(2*x-0.5*x^2,3,6)
plot(c1)
lines(c2, col="red")
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)
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.