Plotting coordinate data on a torus in R - r

I'm importing coordinate data from a model (NetLogo) and am trying to plot it in R. In the NetLogo model the size of the area is given as a 221 x 221 torus with maximum x and y values of 110 and minimum x and y values also of 110.
My code to plot the data, grid the area and extract the number of points per patch is as follows:
x<-c(1:50)
y<-c(1:50)
plot(x, y, pch = 16, xlim=c(-110,110), ylim=c(-110,110))
grid(110,110,lty=1)
xt<-cut(x,seq(-110,110,1))
yt<-cut(y,seq(-110,110,1))
count<-as.vector(table(xt,yt))
table(count)
But when I do this it's obviously giving me the number of patches as 48400. How do I properly set the values so it's 48841 (i.e. 221 x 221)?

-2, -1, 0, 1, 2
5 objects mean 4 intervals, so the plot has (221 - 1)^ 2 patches.

Related

R rgl 3d log scale plot and Antenna pattern plots

first of all before my sharing my problem I want to share a bit of code that might be helpful for some people outside there. I have been looking quite some time code to plot in 3d antenna measurements but I could not find code that does that. The problem is that antenna measurements have polar coordinates and typical 3d plot functions use cartesian coordinates. So my code below does just that (I am not an advanced programmer so I am sure someone might be able to optimize it for its use). The code can be run directly and I added comments to make it easier readable.
require("rgl")
require("fields")
degreeToRadian<-function(degree){
return (0.01745329252*degree)
}
turnPolarToX<-function(Amplitude,Coordinate){
return (Amplitude*cos(degreeToRadian(Coordinate)))
}
turnPolarToY<-function(Amplitude,Coordinate){
return (Amplitude*sin(degreeToRadian(Coordinate)))
}
# inputs for the code
test<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
test2<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
test3<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
# My three input vectors above are considered to be dBm values, typically unit for antenna or propagation measurements
# I want to plot those on three different 3d planes the XY, the YZ and the ZX. Since the rgl does not support
# polar coordinates I need to cast my polar coordinates to cartesian ones, using the three functions
# defined at the beginning. I also need to change my dBm values to their linear relative ones that are the mW
# Convert my dBm to linear ones
test<-10^(test/10)
test2<-10^(test2/10)
test3<-10^(test3/10)
# Start preparing the data to be plotted in cartesian domain
X1<-turnPolarToX(test,1:359)
Y1<-turnPolarToY(test,1:359)
Z1<-rep(0,359)
X2<-turnPolarToX(test2,1:359)
Y2<-rep(0,359)
Z2<-turnPolarToY(test2,1:359)
X3<-rep(0,359)
Y3<-turnPolarToX(test3,1:359)
Z3<-turnPolarToY(test3,1:359)
# Time for the plotting now
Min<-min(test,test2,test3)
Max<-max(test,test2,test3)
bgplot3d( suppressWarnings (
image.plot( legend.only=TRUE, legend.args=list(text='dBm/100kHz'), zlim=c(Min,Max),col=plotrix::color.scale(seq(Min,Max,length.out=21),c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)))
) # zlim is the colorbar numbers
)
# for below alternatively you can also use the lines3d to get values
points3d(X1,Y1,Z1,col=plotrix::color.scale(test,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
points3d(X2,Y2,Z2,col=plotrix::color.scale(test2,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
points3d(X3,Y3,Z3,col=plotrix::color.scale(test3,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
The problem I have now is that my plotting ideally I would like to be on a log scale that the rgl packet does not support! If I try to use log on my X,Y,Z to compress them I get an error that log is not defined for negative numbers (of course that is correct). How would you think to solve that problem on compressing the axes values when log scale plotting is not supported?
I would like to thank you for your reply
Regards
Alex
It doesn't make sense to apply a log scale to X, Y and Z. Just apply it to your original data, and transform the logged values to polar coordinates.
Since your logged test values are negative, you probably will want to apply an offset; polar coordinates with negative radius values are pretty hard to interpret.
Once you have done that, you can use the axis3d() function to add an axis with arbitrary labels to the plot. For example, if you want the origin to correspond to -50 dBm, you'd skip the transformation to linear coordinates and just add 50. You need to undo this when calculating labels. Here's your example, modified:
require("rgl")
require("fields")
degreeToRadian<-function(degree){
return (0.01745329252*degree)
}
turnPolarToX<-function(Amplitude,Coordinate){
return (Amplitude*cos(degreeToRadian(Coordinate)))
}
turnPolarToY<-function(Amplitude,Coordinate){
return (Amplitude*sin(degreeToRadian(Coordinate)))
}
# inputs for the code
test<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
test2<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
test3<-runif(359,min=-50,max=-20) # the 359 elements correspond to the polar coordinates of 1 to 359
# Add an offset of 50 to the values.
test <- test + 50
test2 <- test2 + 50
test3 <- test3 + 50
# Start preparing the data to be plotted in cartesian domain
X1<-turnPolarToX(test,1:359)
Y1<-turnPolarToY(test,1:359)
Z1<-rep(0,359)
X2<-turnPolarToX(test2,1:359)
Y2<-rep(0,359)
Z2<-turnPolarToY(test2,1:359)
X3<-rep(0,359)
Y3<-turnPolarToX(test3,1:359)
Z3<-turnPolarToY(test3,1:359)
# Time for the plotting now
Min<-min(test,test2,test3)
Max<-max(test,test2,test3)
bgplot3d( suppressWarnings (
image.plot( legend.only=TRUE, legend.args=list(text='dBm/100kHz'), zlim=c(Min,Max)-50,col=plotrix::color.scale(seq(Min-50,Max-50,length.out=21),c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)-50))
) # zlim is the colorbar numbers
)
# for below alternatively you can also use the lines3d to get values
points3d(X1,Y1,Z1,col=plotrix::color.scale(test,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
points3d(X2,Y2,Z2,col=plotrix::color.scale(test2,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
points3d(X3,Y3,Z3,col=plotrix::color.scale(test3,c(0,1,1),c(0,1,0),0,xrange=c(Min,Max)),add=TRUE)
# Add axes
labels <- pretty(c(-50, -20))
axis3d("x", at = labels + 50, labels = labels, pos = c(NA, 0, 0) )
axis3d("y", at = labels + 50, labels = labels, pos = c(0, NA, 0) )
axis3d("z", at = labels + 50, labels = labels, pos = c(0, 0, NA) )
One my system it produces this display:
You might want to add circles to show how the scale continues around in each plane. This code would do it:
theta <- seq(0, 2*pi, len = 100)
for (i in seq_along(labels)) {
x <- (labels[i] + 50)*cos(theta)
y <- (labels[i] + 50)*sin(theta)
lines3d(x, y, 0)
lines3d(x, 0, y)
lines3d(0, x, y)
}
I find the plot too busy with those added, but you can try it and decide for yourself.

Filling a curve with points that fit under the curve in R plot

I was wondering how I can efficiently (using short R code) fill a curve with points that can fill up the area under my curve?
I have tried something without success, here is my R code:
data = rnorm(1000) ## random data points to fill the curve
curve(dnorm(x), -4, 4) ## curve to be filled by "data" above
points(data) ## plotting the points to fill the curve
Here's a method that uses interpolation to ensure that the plotted points won't exceed the height of the curve (although, if you want the actual point markers to not stick out above the curve, you'll need to set the threshold slightly below the height of the curve):
# Curve to be filled
c.pts = as.data.frame(curve(dnorm(x), -4, 4))
# Generate 1000 random points in the same x-interval and with y value between
# zero and the maximum y-value of the curve
set.seed(2)
pts = data.frame(x=runif(1000,-4,4), y=runif(1000,0,max(c.pts$y)))
# Using interpolation, keep only those points whose y-value is less than y(x)
pts = pts[pts$y < approx(c.pts$x,c.pts$y,xout=pts$x)$y, ]
# Plot the points
points(pts, pch=16, col="red", cex=0.7)
A method for plotting exactly a desired number of points under a curve
Responding to #d.b's comment, here's a way to get exactly a desired number of points plotted under a curve:
First, let's figure out how many random points we need to generate over the entire plot region in order to get (roughly) a target number of points under the curve. We do this as follows:
Calculate the area under the curve as a fraction of the area of the rectangle bounded by zero and the maximum height of the curve on the vertical axis, and by the width of the curve on the horizontal axis.
The number of random points we need to generate is the target number of points, divided by the area ratio calculated above.
# Area ratio
aa = sum(c.pts$y*median(diff(c.pts$x)))/(diff(c(-4,4))*max(c.pts$y))
# Target number of points under curve
n.target = 1000
# Number of random points to generate
n = ceiling(n.target/aa)
But we need more points than this to ensure we get at least n.target, because random variation will result in fewer than n.target points about half the time, once we limit the plotted points to those below the curve. So we'll add an excess.factor in order to generate more points under the curve than we need, then we'll just randomly select n.target of those points to plot. Here's a function that takes care of the entire process for a general curve.
# Plot a specified number of points under a curve
pts.under.curve = function(data, n.target=1000, excess.factor=1.5) {
# Area under curve as fraction of area of plot region
aa = sum(data$y*median(diff(data$x)))/(diff(range(data$x))*max(data$y))
# Number of random points to generate
n = excess.factor*ceiling(n.target/aa)
# Generate n random points in x-range of the data and with y value between
# zero and the maximum y-value of the curve
pts = data.frame(x=runif(n,min(data$x),max(data$x)), y=runif(n,0,max(data$y)))
# Using interpolation, keep only those points whose y-value is less than y(x)
pts = pts[pts$y < approx(data$x,data$y,xout=pts$x)$y, ]
# Randomly select only n.target points
pts = pts[sample(1:nrow(pts), n.target), ]
# Plot the points
points(pts, pch=16, col="red", cex=0.7)
}
Let's run the function for the original curve:
c.pts = as.data.frame(curve(dnorm(x), -4, 4))
pts.under.curve(c.pts)
Now let's test it with a different distribution:
# Curve to be filled
c.pts = as.data.frame(curve(df(x, df1=100, df2=20),0,5,n=1001))
pts.under.curve(c.pts, n.target=200)
n_points = 10000 #A large number
#Store curve in a variable and plot
cc = curve(dnorm(x), -4, 4, n = n_points)
#Generate 1000 random points
p = data.frame(x = seq(-4,4,length.out = n_points), y = rnorm(n = n_points))
#OR p = data.frame(x = runif(n_points,-4,4), y = rnorm(n = n_points))
#Find out the index of values in cc$x closest to p$x
p$ind = findInterval(p$x, cc$x)
#Only retain those points within the curve whose p$y are smaller than cc$y
p2 = p[p$y >= 0 & p$y < cc$y[p$ind],] #may need p[p$y < 0.90 * cc$y[p$ind],] or something
#Plot points
points(p2$x, p2$y)

Count Points in R scatter plot above/below threshold

I have a scatter plot of two samples of rna seq data normalized to a log2 scale. I am working in R. I expect most values to fall on the x=y line, however I am interested in finding the amount of points that fall outside that range to eliminate the 'noise'. How do I get the count of points above and below a certain threshold (i.e. the red lines: 1+logx, 1+logy).
plot(log2(data$SRR850589_sorted/19108931*1000000+.5),
log2(data$SRR850604_sorted/22989410*1000000+.5),
xlab="log(SRR850589)", ylab="lg(SRR85604)")
abline(0,1,col='cyan')
abline(1,1,col='red')
abline(-1,1,col='red')
For each x,y pair, use the fitted function to determine how far each y-value is from the fitted line at each x-value. For example, let's assume we want to mark points that are at least 5 units away from the fitted line (in the vertical direction):
set.seed(10)
x = runif(100, 0, 10)
y = 2*x + 5 + rnorm(100,0,5)
m1 = lm(y ~ x)
To get the count of points more than 5 units away:
table(abs(y - (coef(m1)[1] + coef(m1)[2]*x)) > 5)
FALSE TRUE
63 37
To plot the points:
plot(x,y,type="n")
abline(coef(m1))
abline(coef(m1) + c(5,0), col="red")
abline(coef(m1) - c(5,0), col="red")
points(x,y, pch=16, col=ifelse(abs(y - (coef(m1)[1] + coef(m1)[2]*x)) > 5, "blue","red"))

Shade area under a curve [duplicate]

This question already has answers here:
Shading a kernel density plot between two points.
(5 answers)
Closed 6 years ago.
I'm trying to shade an area under a curve in R. I can't quite get it right and I'm not sure why. The curve is defined by
# Define the Mean and Stdev
mean=1152
sd=84
# Create x and y to be plotted
# x is a sequence of numbers shifted to the mean with the width of sd.
# The sequence x includes enough values to show +/-3.5 standard deviations in the data set.
# y is a normal distribution for x
x <- seq(-3.5,3.5,length=100)*sd + mean
y <- dnorm(x,mean,sd)
The plot is
# Plot x vs. y as a line graph
plot(x, y, type="l")
The code I'm using to try to color under the curve where x >= 1250 is
polygon(c( x[x>=1250], max(x) ), c(y[x==max(x)], y[x>=1250] ), col="red")
but here's the result I'm getting
How can I correctly color the portion under the curve where x >= 1250
You need to follow the x,y points of the curve with the polygon, then return along the x-axis (from the maximum x value to the point at x=1250, y=0) to complete the shape. The final vertical edge is drawn automatically, because polygon closes the shape by returning to its start point.
polygon(c(x[x>=1250], max(x), 1250), c(y[x>=1250], 0, 0), col="red")
If, rather than dropping the shading all the way down to the x-axis, you prefer to have it at the level of the curve, then you can use the following instead. Although, in the example given, the curve drops almost to the x-axis, so its hard to see the difference visually.
polygon(c(x[x>=1250], 1250), c(y[x>=1250], y[x==max(x)]), col="red")

Why does R add a column to my graph?

I'm trying learn R by graphing 3 series from some data collected in a Physics lab. I have 3 csv files:
r.csv:
"Voltage","Current"
2,133
4,266
6,399
8,532
10,666
And there are two more. I load them like this:
r <- read.csv("~/Desktop/r.csv")
rs <- read.csv("~/Desktop/rs.csv")
rp <- read.csv("~/Desktop/rp.csv")
Then, I combine them into a matrix:
data <- cbind(r, rs, rp)
When I display data, I get this extra column on the left which I though was just there when the matrix was displayed:
data
Voltage Current Voltage Current Voltage Current
1 2 133 2 270 2 67.4
2 4 266 4 535 4 134.3
3 6 399 6 803 6 200.0
4 8 532 8 1070 8 267.0
5 10 666 10 1338 10 334.0
But the graph shows an extra series:
matplot(data, type = c("b"), pch=5, col = 1:5, xlab="Voltage (V)", ylab="Current (A)")
Also, why is the x axis all wrong? The data points are at x = 2, 4, 6, 8, and 10, but the graph shows them at 1, 2, 3, 4, and 5. What am I missing here?
help(matplot) for starters:
matplot(x, y, type = "p", lty = 1:5, lwd = 1, lend = par("lend"),
pch = NULL,....
says:
x,y: vectors or matrices of data for plotting. The number of rows
should match. If one of them are missing, the other is taken
as ‘y’ and an ‘x’ vector of ‘1:n’ is used. Missing values
(‘NA’s) are allowed.
so you've only given one of x and y, so matplot has treated your whole matrix as y and a vector of 1:5 on the X axis. matplot isn't psychic enough to realise your matrix is a mix of X and Y numbers.
Solution is is something like:
matplot(data[,1], data[,-c(1,3,5)],....etc...)
which gets the X coordinates from the first voltage column, and then creates the Y matrix by dropping all the voltage columns using negative indexing to leave just the current columns. data[,c(2,4,6)] would probably work just as well.
Note this only works because your three series have all the same X-values. If this isn't the case and you want to plot multiple lines then I reckon you should look into the ggplot2 package...

Resources