Related
This is the result of applying ahull function from the R package alphahull to my 2D set of points. How do I remove those thick black dots from my image? Thanks!
Simply hiding points of your data seems questionable, but this solution does what you describe by getting the coordinates of the points outside the hull from the ahull object and overlays the points with invisible ones. With this you do not have to change the input data or the ahull object.
library(alphahull)
lim=5
mat <- matrix(data=rnorm(1000), ncol=2)
some_hull = ahull(mat, alpha=0.5)
plot(some_hull, xlim = c(-lim,lim), ylim = c(-lim,lim))
The arc object ahull$arcs contains an indicator in the third column which points are outside of the hull. You can use these coordinates and simply overlay slightly bigger invisible points to hide the original ones, without having to change the input data or the object.
non_members <- some_hull$arcs[which(some_hull$arcs[, 3] == 0), 1:2]
plot(some_hull, xlim = c(-lim,lim), ylim = c(-lim,lim))
points(non_members, col="white", pch=19, cex=1.2)
I want to plot a line chart. Depending on values it should change its color.
What I found is:
plot(sin(seq(from=1, to=10,by=0.1)),type="p",
col=ifelse(sin(seq(from=1, to=10,by=0.1))>0.5,"red","yellow"))
That works. But as soon as I change from type="p" to type="l" the conditional colouring disappears.
Is that behavior intended?
What is a solution with base graphics to plot a functional line with different colors?
Use segments instead of lines.
The segments function will only add to an existing plot. To create a blank plot with the correct axes and limits, first use plot with type="n" to draw "nothing".
x0 <- seq(1, 10, 0.1)
colour <- ifelse(sin(seq(from=1, to=10,by=0.1))>0.5,"red","blue")
plot(x0, sin(x0), type="n")
segments(x0=x0, y0=sin(x0), x1=x0+0.1, y1=sin(x0+0.1), col=colour)
See ?segments for more detail.
Here is a little different approach:
x <- seq(from=1, to=10, by=0.1)
plot(x,sin(x), col='red', type='l')
clip(1,10,-1,.5)
lines(x,sin(x), col='yellow', type='l')
Note that with this method the curve changes colors at exactly 0.5.
After you've drawn a line plot, you can color it with segments():
seq1 <- seq(from=1, to=10, by=0.1)
values <- sin(seq1)
s <- seq(length(seq1)-1)
segments(seq1[s], values[s], seq1[s+1], values[s+1], col=ifelse(values > 0.5, "red", "yellow"))
I would like to highlight certain points (without the adjacent lines) in an ecdf plot. The problem is, that either
a) using col, the lines left of these points get labelled as well:
b) using bg has absolutely no effect even if specifying a pch that normally uses bg:
Where is my mistake? Is there an easy way to do that (other then to extract the ecdf function data and create the plot by hand)? I prefer plain plotting over ggplot etc. Thanks in advance!
set.seed(seed=123)
dta=rnorm(20)
plot(1:2, pch=c(19, 25), col="blue", bg="red", cex=5, lwd=4)
# works perfectly (note: pch=19 only has col, no bg, whereas others (e.g. 25) have col (border) and bg (fill))
# a)
plot(ecdf(dta), pch=19, col=c("gray","red"))
# colored symbols AND lines, but I only want to color the symbols (see 1st figure above)
# b)
plot(ecdf(dta), pch=25, col="gray",bg="red")
# specifying bg does not work from plot.ecdf (see 2nd fig. above)
Would this work for you?
set.seed(seed=123)
dta=rnorm(20)
##
plot(ecdf(dta), pch=19,
col="gray",
col.01line = "gray")
lines(ecdf(dta),col="gray",
col.points=c(
rep(c("gray","red"),20)))
##
EDIT: even easier (without the additional lines call) incorporating at the aditional parameters available for plot.stepfun directly:
# nonsense colors, just to illustrate the possibility to set further parameters:
? plot.stepfun # has many more parameters!!
plot(ecdf(dta), pch=19,
col="blue",
col.points=c(
rep(c("gray","red"),20)),
verticals=TRUE, col.vert="pink",
col.01line = "green")
I have found that when I try to overlay multiple rasters using plot(...,add=T) if I try to overlay more than 3 rasters together the subsequent plot does not align the rasters properly.
My original intent was to create a categorical map of modeled landcover where the darkness of the color representing a cover class varied wrt the certainty in our model projection. To do this, I created a simple script that would loop through each cover class and plot it (e.g., forest, green color on map) using a color gradient from grey (low certainty forest prediction) to full cover color (e.g., dark green for areas are strongly predicted).
What I have found is that using this approach, after the 3rd cover is added to the plot, all subsequent rasters that are overlayed on the plot are arbitrarily misaligned. I have reversed the order of plotting of the cover classes and the same behavior is exhibited meaning it is not an issue with the individual cover class rasters. Even more puzzling in Rstudio, when I use the zoom button to closely inspect the final plot, the misalignment worsens.
Do you have any ideas of why this behavior exists? Most importantly, do you have any suggested solutions or workarounds?
The code and data on the link below has all of the behaviors described captured.
https://dl.dropboxusercontent.com/u/332961/r%20plot%20raster%20add%20issue.zip
Turn plot_gradient=F to see how if you just simply subset a same raster and add the subsets sequentially to the same plot you can replicate the issue. I have already tried setting the extent of the plot device plot(..., ext) but that did not work. I have also checked and the extent of each cover raster is the same.
Below is the figure of the misaligned cover classes. plotting to jpeg device will result in a similar image (i.e., this is not an issue of Rstudio rendering).
Strangely enough, if I zoom into the image using Rstudio, the misalignment is different
For comparison, this is how the covers should align correctly in the landscape
library(raster)
library(colorRamps)
raster_of_classes=raster("C:/r plot raster add issue/raster_of_classes.tif")
raster_of_certainty_of_classes=raster("C:/r plot raster add issue/raster_of_certainty_of_classes.tif")
endCols=c("darkorchid4", "darkorange3", "red3", "green4", "dodgerblue4") #colors to be used in gradients for each class
classes=unique(raster_of_classes)
minVal=cellStats(raster_of_certainty_of_classes, min)
tmp_i=1
addPlot=F
plot_gradient=F #this is for debug only
#classes=rev(classes) #turn this off and on to see how last 2 classes are mis aligned, regardless of plotting order
for (class in classes){
raster_class=raster_of_classes==class #create mask for individual class
raster_class[raster_class==0]=NA #remove 0s from mask so they to do not get plotted
if (plot_gradient){
raster_of_certainty_of_class=raster_of_certainty_of_classes*raster_class #apply class mask to certainty map
}else{
raster_of_certainty_of_class=raster_class #apply class mask to certainty map
}
endCol=endCols[tmp_i] #pick color for gradient
col5 <- colorRampPalette(c('grey50', endCol))
if (plot_gradient){
plot(raster_of_certainty_of_class,
col=col5(n=49), breaks=seq(minVal,1,length.out=50), #as uncertainty values range from 0 to 1 plot them with fixed range
useRaster=T, axes=FALSE, box=FALSE, add=addPlot, legend=F)
}else{
plot(raster_of_certainty_of_class,
col=endCol,
useRaster=T, axes=FALSE, box=FALSE, add=addPlot, legend=F)
}
tmp_i=tmp_i+1
addPlot=T #after plotting first class, all other classes are added
}
I had this problem too and solved it by calling the graphical parameters function, par(), with a subset of parameters, and most importantly, put the new=TRUE in the par() call, not the plot() call, before each additional plot() call. For example:
png(fullname,
width = 3000,
height= 3000)
# original par() call
par(mfrow=c(1,1), cex=3, mar=c(3,3,3,7), bg=bgcol, col=txtcol)
# first plot
plot(zreate,
maxpixels=ncell(zreate),
col=qcol,
colNA=mapbg,
xaxt='n',
yaxt='n',
ext=map_extent,
breaks=tq,
bty='n',
legend=FALSE)
#second plot and par() call
par(mfrow=c(1,1), cex=3, mar=c(3,3,3,7), bg=bgcol, col=txtcol, new=TRUE)
plot(rt,
maxpixels=ncell(rt),
col=dcol,
legend=FALSE,
xaxt='n',
yaxt='n',
ext=map_extent,
bty='n')
#third plot and par() call
par(mfrow=c(1,1), cex=3, mar=c(3,3,3,7), bg=bgcol, col=txtcol, new=TRUE)
plot(r0,
maxpixels=ncell(r0),
col="#9e9ac8",
xaxt='n',
yaxt='n',
ext=map_extent, #PRENAFILTERING fix
bty='n',
legend=FALSE)
In December 2013, I posted a question about exactly this behavior to the R-sig-geo mailing list, and got no useful response (other than a confirmation that it also happens with R versions and OS's different than my own).
Here, for the record, is the reproducible example that I used to illustrate the issue. (See the linked question for some more explanation.)
library(maptools) ## Only needs to be installed for example data
library(raster)
library(rgeos)
## Create an example raster
p <- shapefile(system.file("shapes/co37_d90.shp", package="maptools"))
p <- p[31,] ## A tall narrow county polygon
pr <- gDifference(gBuffer(p, width=.01), p)
r <- rasterize(pr, raster(extent(pr), ncol=100, nrow=100))
## These three are properly registered on one another
plot(r, col="yellow", legend=FALSE)
plot(r, col="green", legend=FALSE, add=TRUE)
plot(r, col="grey", legend=FALSE, add=TRUE)
## All subsequent "layers" are improperly shifted/skewed to right
plot(r, col="yellow", legend=FALSE, add=TRUE)
plot(r, col="blue", legend=FALSE, add=TRUE)
plot(r, col="red", legend=FALSE, add=TRUE)
plot(r, col="grey20", legend=FALSE, add=TRUE)
## Following the above, SpatialPolygons are also shifted/skewed
plot(p, border="red", lwd=2, add=TRUE)
I have run into the same problem and found an answer that is less of a hack than the previous answer. It follows the train of thought described by user "Dial".
The key is to use image(). But add in the argument maxpixels = ncell(x). This way, the resolution is maintained and pixel aggregation does not occur either as much or at all.
> x <- brick(image.path)
> plotRGB(x)
> image(brick.overlay, add = T, col = 'black', maxpixel = ncell(x))
> image(brick.overlay, add = T, col = 'yellow', maxpixel = ncell(x))
The "brick.overlay" would be some mask object, region of interest, or otherwise subsetted data where data is associated with those pixels and all other pixels are NA.
The brick.overlay object should have to have an implied extent based on total number of pixels where all non-interest pixels are NA.
This is hardly the most memory efficient way, but it's the one I know works.
If you use "Dial's" example, I think you would do:
image(r, col="yellow", add=TRUE, maxpixels = ncell(r))
Interesting problem. As you likely know, image() doesn't seem to have the same issue but generally makes uglier maps, right?
library(raster)
library(rgeos)
## Create an example raster
p <- shapefile(system.file("shapes/co37_d90.shp", package="maptools"))
p <- p[31,] ## A tall narrow county polygon
pr <- gDifference(gBuffer(p, width=.01), p)
r <- rasterize(pr, raster(extent(pr), ncol=100, nrow=100))
## These three are properly registered on one another
image(r, col="yellow")
image(r, col="green", add=TRUE)
image(r, col="grey", add=TRUE)
## All subsequent "layers" are also registered
image(r, col="yellow", add=TRUE)
image(r, col="blue", add=TRUE)
image(r, col="red", add=TRUE)
image(r, col="grey20", add=TRUE)
## Following the above, SpatialPolygons are no longer shifted/skewed
plot(p, border="red", lwd=2, add=TRUE)
I am having problems getting segments of small lengths to appear in my plot.
Assuming the following sample data:
x=c(11,22,33,44,55)
y=c(15,23,33,45,57)
z=strptime(20120101:20120105,'%Y%m%d')
If I were to create segments out of this data my segment for the third record does not show up if I want square or butt line ends. It does show up if I allow my line ends to be round lend=0.
plot(z,x,type='n')
segments(as.numeric(z),x,as.numeric(z),y,lwd=5,lend=2)
If I try this:
segments(as.numeric(z),x,as.numeric(z),y,lwd=5,lend=0)
It shows a circle at 33. Is there a way to get at the very least a flat line that will appear at 33 (hopefully in base)?
I would have used my actual data which is also doing this when the range is small for instance 33.0005 to 33.0010, but that data is huge and I was hoping solving for when they are identical would also solve for small ranges.
ETA: If lwd=15 the circle looks even more ridiculous.
Maybe segments are not the right way to approach this?
This is for a candlestick chart, so these numbers would represent open and close. I also have high and low numbers which extend beyond this range and are drawn using lwd=1 under these segments.
As #Joran points out, this may well be the "correct" behaviour.
But a kludgy workaround is to simply add an arbitrary small number to the values. This value should be small enough to not "distort" the data, but large enough to show up in your plot, given your plot device resolution.
delta <- pmax(0.2, y - x)
plot(z,x,type='n')
segments(as.numeric(z),x ,y1 = y + delta, lwd=10, lend=1)
PS. I advise against this. You have been warned.
Base graphics does supply rect. And in fact, it does what you want. Using your definitions above.
xdiff <- max(as.numeric(z)) - min(as.numeric(z))
segwidth <- xdiff/50
plot(z,x,type='n')
rect(z-segwidth/2, x, z+segwidth/2, y, col="black")
Given the edits to your question, I suspect the way to go about this is to plot points to indicate your open and close, and a segment to indicate the range.
In this way, if your open and close points are identical (or close), you get a symbol at the correct point.
x <- strptime(20120101:20120105,'%Y%m%d')
y1 <- c(11,22,33,44,55)
y2 <- c(15,23,33,45,57)
r <- range(c(y1, y2))
plot(c(x, x), c(y1, y2), type="n", xlab="Date", ylab="y")
points(x, y1, pch=18)
points(x, y2, pch=18)
segments(as.numeric(x), y0=y1, y1=y2)
There's something a little odd about "square" lineend
library(grid)
epsilon <- 1e-4
grid.newpage()
grid.points(x=c(0.5-epsilon,0.5+epsilon), y=c(0.5,0.5), pch="+", gp=gpar(cex=2), def="npc")
grid.segments(0.5-epsilon, 0.5, 0.5+epsilon, 0.5, gp=gpar(lineend="square",lwd=50, alpha=0.2))
grid.segments(0.5-epsilon, 0.5, 0.5+epsilon, 0.5, gp=gpar(lineend="round",lwd=50, alpha=0.2))
grid.segments(0.5-epsilon, 0.5, 0.5+epsilon, 0.5, gp=gpar(lineend="butt",lwd=50, alpha=0.2))
the behavior has a jump at epsilon = 0,
for epsilon=1e-4 vs
for epsilon=0
As a workaround, I would draw rectangles instead of lines; they always have at least one linewidth.
grid.newpage()
grid.rect(x=0.5, y=0.5, width=0.01, height=0, gp=gpar(fill="black", col="red", lwd=10, linejoin="mitre"))