I have a data set that I would like to plot in R that contains a factor with categories of "left" and "right". I'd like to use plot symbols that somehow indicate direction, with a simple solution being triangles that point left and right. However, the standard symbol list in R only includes triangles that point up and down (pch = 2,6 or 24,25 for example, with a complete symbol list here). What's the most straightforward method of plotting triangles that point left and right?
Note: I've tried using < and > from the ascii set (pch = 60 and 62), although I'd like symbols that have better visibility because I'll eventually be using these figures on a projector. I'll fall back on these symbols if there isn't a better solution.
plot(x=0, type="n")
text(1, .5, paste0("0.5", intToUtf8(9660)))
text(1, -.5, paste0("-0.5", intToUtf8(9650)))
Left: 9664, right: 9654
Related
I'm having some trouble creating a perspective plot that looks exactly how I want it to look. In particular, I am trying to get the mesh not to be visible at all. If you look at the image on the left you can see faint lines running between the tiles. I want it looking like the right image with no lines visible:
I specifically want a solution with graphics::persp or other base R function. I am not interested in 3rd party packages like rgl.
I obtained the right by using polygon and specifying a border color to match the col color. If I leave border=NA with polygon I get the same result as with persp. However, it seems persp just takes the first border value and re-uses it, unlike polygon which matches colors to the polygons.
This is the code used to generate the image:
nr <- nc <- 10
mx <- matrix(numeric(nr * nc), nr)
par(mai=numeric(4))
col <- gray((row(mx[-1,-1]) * col(mx[-1,-1])/((nr-1)*(nc-1))))
par(mfrow=c(1,3), mai=c(0, 0, .25, 0), pty='s')
persp(
mx, phi=90, theta=0, border=NA, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=NA')
persp(
mx, phi=90, theta=0, border=col, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=col')
plot.new()
mxpoly.x <- rbind(
c(row(mx)[-nr, -nc]), c(row(mx)[-1, -nc]), c(row(mx)[-1, -1]),
c(row(mx)[-nr, -1]), NA
)
mxpoly.y <- rbind(
c(col(mx)[-nr, -nc]), c(col(mx)[-1, -nc]), c(col(mx)[-1, -1]),
c(col(mx)[-nr, -1]), NA
)
title('Polygon')
polygon(
((mxpoly.x - 1) / (max(mxpoly.x,na.rm=TRUE) - 1)),
((mxpoly.y - 1) / (max(mxpoly.y,na.rm=TRUE) - 1)),
col=col, border=col
)
That looks like a result of antialiasing. When each cell is drawn, the background is white, so antialiasing means the border pixels are drawn in a lighter colour.
On a Mac, you can fix this by turning antialiasing off. Your first example gives
by default, but if I open the graphics device using
quartz(antialias = FALSE)
and then run the identical code, I get
Turning off antialiasing can cause jagged edges, so this might not really be an acceptable solution to your real problem if it has diagonal lines.
You might be able to get things to work by drawing the surface twice with antialiasing: the first time will show borders, the second time might still show something, but should show less. However, persp() has no add = TRUE argument, so drawing things the second time is likely to be tricky.
If you're not on a Mac, you'll need to read about the device you're using to find if it allows control of antialiasing.
Edited to add: I tried modifying the C source to the persp function
to draw the surface 2 or 3 times. The boundaries were still slightly
visible when it was drawn twice, but invisible with 3 draws.
I will try 3D printing data to make some nice visual illustration for a binary classification example.
Here is my 3D plot:
require(rgl)
#Get example data from mtcars and normalize to range 0:1
fun_norm <- function(k){(k-min(k))/(max(k)-min(k))}
x_norm <- fun_norm(mtcars$drat)
y_norm <- fun_norm(mtcars$mpg)
z_norm <- fun_norm(mtcars$qsec)
#Plot nice big spheres with rgl that I hope will look good after 3D printing
plot3d(x_norm, y_norm, z_norm, type="s", radius = 0.02, aspect = T)
#The sticks are meant to suspend the spheres in the air
plot3d(x_norm, y_norm, z_norm, type="h", lwd = 5, aspect = T, add = T)
#Nice thick gridline that will also be printed
grid3d(c("x","y","z"), lwd = 5)
Next, I wanted to add a z=0 plane, inspired by this blog here describing the r2stl written by Ian Walker. It is supposed to be the foundation of the printed structure that holds everything together.
planes3d(a=0, b=0, c=1, d=0)
However, it has no volume, it is a thin slab with height=0. I want it to form a solid base for the printed structure, which is meant to keep everything together (check out the aforementioned blog for more details, his examples are great). How do I increase the thickness of my z=0 plane to achieve the same effect?
Here is the final step to exporting as STL:
writeSTL("test.stl")
One can view the final product really nicely using the open source Meshlab as recommended by Ian in the blog.
Additional remark: I noticed that the thin plane is also separate from the grids that I added on the -z face of the cube and is floating. This might also cause a problem when printing. How can I merge the grids with the z=0 plane? (I will be sending the STL file to a friend who will print for me, I want to make things as easy for him as possible)
You can't make a plane thicker. You can make a solid shape (extrude3d() is the function to use). It won't adapt itself to the bounding box the way a plane does, so you would need to draw it last.
For example,
example(plot3d)
bbox <- par3d("bbox")
slab <- translate3d(extrude3d(bbox[c(1,2,2,1)], bbox[c(3,3,4,4)], 0.5),
0,0, bbox[5])
shade3d(slab, col = "gray")
produces this output:
This still isn't printable (the points have no support), but it should get you started.
In the matlib package, there's a function regvec3d() that draws a vector space representation of a 2-predictor multiple regression model. The plot method for the result of the function has an argument show.base that draws the base x1-x2 plane, and draws it thicker if show.base >0.
It is a simple hack that just draws a second version of the plane at a small offset. Maybe this will be enough for your application.
if (show.base > 0) planes3d(0, 0, 1, 0, color=col.plane, alpha=0.2)
if (show.base > 1) planes3d(0, 0, 1, -.01, color=col.plane, alpha=0.1)
I try to make a plot similar to the top three plot of this:
I found a partial answer here, however I am unsure how to add the p-values in the scatter-plot.
Any tips?
You've already got a partial answer. If you just want to know how to put p-values on then use text. (looking at graph C).
text(x = 1.5, y = 73, 'p = 0.03')
If you want the p-values and the lines underneath, assuming you also want those caps on the lines, use arrows instead of segments.
arrows(1, 70, 2, length = 2, angle = 90, code = 3)
If you're sticking with solving this in base R that's a great learning exercise and can give you full control over your plot. However, if you just want to get it done I'd suggest the beeswarm package (you're making beeswarm plots).
As an aside, this prompted me to investigate why you get those upward curving lines in beeswarm plots. It's a consequence of the typical algorithm. The line curves upward because the positions are calculated through increasing y-values. If the next y-value is so close that the points would overlap in the y-axis it's plotted at an angle off the x position. Many points close together on Y results in upward curving lines until you get far enough along Y to go back to X. Smaller points should alleviate that. Also, the beeswarm package in R has several optional algorithms that avoid that as well.
t = 0:%pi/50:10*%pi;
plot3d(sin(t),cos(t),t)
When I execute this code the plot is done but the line is not visible, only the box. Any ideas which property I have to change?
Thanks
The third argument should, in this case, be a matrix of the size (length arg1) x (length arg2).
You'd expect plot3d to behave like an extension of plot and plot2d but it isn't quite the case.
The 2d plot takes a vector of x and a vector of y and plots points at (x1,y1), (x2,y2) etc., joined with lines or not as per style settings. That fits the conceptual model we usually use for 2d plots - charting the relationship of one thing as a function of another, in most cases (y = f(x)). THere are other ways to use a 2d plot: scatter graphs are common but it's easy enough to produce one using the two-rows-of-data concept.
This doesn't extend smoothly to 3d though as there are many other ways you could use a 3d plot to represent data. If you gave it three vectors of coordinates and asked it to draw a line between them all what might we want to use that for? Is that the most useful way of using a 3d plot?
Most packages give you different visualisation types for the different kinds of data. Mathematica has a lot of 3d visualisation types and Python/Scipy/Mayavi2 has even more. Matlab has a number too but Scilab, while normally mirroring Matlab, in this case prefers to handle it all with the plot3d function.
I think of it like a contour plot: you give it a vector of x and a vector of y and it uses those to create a grid of (x,y) points. The third argument is then a matrix whose dimensions match those of the (x,y) grid holding the z-coordinates of each point. The first example in the docs does what I think you're after:
t=[0:0.3:2*%pi]';
z=sin(t)*cos(t');
plot3d(t,t,z);
The first line creates a column vector of length 21
-->size(t)
ans =
21. 1.
The second line computes a 21 x 21 matrix of products of the permutations of sin(t) with cos(t) - note the transpose in the cos(t') element.
-->size(z)
ans =
21. 21.
Then when it plots them it draws (x1,y1,z11), (x1,y2,x12), (x2,y2,z22) and so on. It draws lines between adjacent points in a mesh, or no lines, or just the surface.
How can I programmatically capture the X coordinate of letter "r" in "better" string in the example? I'd like to capture that coordinate in order to dynamically draw the arrow next to it. Can this be done in R? It can be done in SAS ...with some difficulty.
x<-c(1,3)
y<-c(3,3)
plot(y~x, type="c", ylim=c(-1,5), xlim=c(-3,3), col="red")
abline(h=0,v=0, lty=3)
text(0.3, -2.5, xpd=T, "Group 1 better", adj=0)
#manually draw the arrow
arrows(x0=1.3, x1=3, y0=-2.5, y1=-2.5, length=0.1, code=2,xpd=T)
The obvious approach is to count number of letters in the string and add that number to the X coordinate of the string. This approach will probably work well in most cases but may not work well in if the size/style,angle of the text changes. I am wondering if there is a an easy way to capture the last position of the string.
SAS does it by placing the arrow relative to the internal coordinates of the previous string.
I would also like to know how to capture the last coordinate of any other graphical element as well - if possible at all.
Thank you
Use strwidth to get the string width, and add an extra character's width for a bit of space:
> arrowlabel = "Group 1 better")
> sw = strwidth(arrowlabel)
> xpos = 0.3 + sw + sw/nchar(arrowlabel)
Then draw your arrow starting at x=xpos.