Using the identify function in R - r

In a scatterplot, I would like to use identify function to label the right top point.
I did this:
identify(x, y, labels=name, plot=TRUE)
*I have a named vector.
Then, while it is running, I point to the right point. Then after stopping it, it shows me the
label of the point.
Do I have to click the point that I want to label each time? Can I save it?

# Here is an example
x = 1:10
y = x^2
name = letters[1:10]
plot(x, y)
identify(x, y, labels = name, plot=TRUE)
# Now you have to click on the points and select finish at the end
# The output will be the labels you have corresponding to the dots.
Regarding saving it:
I couldn't do it using
pdf()
# plotting code
dev.off()
However in Rstudio it was posible to "copy-paste" it. If you need one plot only, i guess this would work.

You can use the return value of identify function to reproduce the labelling:
labels <- rep(letters, length.out=nrow(cars))
p <- identify(cars$speed, cars$dist, labels, plot=T)
#now we can reproduce labelling
plot(cars)
text(cars$speed[p], cars$dist[p], labels[p], pos=3)
To save the plot after using identify, you can use dev.copy:
labels <- rep(letters, length.out=nrow(cars))
identify(cars$speed, cars$dist, labels, plot=T)
#select your points here
dev.copy(png, 'myplot.png', width=600, height=600)
dev.off()

Related

R contour levels don't match filled.contour

Hopefully a straightforward question but I made a simple figure in R using filled.contour(). It looks fine, and what it should like given the data. However, I want to add a reference line along a contour for 0 (level = 0), and the plotted line doesn't match the colors on the filled.contour figure. The line is close, but not matching with the figure (and eventually crossing over another contour from the filled.contour plot). Any ideas why this is happening?
aa <- c(0.05843150, 0.11300040, 0.15280030, 0.183524400, 0.20772430, 0.228121000)
bb <- c(0.01561055, 0.06520635, 0.10196237, 0.130127650, 0.15314544, 0.172292410)
cc <- c(-0.02166599, 0.02306650, 0.05619421, 0.082193680, 0.10334837, 0.121156780)
dd <- c(-0.05356592, -0.01432910, 0.01546647, 0.039156660, 0.05858709, 0.074953650)
ee <- c(-0.08071987, -0.04654243, -0.02011676, 0.000977798, 0.01855881, 0.033651089)
ff <- c(-0.10343798, -0.07416114, -0.05111547, -0.032481132, -0.01683215, -0.003636035)
gg <- c(-0.12237798, -0.09753544, -0.07785126, -0.061607548, -0.04788856, -0.036169540)
hh <-rbind(aa,bb,cc,dd,ee,ff,gg)
z <- as.matrix(hh)
y <- seq(0.5,1.75,0.25)
x <- seq(1,2.5,0.25)
filled.contour(x,y,z,
key.title = title(main=expression("log"(lambda))),
color.palette = topo.colors) #This works
contour(x,y,z, level=0,add=T,lwd=3) #This line doesn't match plot
This is completely answered in the ?filled.contour help page. In the Notes section it states
The output produced by filled.contour is actually a combination of two plots; one is the filled contour and one is the legend. Two separate coordinate systems are set up for these two plots, but they are only used internally – once the function has returned these coordinate systems are lost. If you want to annotate the main contour plot, for example to add points, you can specify graphics commands in the plot.axes argument. See the examples.
And the examples given in that help page show how to annotate on top of the main plot. In this particular case, the correct way would be
filled.contour(x,y,z,
key.title = title(main=expression("log"(lambda))),
color.palette = topo.colors,
plot.axes = {
axis(1)
axis(2)
contour(x,y,z, level=0,add=T,lwd=3)
}
)
which produces

Setting equal xlim and ylim in plot function

Is there a way to get the plot function to generate equal xlimand ylimautomatically?
I do not want to define a fix range beforehand, but I want the plot function to decide about the range itself. However, I expect it to pick the same range for x and y.
A possible solution is to define a wrapper to the plot function:
plot.Custom <- function(x, y, ...) {
.limits <- range(x, y)
plot(x, y, xlim = .limits, ylim = .limits, ...)
}
One way is to manipulate interactively and then choose the right one. A slider will appear once you run the following code.
library(manipulate)
manipulate(
plot(cars, xlim=c(x.min,x.max)),
x.min=slider(0,15),
x.max=slider(15,30))
I'm not aware of anyway to do this using plot(doesn't mean there isn't one). ggplot might be the way to go; it lends itself more to be being retroactively changed since it is designed around a layer system.
library(ggplot2)
#Creating our ggplot object
loop_plot <- ggplot(cars, aes(x = speed, y = dist)) +
geom_point()
#pulling out the 'auto' x & y axis limits
rangepull <- t(cbind(
ggplot_build(loop_plot)$panel$ranges[[1]]$x.range,
ggplot_build(loop_plot)$panel$ranges[[1]]$y.range))
#taking the max and min(so we don't cut out data points)
newrange <- list(cor.min = min(rangepull[,1]), cor.max = max(rangepull[,2]))
#changing our plot size to be nice and symmetric
loop_plot <- loop_plot +
xlim(newrange$cor.min, newrange$cor.max) +
ylim(newrange$cor.min, newrange$cor.max)
Note that the loop_plot object is of ggplot class, and wont actually print until its called.
I used the cars dataset in the code above to show whats going on, but just sub in your data set[s] and then do whatever postmortem your end goal is.
You'll also be able to add in titles and the like based off of the dataset name et cetera which will likely end up producing a clearer visualization out of your loop.
Hopefully this works for your needs.

