I have a sample data set for which I plot several png files divided by groups (in this case by ID) in a loop.
A question that concerns the x axis: How could I introduce a fixed range (lets say from 1940 to 2014 in every graph) into the for loop, so that the x-axis always corresponds to this range (case1) but if values in YEAR before 1940 are included, do the plot scenario with the whole group which is plotting the axis ranges automatically like in the for-loop introduced in the code above (case 2)?
Case 1 with the sample data would be for the group with ID 259 (NAME2) and case 2 would be for the group with ID 47 (NAME1)
Here is my code:
xy <- data.frame(NAME=c("NAME1", "NAME1","NAME1","NAME1","NAME2","NAME2","NAME2"),ID=c(47,47,47,47,259,259,259),YEAR=c(1932,1942,1965,1989,2007,2008,2014),VALUE=c(0,NA,-6,-16,0,-9,-28))
ind <- split(x = xy,f = xy[,'ID'])
### PLOT
for(i in 1:length(ind)){
png(names(ind[i]), width=3358, height=2329, res=300)
par(mar=c(6,8,6,5))
plot(ind[[i]][,c('YEAR','VALUE')],
type='n',
main=ind[[i]][1,'NAME'],
xlab="Time [Years]",
ylab="Length change [m]")
# plot axis
axis(1, at = seq(1000,2030,10), cex.axis=1, labels=FALSE, tcl=-0.3)
# plot points and lines
points(ind[[i]][,c('YEAR','VALUE')], type="l", lwd=2)
points(ind[[i]][,c('YEAR','VALUE')], type="p", lwd=1, cex=1, pch=21, bg='white')
# plot vertical line through 0
abline(h=0)
dev.off()
}
You've almost got it! Starting with a blank plot and then adding points/lines is perfect. Change your initial plot call to include the ranges you want, and you're good to go:
x.range <- c(1940, 2014)
if (min(ind[[i]][, 'YEAR'], na.rm = T) < 1940) {
x.range <- range(ind[[i]][, 'YEAR'], finite = T)
}
plot(x = x.range,
y = range(ind[[i]][,'VALUE'], finite = T),
type='n',
main=ind[[i]][1,'NAME'],
xlab="Time [Years]",
ylab="Length change [m]")
Note that these plots will still have different y axes.
Related
I would like your help, please.
I have this 2 plots, separately. One is normal frequency and the other one, with exactly the same data, is for relative frequency.
Can you tell me how can i join them in a single plot with 2 y axis ( frequency and relative frequency?)
x<- AAA$starch
h<-hist(x, breaks=40, col="lightblue", xlab="Starch ~ Corn",
main="Histogram with Normal Curve", xlim=c(58,70),ylim = c(0,2500),axes=TRUE)
xfit<-seq(min(x),max(x),length=40)
yfit<-dnorm(xfit,mean=mean(x),sd=sd(x))
yfit <- yfit*diff(h$mids[1:2])*length(x)
lines(xfit, yfit, col="blue", lwd=3)
library(HistogramTools)
x<- AAA$starch
c <- hist(x,breaks=10, ylab="Relative Frequency", main="Histogram with Normal Curve",ylim=c(0,2500), xlim=c(58,70), axes=TRUE)
PlotRelativeFrequency((c))
Thank you!!
EDIT:
This is just an example image of what I want...
I use doubleYScale from package latticeExtra.
Here is an example (I am not sure about relative frequency calculation) :
library(latticeExtra)
set.seed(42)
firstSet <- rnorm(500,4)
breaks = 0:10
#Cut data into sections
firstSet.cut = cut(firstSet, breaks, right=FALSE)
firstSet.freq = table(firstSet.cut)
#Calculate relative frequency
firstSet.relfreq = firstSet.freq / length(firstSet)
#Parse to a list to use xyplot later and assigning x values
firstSet.list <- list(x = 1:10, y = as.vector(firstSet.relfreq))
#Build histogram and relative frequency curve
hist1 <- histogram(firstSet, breaks = 10, freq = TRUE, col='skyblue', xlab="Starch ~ Corn", ylab="Frequency", main="Histogram with Normal Curve", ylim=c(0,40), xlim=c(0,10), plot=FALSE)
relFreqCurve <- xyplot(y ~ x, firstSet.list, type="l", ylab = "Relative frequency", ylim=c(0,1))
#Build double objects plot
doubleYScale(hist1, relFreqCurve, add.ylab2 = TRUE)
And here is the result with two y axis with different scales :
The goal is to reproduce this Bid-Rent graph in R:
The challenge is to draw the projected circles. So far I got:
The 2D part is created by the R code below with the traditional graphic system in base R:
#Distance
X <- seq(0,7,1)
#Bid Rent Curves: Commercial, Industrial, Residential
com <- -5*X + 10
ind <- -2*X + 7
res <- -0.75*X + 4
graph <- plot(X, com, type="l", col="green", ylim=c(0,10), xlab="", ylab="", axes=FALSE)
lines(X, ind, col="red")
lines(X, res, col="blue")
abline(v=0, h=0)
segments(1,0, 1,5, lty=2)
segments(2.5,0, 2.5,2, lty=2)
title(main="Bid Rent Curves", sub="Alonso Model",
xlab="Distance from CBD", ylab="Rent per m2")
text(2.5,7.5, "Commercial", col="green")
text(3.5,4, "Industrial", col="red")
text(5.5,2, "Residential", col="blue")
(Detail: Why the curves do not respect the ylim = 0 ?)
How make the projection and draw the semi-circles?
It is not exactly a 3D plot. I have looked into plot3D and rgl. I am not sure which packages or strategy to use from here.
I'm taking you at your word that you want circles, so you need to push the plot area into the upper right corner:
outHalfCirc <- function(r,colr) {opar=par(xpd=TRUE, new=TRUE) #plot ouside plot area
polygon(x=seq(r,-r,by=-0.1),
y= -sqrt(r^2 - seq(r,-r,by=-0.1)^2) , # solve r^2 = x^2 +y^2 for y
xlim =c(0,7 ), ylim=c(0,10), col=colr, # need xlim and ylim to match base plot ranges
yaxs="i", yaxt="n", xaxs="i") # yaxis off; x and y axes meet at origin
par(opar)}
Then push plot up and to the right: This will draw a colored half-circles (largest first so they overlap) below the y=0 line.
png() # send to image file; not needed for testing
opar <- par(mar=c(15, 15, 2,2) ) # default units are in widths of text-"line".
# the margins start at lower, then clockwise
# run your code
outHalfCirc(5.5, "blue")
outHalfCirc(2.5, "red")
outHalfCirc(1, "green")
dev.off() # complete image production
par(opar) # different than the 'opar' inside the function
Voila! Although not really circles because the aspect ratio is not 1. That can be fixed (or you could set the xlim and ylim to be equal.
I would like to plot something like this (from this paper) where icons, in this case small graphs, are used as tick labels.
I get this far, where icons are more or less properly placed:
This is the code:
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
par(fig=c(0.05,1,0.3,1), new=FALSE)
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
xspan <- 0.9
xoffset <- (0.07+0.5/npoints)*xspan
for(i in 1:npoints){
x1 <- (xoffset+(i-1)/npoints)*xspan
x2 <- min(xspan*(xoffset+(i)/npoints),1)
par(fig=c(x1,x2,0,0.5), new=TRUE)
plot(graph.ring(i), vertex.label=NA)
}
However, if the number of points grows (e.g. npoints <- 15) it complains because there is no place for the icons:
Error in plot.new() : figure margins too large
I wonder wether there is a more natural way to do this so that it works for any (reasonable) number of points.
Any advice is welcome.
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
# reserve some extra space on bottom margin (outer margin)
par(oma=c(3,0,0,0))
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
# graph numbers
x = 1:npoints
# add offset to first graph for centering
x[1] = x[1] + 0.4
x1 = grconvertX(x=x-0.4, from = 'user', to = 'ndc')
x2 = grconvertX(x=x+0.4, from = 'user', to = 'ndc')
# abline(v=1:npoints, xpd=NA)
for(i in x){
print(paste(i, x1[i], x2[i], sep='; '))
# remove plot margins (mar) around igraphs, so they appear bigger and
# `figure margins too large' error is avoided
par(fig=c(x1[i],x2[i],0,0.2), new=TRUE, mar=c(0,0,0,0))
plot(graph.ring(i), vertex.label=NA)
# uncomment to draw box around plot to verify proper alignment:
# box()
}
I have 7 different categories per each value in X. I am using barplot to plot these categories. Such graph looks fine in colors printer, but what if I want it to be fine in black & white. You can check the graph below. I want to have different colors texture, so the graph looks good in color and black & white printer.
I used densities = c(10,30,40,50,100,60,80) for density parameter in barplot function. Are there any other ways to do different texture in barplot?
Note: I tried the angle value in barplot. However, it isn't a good solution in that case, since not all bars have high values (i.e height of the bar).
Along the lines of my comment, you might find the following helpful:
# data generation ---------------------------------------------------------
set.seed(1)
mat <- matrix(runif(4*7, min=0, max=10), 7, 4)
rownames(mat) <- 1:7
colnames(mat) <- LETTERS[1:4]
# plotting settings -------------------------------------------------------
ylim <- range(mat)*c(1,1.5)
angle1 <- rep(c(45,45,135), length.out=7)
angle2 <- rep(c(45,135,135), length.out=7)
density1 <- seq(5,35,length.out=7)
density2 <- seq(5,35,length.out=7)
col <- 1 # rainbow(7)
# plot --------------------------------------------------------------------
op <- par(mar=c(3,3,1,1))
barplot(mat, beside=TRUE, ylim=ylim, col=col, angle=angle1, density=density1)
barplot(mat, add=TRUE, beside=TRUE, ylim=ylim, col=col, angle=angle2, density=density2)
legend("top", legend=1:7, ncol=7, fill=TRUE, col=col, angle=angle1, density=density1)
par(bg="transparent")
legend("top", legend=1:7, ncol=7, fill=TRUE, col=col, angle=angle2, density=density2)
par(op)
the type argument to xyplot() can take "s" for "steps." From help(plot):
The two step types differ in their x-y preference: Going from
(x1,y1) to (x2,y2) with x1 < x2, 'type = "s"' moves first
horizontal, then vertical, whereas 'type = "S"' moves the other
way around.
i.e. if you use type="s", the horizontal part of the step has its left end attached to the data point, while type="S" has its right end attached to the data point.
library(lattice)
set.seed(12345)
num.points <- 10
my.df <- data.frame(x=sort(sample(1:100, num.points)),
y=sample(1:40, num.points, replace=TRUE))
xyplot(y~x, data=my.df, type=c("p","s"), col="blue", main='type="s"')
xyplot(y~x, data=my.df, type=c("p","S"), col="red", main='type="S"')
How could one achieve a "step" plot, where the vertical motion happens between data points points, i.e. at x1 + (x2-x1)/2, so that the horizontal part of the step is centered on the data point?
Edited to include some example code. better late than never I suppose.
I am using excellent #nico answer to give its lattice version. Even I am ok with #Dwin because the question don't supply a reproducible example, but customizing lattice panel is sometimes challenging.
The idea is to use panel.segments which is the equivalent of segments of base graphics.
library(lattice)
xyplot(y~x,
panel =function(...){
ll <- list(...)
x <- ll$x
y <- ll$y
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
panel.segments(x.start, y, x.end, y, col="orange", lwd=2)
panel.segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
## this is optional just to compare with type s
panel.xyplot(...,type='s')
## and type S
panel.xyplot(...,type='S')
})
This is a base graphics solution, as I am not too much of an expert in lattice.
Essentially you can use segments to draw first the horizontal, then the vertical steps, passing the shifted coordinates as a vector.
Here is an example:
set.seed(12345)
# Generate some data
num.points <- 10
x <- sort(sample(1:100, num.points))
y <- sample(1:40, num.points, replace=T)
# Plot the data with style = "s" and "S"
par(mfrow=c(1,3))
plot(x, y, "s", col="red", lwd=2, las=1,
main="Style: 's'", xlim=c(0, 100))
points(x, y, pch=19, col="red", cex=0.8)
plot(x, y, "S", col="blue", lwd=2, las=1,
main="Style: 'S'", xlim=c(0, 100))
points(x, y, pch=19, col="blue", cex=0.8)
# Now plot our points
plot(x, y, pch=19, col="orange", cex=0.8, las=1,
main="Centered steps", xlim=c(0, 100))
# Calculate the starting and ending points of the
# horizontal segments, by shifting the x coordinates
# by half the difference with the next point
# Note we leave the first and last point as starting and
# ending points
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
# Now draw the horizontal segments
segments(x.start, y, x.end, y, col="orange", lwd=2)
# and the vertical ones (no need to draw the last one)
segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
Here is the result: