Convert lwd unit to user coordinates (R base graphic)? - r

How can I use the lwd to represent some quantity?
For instance:
plot(NULL, type="n", xlim=c(4,7), ylim=c(1,6), xlab="", ylab="")
points(c(5.25,5.25), c(4,5), type="l", lwd=87)
points(c(5.5,6.5), c(3.5,3.5), type="l", lwd=92)
rect(5,3,5.5,4, col="white")
I want the lines drawn with the points functions exactly as wide/tall as the rectangle. The values 87 and 92 above I found manually. Is there a way to calculate those quantities?
EDIT:
The background for the question is: I want to draw bezier curves, and I want the thickness of the curve represent my data. My first idea was to use lwd for that. Can I do better?

lwd is the wrong tool for what you're trying to do. The actual line width will change relative to user coordinates depending on how your plot window is resized (or the dimensions when you save it). You obviously know about the rect command, why not just use that? You might also look into the shape package.
--Edit--
For more complex shapes, my experience doesn't extend beyond polygon. With that, you could get the coordinates bc for a Bezier curve, and then draw a polygon around x = c(bc$x + dx, rev(bc$x - dx), y = c(bc$y + dy, rev(bc$y - dy), but I'm not sure how well that would look for a complex curve.
As an aside, you can replace points(..., type = "l") with lines(...) if you'd like. (I think it makes my code more readable.)

Related

How can I plot a smooth line over plot points, like a contour/skyline of the plot?

What I'm looking for is best explained by a picture: A line that "contours" the maxima of my points (like giving the "skyline" of the plot). I have a plot of scattered points with dense, (mostly) unique x coordinates (not equally distributed in either axis). I want a red line surfacing this plot:
What I've tried/thought of so far is, that a simple "draw as line" approach fails due to the dense nature of the data with unique x values and a lot of local maxima and minima (basically at every point). The same fact makes a mere "get maximum"-approach impossible.
Therefore I'm asking: Is there some kind of smoothing option for a plot? Or any existing "skyline" operator for a plot?
I am specifically NOT looking for a "contour plot" or a "skyline plot" (as in Bayesian skylineplot) - the terms would actually describe what I want, but unfortunately are already used for other things.
Here is a minimal version of what I'm working with so far, a negative example of lines not giving the desired results. I uploaded sample data here.
load("xy_lidarProfiles.RData")
plot(x, y,
xlab="x", ylab="y", # axis
pch = 20, # point marker style (1 - 20)
asp = 1 # aspect of x and y ratio
)
lines(x, y, type="l", col = "red") # makes a mess
You will get close to your desired result if you order() by x values. What you want then is a running maximum, which TTR::runMax() provides.
plot(x[order(x)], y[order(x)], pch=20)
lines(x[order(x)], TTR::runMax(y[order(x)], n=10), col="red", lwd=2)
You may adjust the window with the n= parameter.

Draw a circle to highlight a curve's maximum in R

I'm trying to draw a little circle to highlight the maximum of a function plotted with the curve() method. I already know the coordinates of the point, so it isn't necessary to compute them with R.
This is the code I've written to draw the curve:
curve(expr=exp(-((sum(s1, s2, s3, s4, s10, s599)-x*1599)^2)/
(2*1599*x))/sqrt(2*pi*1599*x), xlim=c(0.5, 1.5),
xlab=expression("rate"~~"[ "*s^-1*" ]"), ylab="")
I also attach a pair of images of what I have and what I'd like to do.
The curve I can draw:
The curve with the little circle:
I thank you all in advance for any help you will give.
Lorenzo
We can use points.
Example:
curve(x^2)
points(x=.5, y=.25, cex=2, col="red")
Or, more sophisticated...
v <- curve(-x^2, xlim=c(-1, 1))
points(max(v$y), v$x[which.max(v$y)], cex=2, col=2)
Another option to find the location of the maximum a bit more precisely than which.max is to use optimize.
y = function(x,s=2000) exp(-((s-x*1599)^2)/(2*1599*x))/sqrt(2*pi*1599*x)
xlim = c(0.5, 1.5)
curve(y, xlim=xlim)
maximum = optimize(y, xlim, maximum = TRUE)
points(maximum$maximum, maximum$objective, col='red')

How to get shaded background in xyplot in R?

using xyplot from the lattice package, I plot a time series over a number of years. I would to add a shaded area for some of these years to indicate that this time period was "special" (e.g. war).
Please apologize if this is trivial, but I could not figure out how to do that, so I would be happy if someone could help me out, or at least point me in the right direction. I think my main problem is that I don't really know how to approach this problem. I am still relatively new to R, and to lattice in particular.
Here a minimal example:
xyplot( rnorm(100) ~ 1:100, type="l", col="black")
In the corresponding plot, I would like the color of the background (from say x-values of 45 until 65) from the bottom to the top of the plotting area be shaded in, say, light grey.
Note that solutions that I have found so far use base graphics and the polygon-function, but there the intention is to shade the area under or above a curve, which is different from what I would like to do. I don't "just" want to shade the area below my line, or above my line. Instead I would like to shade the entire background for a given time interval.
If anyone could help me out here, I would be very grateful!
See ?panel.xblocks in the latticeExtra package:
library(latticeExtra)
x <- 1:100
xyplot( rnorm(100) ~ x, type="l", col="black") +
layer_(panel.xblocks(x, x > 20, col = "lightgrey"))
Try this:
xyplot(
rnorm(100) ~ 1:100, type="l", col="black",
panel=function (x,y,...){
panel.rect(xleft=45, xright=65,ybottom=-3, ytop=3,col="grey")
panel.xyplot(x,y,...)
}
)
The panel.rect() function controls the rectangle and is the lattice equivalent of the rect() function. It has a variety of settings that you may find useful. It is called first and then the xyplot() is put on top of it. You many need to play around a little to get your ybottom and ytop parameters to look as you like them.
trellis.focus("panel", 1, 1)
grid.rect(x =.55, , y=.5, w = .2, height=6,
gp = gpar(fill = "light grey"))
trellis.unfocus()
This differs from #JohnPaul's solution in a couple of ways (and I think his answer is better). This uses the center of the desired X-band for placement in "native coordinates" and calculates the width as 'range(xlim)/range(band)' and it modifies an existing plot. the grid.rect function is the grid packages lower level function that is used by panel.rect. I sometimes find this useful when integrating lattice panels inside the xyplot system defeats me.

How to draw a smooth curve passing through some points

I have
plot(rnorm(120), rnorm(120), col="darkblue", pch=16, xlim=c(-3,3), ylim=c(-4,4))
points(rnorm(120,-1,1), rnorm(120,2,1), col="darkred", pch=16)
points(c(-1,-1.5,-3), c(4,2,0), pch=3, cex=3)
I want to delineate a part of a graph, by drawing a smooth curve passing through a set of points.I can define 3-4 set of points but i cannot define a function. I would like to do this in R (as opposed to GIMP) as I would like to submit as SVG. What I would like to achieve is the following
Is this possible? I know this is not a sophisticated graphing question but any base R solution will do.
if I understood the question right, drawing a spline through control points should do the job:
xspline(c(-1,-1.5,-3), c(4,2,0), shape = -1)

Cannot get lines of small length to show up in plot

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"))

Resources