Equivalent of boxplot lwd parameter for bwplot

I want to have the box plotted with thicker lines. In boxplot function I simply put lwd=2, but in the lattice bwplot I can pull my hair out and haven't found a solution!
(with the box I mean the blue thing in the image above)
Sample code to work with:
require(lattice)
set.seed(123)
n <- 300
type <- sample(c("city", "river", "village"), n, replace = TRUE)
month <- sample(c("may", "june"), n, replace = TRUE)
x <- rnorm(n)
df <- data.frame(x, type, month)
bwplot(x ~ type|month, data = df, panel=function(...) {
panel.abline(h=0, col="green")
panel.bwplot(...)
})
As John Paul pointed out, the line widths are controlled by the the box.rectangle and box.umbrella components of lattice's graphical parameter list. (For your future reference, typing names(trellis.par.get()) is a fast way to scan the list of graphical attributes controlled by that list.)
Here's a slightly cleaner way to set those options for one or more particular figures:
thickBoxSettings <- list(box.rectangle=list(lwd=2), box.umbrella=list(lwd=2))
bwplot(x ~ type|month, data = df,
par.settings = thickBoxSettings,
panel = function(...) {
panel.abline(h=0, col="green")
panel.bwplot(...)
})
One thing you can do is get the trellis settings for the box, and change those. Try
rect.settings<-trellis.par.get("box.rectangle") #gets all rectangle settings
rect.settings$lwd<-4 #sets width to 4, you can choose what you like
trellis.par.set("box.rectangle",rect.settings)
Put these above your bwplot call and it should do it.
The box rectangle settings also has color, fill etc.
Edit to add if you get box.umbrella you can edit it to change what the lines above and below the box look like.
There is a further feature of lattice plots that needs mention. They are really objects, so methods exist for modifying their list representations;
myBW <- bwplot(x ~ type|month, data = df, panel=function(...) {
panel.abline(h=0, col="green")
panel.bwplot(...)
})
newBW <- update(myBW, par.settings=list(box.rectangle=list(lwd=4) ))
plot(newBW) # need to print or plot a grid object
You can also use trellis.focus and apply further updating function to overlay new data or text.

Colorfill boxplot in R-cran with lines, dots, or similar

