How to draw a smooth curve passing through some points - r

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)

Related

Contour plot via Scatter plot

Scatter plots are useless when number of plots is large.
So, e.g., using normal approximation, we can get the contour plot.
My question: Is there any package to implement the contour plot from scatter plot.
Thank you #G5W !! I can do it !!
You don't offer any data, so I will respond with some artificial data,
constructed at the bottom of the post. You also don't say how much data
you have although you say it is a large number of points. I am illustrating
with 20000 points.
You used the group number as the plotting character to indicate the group.
I find that hard to read. But just plotting the points doesn't show the
groups well. Coloring each group a different color is a start, but does
not look very good.
plot(x,y, pch=20, col=rainbow(3)[group])
Two tricks that can make a lot of points more understandable are:
1. Make the points transparent. The dense places will appear darker. AND
2. Reduce the point size.
plot(x,y, pch=20, col=rainbow(3, alpha=0.1)[group], cex=0.8)
That looks somewhat better, but did not address your actual request.
Your sample picture seems to show confidence ellipses. You can get
those using the function dataEllipse from the car package.
library(car)
plot(x,y, pch=20, col=rainbow(3, alpha=0.1)[group], cex=0.8)
dataEllipse(x,y,factor(group), levels=c(0.70,0.85,0.95),
plot.points=FALSE, col=rainbow(3), group.labels=NA, center.pch=FALSE)
But if there are really a lot of points, the points can still overlap
so much that they are just confusing. You can also use dataEllipse
to create what is basically a 2D density plot without showing the points
at all. Just plot several ellipses of different sizes over each other filling
them with transparent colors. The center of the distribution will appear darker.
This can give an idea of the distribution for a very large number of points.
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=c(seq(0.15,0.95,0.2), 0.995),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.15, lty=1, lwd=1)
You can get a more continuous look by plotting more ellipses and leaving out the border lines.
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=seq(0.11,0.99,0.02),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.05, lty=0)
Please try different combinations of these to get a nice picture of your data.
Additional response to comment: Adding labels
Perhaps the most natural place to add group labels is the centers of the
ellipses. You can get that by simply computing the centroids of the points in each group. So for example,
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=c(seq(0.15,0.95,0.2), 0.995),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.15, lty=1, lwd=1)
## Now add labels
for(i in unique(group)) {
text(mean(x[group==i]), mean(y[group==i]), labels=i)
}
Note that I just used the number as the group label, but if you have a more elaborate name, you can change labels=i to something like
labels=GroupNames[i].
Data
x = c(rnorm(2000,0,1), rnorm(7000,1,1), rnorm(11000,5,1))
twist = c(rep(0,2000),rep(-0.5,7000), rep(0.4,11000))
y = c(rnorm(2000,0,1), rnorm(7000,5,1), rnorm(11000,6,1)) + twist*x
group = c(rep(1,2000), rep(2,7000), rep(3,11000))
You can use hexbin::hexbin() to show very large datasets.
#G5W gave a nice dataset:
x = c(rnorm(2000,0,1), rnorm(7000,1,1), rnorm(11000,5,1))
twist = c(rep(0,2000),rep(-0.5,7000), rep(0.4,11000))
y = c(rnorm(2000,0,1), rnorm(7000,5,1), rnorm(11000,6,1)) + twist*x
group = c(rep(1,2000), rep(2,7000), rep(3,11000))
If you don't know the group information, then the ellipses are inappropriate; this is what I'd suggest:
library(hexbin)
plot(hexbin(x,y))
which produces
If you really want contours, you'll need a density estimate to plot. The MASS::kde2d() function can produce one; see the examples in its help page for plotting a contour based on the result. This is what it gives for this dataset:
library(MASS)
contour(kde2d(x,y))

Drawing sawtooth graph with lines() in R

I'd like to draw a graph which has a sawtooth shape, connecting some points.The problem is that when I use lines() I can't get the correct pattern
These are the points:
I'd like to get the following lines (red line):
and
With lines() I don't get the correct plot. I tried these codes:
A)
lines(opsOK$X, opsOK$VariableCost, lty=2, col=cols[2])
points(opsOK$X, opsOK$VariableCost)
B)
lines(opsOK$X, opsOK$VariableCost, type="s", lty=2, col=cols[2])
points(opsOK$X, opsOK$VariableCost)
Is there a way I can draw these graphs?
Thanks a lot!
As R. Schifini pointed out, it's necessary to add the missing points to the graph.

Draw 3 curves together in the same graph and scale

I need to draw three curves together in the same graph with the same scale. I know how to draw two curves together, as the following code:
r=0.8
z=2
k=seq(0,5,by=0.1)
y1=(z^2+k*r)/(r*z+k)
y2=z*(z+k*r)/(r+k)
plot(k,y1,type='l',ylab=' ',col="red",ylim=range(c(y1,y2)))
par(new=TRUE)
plot(k,y2,type='l',col="green",ylim=range(c(y1,y2)))
It works fine, but I don't know how to add the third curve, means how to set ylim.
Any help is appreciated.
Use lines
r=0.8
z=2
k=seq(0,5,by=0.1)
y1=(z^2+k*r)/(r*z+k)
y2=z*(z+k*r)/(r+k)
y3=0.8*y2
ymin=min(c(y1,y2,y3))
ymax=max(c(y1,y2,y3))
plot(k,y1,type='l',ylab='lines',col="red",ylim=c(ymin,ymax))
par(new=TRUE)
lines(k,y2,type='l',col="green",ylim=range(c(y1,y2)))
lines(k,y3)
Here is another example, with data you have provided as a comment
r=0.8
z=3
p=seq(0.1,5,by=0.1)
y1=(p*z^2+r*z)/(p*r*z+1)
y2=z*(p+r)/(p*r+1)
y3=(p^2*z*2-1+sqrt((p^2*z*2)^2+4*p^2*r*2*z*2))/(2*p*2*r*z)
ymin=-1
ymax=max(c(y1,y2,y3))
plot(p,y1,type='l',ylab='lines',col="red",ylim=c(ymin,ymax))
par(new=TRUE)
lines(p,y2,type='l',col="green",ylim=range(c(y1,y2)))
lines(p,y3, col="blue")

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.

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

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

Resources