I'm plotting a 3D histogram in R using the method from figure 6.15 here.
I've set scale = list(arrow=F), so that I have tick marks instead of arrows on each axis.
The plot looks fine, but I want to change the axis tick labels. My x-axis goes from 1-26, my y from 1-24, and my Z from 0-8E-6. Ideally I'd like a single label at each of the discrete x and y values, and then at some reasonable interval on the z axis.
I've tried using the scale option 'tick.number', but it seems to only take one number, or use the first in a list, so if I set it to 26 I get excess tick marks on the y axis, and the z axis looks like rubbish.
I see there is an 'at' and 'labels' options in scales, much like for 2D plots, but I can't seem to get it to work. The docs indicate it should be a list of vectors with locations and labels for each panel, so I tried:
at = list(c(1:26), c(1:24), c(2*10^-6, 4*10^-6, 8*10^-6))
but it complains:
(list) object cannot be coerces to type 'double'
I presume if I figure out how to use 'at' then 'label' should become clear.
EDIT:
Here is sample code:
library(latticeExtra)
Cg = 1:25
Cr = 1:25
freqs = rnorm(25, .5, .1)
cloud(freqs~Cg*Cr, xlim=c(27,-1), ylim=c(25,-1), panel.3d.cloud=panel.3dbars, par.settings=list(box.3d = list(col="transparent")), col.facet="grey", scales=list(arrows=F))
My data has different lengths X and Y axes, and not just the diagonal is filled, but it shows the axis problem.
From the docs of cloud (lattice):
‘at’ gives the vector of
cutpoints where the colors change
So it needs to be a vector, not a list of vectors. The error you are getting is caused by a failure to transform your list of vectors to numeric:
at = list(c(1:26), c(1:24), c(2*10^-6, 4*10^-6, 8*10^-6))
> as.numeric(at)
Error: (list) object cannot be coerced to type 'double'
The at parameter describes where the colors change, similar to levelplot. So it only needs to be one vector.
Those result needs further work, but it does satisfy your request and shows you what is meant by passing the arguments to 'scales' as a list:
cloud(freqs~Cg*Cr, xlim=c(27,-1), ylim=c(25,-1),zlim=c(0,1), # needed to add zlim
panel.3d.cloud=panel.3dbars, par.settings=list(box.3d = list(col="transparent")),
col.facet="grey",
scales=list(arrows=F, x=list(at = c(1:26), lab=c(1:26)) ,
y=list(at= c(1:24), lab=c(1:24)),
z= list(rot=20, # to prevent over-riding the tick marks
at= c(.2,.4,.8) , # data spans larger range
lab=c("2*10^-6 ", "4*10^-6 ", "8*10^-6 ") )
) )
The range of z is simply not what you offered with those small values, so you need to accept that reality. If you want to relabel the proportions, then I have shown you how to do it.
Related
I have a 2D surface plot made with ggplot which I am colouring to show the value of it's third dimension. This data is wide-ranging in the Z direction, so I have log-transformed it before doing the interpolation for plotting.
I like the default choices that the plotting algorithm has chosen for the breaks. However, the labels for those breaks are now the log values.
The current code for the colouring is:
scale_fill_viridis(breaks = waiver(), labels = waiver())
waiver() is a flag to the underlying algorithm to use default values. Ideally, I'd like to do something like
scale_fill_viridis(breaks = waiver(), labels = sprintf("%.1f", 10^waiver()))
but that breaks with a Error in 10^my_breaks : non-numeric argument to binary operator error
How can I mathematically transform the default values for the labels into the values they would be prior to the log transform?
How about something like this:
your_function <- function(x){sprintf("%.1f", 10^as.numeric(x))}
labels = scales::trans_format("identity", your_function)
your_function will take the inverse of log10, effectively undoing the log transform. scales::trans_format will reformat the given label by applying whatever function it passed to it, in this case your_function.
You can of course define the reformatting function inside trans_format but in order to make things as clear as possible I've done it in two steps.
I am trying to implement an array in R but plotting same y-values for all x values. If value is NA, then it shouldn't be plotted
I tried the following plot which shows the histogram for all 10 values.
plot(c(1,2,NA,3,4,5,3,NA,2,4),type='h', ylim=c(0,4))
However, for the case below, when I try to control the y-values, the repeated values are not considered in the plot.
plot(c(1,2,NA,3,4,5,3,NA,2,4), rep(1,10),type='h', ylim=c(0,4))
Is this possible with plot function? Please suggest if the same can be done with an alternative.
Please look again at the help page of ?plot.
In your second line you plot the y value 1 at the x values 1 to 5. The plot you get is exactly the plot you asked for, which is not the plot you cared for. In the first plot, your values are interpreted as the y values, not the x values. The x values in the plot are just the indices in the first example.
If you want to get the lines not plotted at the NA values, just do:
x <- c(1,2,NA,3,4,5,3,NA,2,4)
plot(!is.na(x), type = 'h')
Now you plot a TRUE (which is a value of 1) whenever there is a value, and FALSE (which translates to 0) whenever there is none.
This is the exact same as :
xx <- ifelse(is.na(x),0,1)
plot(xx, type = 'h')
On a sidenote: Please do not call this a histogram. A histogram represents counts for bins, this doesn't even come close to that.
plot(!is.na(c(1,2,NA,3,4,5,3,NA,2,4)),type='h', ylim=c(0,4))
I want to change x-axis in my graphic, but it doesn't work properly with axis(). Datas in the graphic are daily datas and I want to show only years. Hope someone understands me and find a solution. This is how it looks like now: enter image description here and this is how it looks like with the code >axis (1, at = seq(1800, 1975, by = 25), las=2): enter image description here
Without a reproducible code is not easy to get what could be the problem. I try a "quick and dirt" approach.
High level plots are composed by elements that are sub-composed themselves. Hence, separate drawing commands could turn in use by allowing a finer control on the plotting procedure.
In practice, the first thing to do is plot "nothing".
> plot(x, y, type = "n", xlab = "", ylab = "", axes = F)
type = "n" causes the data to not be drawn. axes = F suppresses the axis and the box around the plot. In spite of that, the plotting region is ready to show the data.
The main benefit is that now the plotting area is correctly dimensioned. Try now to add the desired x axis as you tried before.
> points(x, y) # Plots the data in the area
> axis() # Plots the desired axis with your scale
> title() # Plots the desired titles
> box() # Prints the box surrounding the plot
EDITED based on comment by #scoa
As a quick and dirty solution, you can simply enter the following line after your plot() line:
# This reads as, on axis x (1), anchored at the first (day) value of 0
# and last (day) value of 63917 with 9131 day year increments (by)
# and labels (las) perpendicular (2) to axis (for readability)
# EDITED: and AT the anchor locations, put the labels
# 1800 (year) to 1975 (year) in 25 (year) increments
axis (1, at = seq(0, 63917, by = 9131), las=2, labels=seq(1800, 1975, by=25));
For other parameters, check out ?axis. As #scoa mentioned, this is approximate. I have used 365.25 as a day-to-year conversion, but it's not quite right. It should suffice for visual accuracy at the scale you have provided. If you need precise conversion from days to years, you need to operate on your original data set first before plotting.
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'm using the rgl package in r to plot some data. As done here:
http://www.r-bloggers.com/creating-3d-geographical-plots-in-r-using-rgl/
For some reason the scale does not align with the graph.
I changed the scale of the X and Z axis to increase the relief, which I initially thought was causing the issue, but in the example below, even if I change 0.02 to 1 the issue occurs.
library(rgl)
rdata <- c(0.8926,0.8986,0.9478,0.9672,0.916,0.912,0.9324,0.9532,0.9488,0.9376,0.921,0.927,0.9728,0.956,0.9318,0.9202)
labs <-c(100,200,500,1000)
rmatrix <- matrix(rdata, nrow=4,ncol=4,)
dimnames(rmatrix) <- list(labs,labs)
y <- as.matrix(rmatrix)
x <- 0.02*(1:nrow(y))
z <- 0.02*(1:ncol(y))
rgl.surface(x, z, y, color="red", back="lines")
axis3d('x--', labels=row.names(rmatrix),color="black")
Why is this happening?
Thanks for your help!
Mat
Without supplying a value to the labels argument in axis3d, I get an axis with six tick marks. Since you supply a vector with only four values to the labels argument, it looks like axis3d recycles those values to cover all the tick marks.
Tell axis3d at what data values you'd like to place the tick marks by supplying a value to the at argument:
axis3d('x--', at = x, labels=row.names(rmatrix),color="black")
p.s. I had to add the following line before rgl.surface() to avoid getting a segfault
rgl.open()