I need to use black and white color for my boxplots in R. I would like to colorfill the boxplot with lines and dots. For an example:
I imagine ggplot2 could do that but I can't find any way to do it.
Thank you in advance for your help!
I thought this was a great question and pondered if it was possible to do this in base R and to obtain the checkered look. So I put together some code that relies on boxplot.stats and polygon (which can draw angled lines). Here's the solution, which is really not ready for primetime, but is a solution that could be tinkered with to make more general.
boxpattern <-
function(y, xcenter, boxwidth, angle=NULL, angle.density=10, ...) {
# draw an individual box
bstats <- boxplot.stats(y)
bxmin <- bstats$stats[1]
bxq2 <- bstats$stats[2]
bxmedian <- bstats$stats[3]
bxq4 <- bstats$stats[4]
bxmax <- bstats$stats[5]
bleft <- xcenter-(boxwidth/2)
bright <- xcenter+(boxwidth/2)
# boxplot
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[1], density=angle.density)
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[2], density=angle.density)
# lines
segments(bleft,bxmedian,bright,bxmedian,lwd=3) # median
segments(bleft,bxmin,bright,bxmin,lwd=1) # min
segments(xcenter,bxmin,xcenter,bxq2,lwd=1)
segments(bleft,bxmax,bright,bxmax,lwd=1) # max
segments(xcenter,bxq4,xcenter,bxmax,lwd=1)
# outliers
if(length(bstats$out)>0){
for(i in 1:length(bstats$out))
points(xcenter,bstats$out[i])
}
}
drawboxplots <- function(y, x, boxwidth=1, angle=NULL, ...){
# figure out all the boxes and start the plot
groups <- split(y,as.factor(x))
len <- length(groups)
bxylim <- c((min(y)-0.04*abs(min(y))),(max(y)+0.04*max(y)))
xcenters <- seq(1,max(2,(len*(1.4))),length.out=len)
if(is.null(angle)){
angle <- seq(-90,75,length.out=len)
angle <- lapply(angle,function(x) c(x,x))
}
else if(!length(angle)==len)
stop("angle must be a vector or list of two-element vectors")
else if(!is.list(angle))
angle <- lapply(angle,function(x) c(x,x))
# draw plot area
plot(0, xlim=c(.97*(min(xcenters)-1), 1.04*(max(xcenters)+1)),
ylim=bxylim,
xlab="", xaxt="n",
ylab=names(y),
col="white", las=1)
axis(1, at=xcenters, labels=names(groups))
# draw boxplots
plots <- mapply(boxpattern, y=groups, xcenter=xcenters,
boxwidth=boxwidth, angle=angle, ...)
}
Some examples in action:
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2)),
x=sort(rep(1:2,200)))
drawboxplots(mydat$y, mydat$x)
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2),
rnorm(200,3,3),rnorm(400,-2,8)),
x=sort(rep(1:5,200)))
drawboxplots(mydat$y, mydat$x)
drawboxplots(mydat$y, mydat$x, boxwidth=.5, angle.density=30)
drawboxplots(mydat$y, mydat$x, # specify list of two-element angle parameters
angle=list(c(0,0),c(90,90),c(45,45),c(45,-45),c(0,90)))
EDIT: I wanted to add that one could also obtain dots as a fill by basically drawing a pattern of dots, then covering them a "donut"-shaped polygon, like so:
x <- rep(1:10,10)
y <- sort(x)
plot(y~x, xlim=c(0,11), ylim=c(0,11), pch=20)
outerbox.x <- c(2.5,0.5,10.5,10.5,0.5,0.5,2.5,7.5,7.5,2.5)
outerbox.y <- c(2.5,0.5,0.5,10.5,10.5,0.5,2.5,2.5,7.5,7.5)
polygon(outerbox.x,outerbox.y, col="white", border="white") # donut
polygon(c(2.5,2.5,7.5,7.5,2.5),c(2.5,2.5,2.5,7.5,7.5)) # inner box
But mixing that with angled lines in a single plotting function would be a bit difficult, and is generally a bit more challenging, but it starts to get you there.
I think it is hard to do this with ggplot2 since it dont use shading polygon(gris limitatipn). But you can use shading line feature in base plot, paramtered by density and angle arguments in some plot functions ( ploygon, barplot,..).
The problem that boxplot don't use this feature. So I hack it , or rather I hack bxp internally used by boxplot. The hack consist in adding 2 arguments (angle and density) to bxp function and add them internally in the call of xypolygon function ( This occurs in 2 lines).
my.bxp <- function (all.bxp.argument,angle,density, ...) {
.....#### bxp code
xypolygon(xx, yy, lty = boxlty[i], lwd = boxlwd[i],
border = boxcol[i],angle[i],density[i])
.......## bxp code after
xypolygon(xx, yy, lty = "blank", col = boxfill[i],angle[i],density[i])
......
}
Here an example. It should be noted that it is entirely the responsibility of the user to ensure
that the legend corresponds to the plot. So I add some code to rearrange the legend an the boxplot code.
require(stats)
set.seed(753)
(bx.p <- boxplot(split(rt(100, 4), gl(5, 20))))
layout(matrix(c(1,2),nrow=1),
width=c(4,1))
angles=c(60,30,40,50,60)
densities=c(50,30,40,50,30)
par(mar=c(5,4,4,0)) #Get rid of the margin on the right side
my.bxp(bx.p,angle=angles,density=densities)
par(mar=c(5,0,4,2)) #No margin on the left side
plot(c(0,1),type="n", axes=F, xlab="", ylab="")
legend("top", paste("region", 1:5),
angle=angles,density=densities)

R arrowed labelling of data points on a plot

I am looking to label data points with indices -- to identify the index number easily by visual examination.
So for instance,
x<-ts.plot(rnorm(10,0,1)) # would like to visually identify the data point indices easily through arrow labelling
Of course, if there's a better way of achieving this, please suggest
You can use arrows function:
set.seed(1); ts.plot(x <-rnorm(10,0,1), ylim=c(-1.6,1.6)) # some random data
arrows(x0=1:length(x), y0=0, y1=x, code=2, col=2, length=.1) # adding arrows
text(x=1:10, y=x+.1, 0, labels=round(x,2), cex=0.65) # adding text
abline(h=0) # adding a horizontal line at y=0
Use my.symbols from package TeachingDemos to get arrows pointing to the locations you want:
require(TeachingDemos)
d <- rnorm(10,0,1)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
my.symbols(x=1:10, y=d, ms.arrows, angle=pi/2, add=T, symb.plots=TRUE, adj=1.5)
You can use text() for this
n <- 10
d <- rnorm(n)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
text(1:n, d+par("cxy")[2]/2,col=2) # Upside
text(1:n, d-par("cxy")[2]/2,col=3) # Downside
Here a lattice version, to see the analogous of some base function.
set.seed(1234)
dat = data.frame(x=1:10, y = rnorm(10,0,1))
xyplot(y~x,data=dat, type =c('l','p'),
panel = function(x,y,...){
panel.fill(col=rgb(1,1,0,0.5))
panel.xyplot(x,y,...)
panel.arrows(x, y0=0,x1=x, y1=y, code=2, col=2, length=.1)
panel.text(x,y,label=round(y,2),adj=1.2,cex=1.5)
panel.abline(a=0)
})

Resources