I´m working with R scatterplot3D and I need to use expression() in labels because I have to use some Greek letters;
my question is: is there a way to pull the y.lab name down or write it along the axis (in a diagonal position)? I went to help and packages description but nothing seems to work;
thanks in advance for any help
Maria
library(scatterplot3d)
par(mfrow=c(1,1))
A <- c(3,2,3,3,2)
B <- c(2,4,5,3,4)
D <- c(4,3,4,2,3)
scatterplot3d(A,D,B, xlab=expression(paste(x[a],"-",x[b])),
ylab=expression(x[a]),
zlab=expression(sigma^2))
You can't use any of the classic ways due to the way the scatterplot3d() function constructs the plot. It's basically plotted on top of a classic plot pane, which means the axis labels are bound to the classic positions. The z-label is printed at the real left Y-axis, and the y label is printed at the real right Y-axis.
You can use text() to get around this:
use par("usr") to get the limits of the X and Y coordinates
calculate the position you want the label on (at 90% of the horizontal position and 8% of the vertical position for example.)
use text() to place it (and possibly the parameter srt to turn the label)
This makes it a bit more generic, so you don't have try different values for every new plot you make.
Example :
scatterplot3d(A,D,B, xlab=expression(paste(x[a],"-",x[b])),
ylab="",
zlab=expression(sigma^2))
dims <- par("usr")
x <- dims[1]+ 0.9*diff(dims[1:2])
y <- dims[3]+ 0.08*diff(dims[3:4])
text(x,y,expression(x[a]),srt=45)
Gives
scatterplot3d(A,D,B, xlab=expression(paste(x[a],"-",x[b])),
ylab="",
zlab=expression(sigma^2))
mtext( expression(x[a]), side=4,las=2,padj=18, line=-4)
One does need to use fairly extreme parameter values to get the expression in the right place in that transformed spatial projection.
Related
For educational purpose I'm trying to plot a singel horizontal "numberline" with some datapoints with labels in R. I came this far;
library(plotrix)
source("spread.labels.R")
plot(0:100,axes=FALSE,type="n",xlab="",ylab="")
axis(1,pos=0)
spread.labels(c(5,5,50,60,70,90),rep(0,6),ony=FALSE,
labels=c("5","5","50","60","70","90"),
offsets=rep(20,6))
This gave me a numberline with smaller lines pointing up to (and a little bit "in") the labels from where the datapoints should lie on the numberline - but without the points itself. Can anyone give me additional or alternative R-codes for solving thess problems:
- datapoints itself still missing are not plotted,
- and labels maybe not evenly divided over the whole numberline,
- and lines come into the labels and not merely point to the labels
Thank a lot,
Benjamin Telkamp
I usually like to create plots using primitive base R graphics functions, such as points(), segments(), lines(), abline(), rect(), polygon(), text(), and mtext(). You can easily create curves (e.g. for circles) and more complex shapes using segments() and lines() across granular coordinate vectors that you define yourself. For example, see Plot angle between vectors. This provides much more control over the plot elements you create, however, it often takes more work and careful coding than more "pre-packaged" solutions, so it's a tradeoff.
For your case, it sounds to me like you're happy with what spread.labels() is trying to do, you just want the following changes:
add point symbols at the labelled points.
prevent overlap between labels and lines.
Here's how this can be done:
## define plot data
xlim <- c(0,100);
ylim <- c(0,100);
px <- c(5,5,50,60,70,90);
py <- c(0,0,0,0,0,0);
lx.buf <- 5;
lx <- seq(xlim[1]+lx.buf,xlim[2]-lx.buf,len=length(px));
ly <- 20;
## create basic plot outline
par(xaxs='i',yaxs='i',mar=c(5,1,1,1));
plot(NA,xlim=xlim,ylim=ylim,axes=F,ann=F);
axis(1);
## plot elements
segments(px,py,lx,ly);
points(px,py,pch=16,xpd=NA);
text(lx,ly,px,pos=3);
I'm trying to plot half circles using R. My final aim is to draw a circle, divided in the middle by color. The only way I have found yet is to draw two half-circles with different colors.
So I have created my own functions:
upper.half.circle <- function(x,y,r,nsteps=100,...){
rs <- seq(0,pi,len=nsteps)
xc <- x+r*cos(rs)
yc <- y+r*sin(rs)
polygon(xc,yc,...)
}
lower.half.circle <- function(x,y,r,nsteps=100,...){
rs <- seq(0,pi,len=nsteps)
xc <- x-r*cos(rs)
yc <- y-r*sin(rs)
polygon(xc,yc,...)
}
However, for some reason my half-circles end up more like half-ellipses. For example, try running:
plot(1, type="n",axes=F,xlab="", ylab="",xlim=c(0,200),ylim=c(0,200))
upper.half.circle(15,170,10,nsteps=1000,col='red')
Does anyone know why I'm having this trouble, or alternatively, knows of a better way to do what I want?
Thanks!
The problem is the default aspect ratio is not 1:1.
To fix this, set asp=1 in plot:
Inspired by this Q & A. You could have sniffed out this was the case by turning on the axes and x/y labels.
If using the grid package would be also an opportunity for you, there is a much simpler solution:
library(grid)
vp <- viewport(width=0.5, height=0.5, clip = "on")
grid.circle(0.5,0,r=0.5, gp = gpar(fill = 'red'), vp = vp)
This creates a viewport with clipping, i.e., an appropriate positioning of the filled circle creates a half circle.
If you want to add your half circles to an existing plot (and therefore cannot control the aspect ratio directly) then one option for this specific case is to use the floating.pie function from the plotrix package.
A more general tool for creating custom symbols and adding them to plots (with the symbols having a different aspect ratio from the overall plot) is to use the my.symbols function from the TeachingDemos package.
I'm trying to draw a graph looks like below using r, and was wondering if there is a way to
1) omit the lower range of y values, but still start from 0.
2) Also, instead of numerical values, how do I label each bar in the histogram with texts?
I would appreciate any help. Thanks!
The worth-a-read answer in the comments was using the same example of plotrix::gap.barplot that I picked but I've been working on those "squiggly lines":
require(plotrix)
twogrp<-c(rnorm(10)+4,rnorm(10)+20)
gap.barplot(twogrp, gap=c(8,16), xlab="Index", ytics=c(3,6,17,20),
ylab="Group values", main="Barplot with gap")
polygon(y=c( 7.5+c(-1,1)*.2*rep(1,length(twogrp)+2),
8.5+ c(-1,1)*.2*rep(1,length(twogrp)+2) ) ,
x=c(0,seq_along(twogrp), rep(length(twogrp)+1, 2), # going to the right...
rev(seq_along(twogrp)) ,0) , # and coming back to the left
col="white", border="white") # could also try border="lightblue"
There is also an axis.break function in plotrix that will give you the annotation on the axis. You would use the text function for labels inside the plot area.
I'm looking to plot a set of sparklines in R with just a 0 and 1 state that looks like this:
Does anyone know how I might create something like that ideally with no extra libraries?
I don't know of any simple way to do this, so I'm going to build up this plot from scratch. This would probably be a lot easier to design in illustrator or something like that, but here's one way to do it in R (if you don't want to read the whole step-by-step, I provide my solution wrapped in a reusable function at the bottom of the post).
Step 1: Sparklines
You can use the pch argument of the points function to define the plotting symbol. ASCII symbols are supported, which means you can use the "pipe" symbol for vertical lines. The ASCII code for this symbol is 124, so to use it for our plotting symbol we could do something like:
plot(df, pch=124)
Step 2: labels and numbers
We can put text on the plot by using the text command:
text(x,y,char_vect)
Step 3: Alignment
This is basically just going to take a lot of trial and error to get right, but it'll help if we use values relative to our data.
Here's the sample data I'm working with:
df = data.frame(replicate(4, rbinom(50, 1, .7)))
colnames(df) = c('steps','atewell','code','listenedtoshell')
I'm going to start out by plotting an empty box to use as our canvas. To make my life a little easier, I'm going to set the coordinates of the box relative to values meaningful to my data. The Y positions of the 4 data series will be the same across all plotting elements, so I'm going to store that for convenience.
n=ncol(df)
m=nrow(df)
plot(1:m,
seq(1,n, length.out=m),
# The following arguments suppress plotting values and axis elements
type='n',
xaxt='n',
yaxt='n',
ann=F)
With this box in place, I can start adding elements. For each element, the X values will all be the same, so we can use rep to set that vector, and seq to set the Y vector relative to Y range of our plot (1:n). I'm going to shift the positions by percentages of the X and Y ranges to align my values, and modified the size of the text using the cex parameter. Ultimately, I found that this works out:
ypos = rev(seq(1+.1*n,n*.9, length.out=n))
text(rep(1,n),
ypos,
colnames(df), # These are our labels
pos=4, # This positions the text to the right of the coordinate
cex=2) # Increase the size of the text
I reversed the sequence of Y values because I built my sequence in ascending order, and the values on the Y axis in my plot increase from bottom to top. Reversing the Y values then makes it so the series in my dataframe will print from top to bottom.
I then repeated this process for the second label, shifting the X values over but keeping the Y values the same.
text(rep(.37*m,n), # Shifted towards the middle of the plot
ypos,
colSums(df), # new label
pos=4,
cex=2)
Finally, we shift X over one last time and use points to build the sparklines with the pipe symbol as described earlier. I'm going to do something sort of weird here: I'm actually going to tell points to plot at as many positions as I have data points, but I'm going to use ifelse to determine whether or not to actually plot a pipe symbol or not. This way everything will be properly spaced. When I don't want to plot a line, I'll use a 'space' as my plotting symbol (ascii code 32). I will repeat this procedure looping through all columns in my dataframe
for(i in 1:n){
points(seq(.5*m,m, length.out=m),
rep(ypos[i],m),
pch=ifelse(df[,i], 124, 32), # This determines whether to plot or not
cex=2,
col='gray')
}
So, piecing it all together and wrapping it in a function, we have:
df = data.frame(replicate(4, rbinom(50, 1, .7)))
colnames(df) = c('steps','atewell','code','listenedtoshell')
BinarySparklines = function(df,
L_adj=1,
mid_L_adj=0.37,
mid_R_adj=0.5,
R_adj=1,
bottom_adj=0.1,
top_adj=0.9,
spark_col='gray',
cex1=2,
cex2=2,
cex3=2
){
# 'adJ' parameters are scalar multipliers in [-1,1]. For most purposes, use [0,1].
# The exception is L_adj which is any value in the domain of the plot.
# L_adj < mid_L_adj < mid_R_adj < R_adj
# and
# bottom_adj < top_adj
n=ncol(df)
m=nrow(df)
plot(1:m,
seq(1,n, length.out=m),
# The following arguments suppress plotting values and axis elements
type='n',
xaxt='n',
yaxt='n',
ann=F)
ypos = rev(seq(1+.1*n,n*top_adj, length.out=n))
text(rep(L_adj,n),
ypos,
colnames(df), # These are our labels
pos=4, # This positions the text to the right of the coordinate
cex=cex1) # Increase the size of the text
text(rep(mid_L_adj*m,n), # Shifted towards the middle of the plot
ypos,
colSums(df), # new label
pos=4,
cex=cex2)
for(i in 1:n){
points(seq(mid_R_adj*m, R_adj*m, length.out=m),
rep(ypos[i],m),
pch=ifelse(df[,i], 124, 32), # This determines whether to plot or not
cex=cex3,
col=spark_col)
}
}
BinarySparklines(df)
Which gives us the following result:
Try playing with the alignment parameters and see what happens. For instance, to shrink the side margins, you could try decreasing the L_adj parameter and increasing the R_adj parameter like so:
BinarySparklines(df, L_adj=-1, R_adj=1.02)
It took a bit of trial and error to get the alignment right for the result I provided (which is what I used to inform the default values for BinarySparklines), but I hope I've given you some intuition about how I achieved it and how moving things using percentages of the plotting range made my life easier. In any event, I hope this serves as both a proof of concept and a template for your code. I'm sorry I don't have an easier solution for you, but I think this basically gets the job done.
I did my prototyping in Rstudio so I didn't have to specify the dimensions of my plot, but for posterity I had 832 x 456 with the aspect ratio maintained.
I try to overlay two histograms in the same plane but the option Probability=TRUE (relative frequencies) in hist() is not effective with the code below. It is a problem because the two samples have very different sizes (length(cl1)=9 and length(cl2)=339) and, with this script, I cannot vizualize differences between both histograms because each shows frequencies. How can I overlap two histograms with the same bin width, showing relative frequencies?
c1<-hist(dataList[["cl1"]],xlim=range(minx,maxx),breaks=seq(minx,maxx,pasx),col=rgb(1,0,0,1/4),main=paste(paramlab,"Group",groupnum,"cl1",sep=" "),xlab="",probability=TRUE)
c2<-hist(dataList[["cl2"]],xlim=range(minx,maxx),breaks=seq(minx,maxx,pasx),col=rgb(0,0,1,1/4),main=paste(paramlab,"Group",groupnum,"cl2",sep=" "),xlab="",probability=TRUE)
plot(c1, col=rgb(1,0,0,1/4), xlim=c(minx,maxx), main=paste(paramlab,"Group",groupnum,sep=" "),xlab="")# first histogram
plot(c2, col=rgb(0,0,1,1/4), xlim=c(minx,maxx), add=T)
cl1Col <- rgb(1,0,0,1/4)
cl2Col <- rgb(0,0,1,1/4)
legend('topright',c('Cl1','Cl2'),
fill = c(cl1Col , cl2Col ), bty = 'n',
border = NA)
Thanks in advance for your help!
When you call plot on an object of class histogram (like c1), it calls the S3 method for the histogram. Namely, plot.histogram. You can see the code for this function if you type graphics:::plot.histogram and you can see its help under ?plot.histogram. The help file for that function states:
freq logical; if TRUE, the histogram graphic is to present a
representation of frequencies, i.e, x$counts; if FALSE, relative
frequencies (probabilities), i.e., x$density, are plotted. The default
is true for equidistant breaks and false otherwise.
So, when plot renders a histogram it doesn't use the previously specified probability or freq arguments, it tries to figure it out for itself. The reason for this is obvious if you dig around inside c1, it contains all of the data necessarily for the plot, but does not specify how it should be rendered.
So, the solution is to reiterate the argument freq=FALSE when you run the plot functions. Notably, freq=FALSE works whereas probability=TRUE does not because plot.histogram does not have a probability option. So, your plot code will be:
plot(c1, col=rgb(1,0,0,1/4), xlim=c(minx,maxx), main=paste(paramlab,"Group",groupnum,sep=" "),xlab="",freq=FALSE)# first histogram
plot(c2, col=rgb(0,0,1,1/4), xlim=c(minx,maxx), add=T, freq=FALSE)
This all seems like a oversight/idiosyncratic decision (or lack thereof) on the part of the R devs. To their credit it is appropriately documented and is not "unexpected behavior" (although I certainly didn't expect it). I wonder where such oddness should be reported, if it should be reported at all.