R scientific notation in plots - r

I have a simple plot:
#!/usr/bin/Rscript
png('plot.png')
y <- c(102, 258, 2314)
x <- c(482563, 922167, 4462665)
plot(x,y)
dev.off()
R uses 500, 1000, 1500, etc for the y axis. Is there a way I can use scientific notation for the y axis and put * 10^3 on the top of the axis like the figure below?

A similar technique is to use eaxis (extended / engineering axis) from the sfsmisc package.
It works like this:
library(sfsmisc)
x <- c(482563, 922167, 4462665)
y <- c(102, 258, 2314)
plot(x, y, xaxt="n", yaxt="n")
eaxis(1) # x-axis
eaxis(2) # y-axis

This is sort of a hacky way, but there's nothing wrong with it:
plot(x,y/1e3, ylab="y /10^3")

How you get the labels onto your axis depends upon the used plotting system.(base, ggplot2 or lattice)
You can use functions from scales package to format your axis numbers:
library(scales)
x <- 10 ^ (1:10)
scientific_format(1)(x)
[1] "1e+01" "1e+02" "1e+03" "1e+04" "1e+05" "1e+06" "1e+07" "1e+08" "1e+09" "1e+10"
Here an example using ggplot2 :
library(ggplot2)
dat <- data.frame(x = c(102, 258, 2314),
y = c(482563, 922167, 4462665))
qplot(data=dat,x=x,y=y) +
scale_y_continuous(label=scientific_format(digits=1))+
theme(axis.text.y =element_text(size=50))
EDIT The OP has a specific need. Here some ideas I used here in order to accomplish this :
You can customize your plot labels using axis function.
Use mtext to put text in the outer plot region
Use expression to profit from the plotmath features...
y <- c(102, 258, 2314)
x <- c(482563, 922167, 4462665)
plot(x,y,ylab='',yaxt='n')
mtext(expression(10^3),adj=0,padj=-1,outer=FALSE)
axis(side=2,at=y,labels=round(y/1000,2))

Related

How do I make x and y axes thicker with Plots (Julia)?

How can I make the lines for the x- and y-axes thicker in Julia Plots?
Is there a simple way to achieve this?
MWE:
using Plots
Nx, Ny = 101,101
x = LinRange(0, 100, Nx)
y = LinRange(0, 100, Ny)
foo(x,y; x0=50, y0=50, sigma =1) = exp(- ((x-x0)^2 + (y-y0)^2)/(2*sigma^2) )
NA = [CartesianIndex()] # for "newaxis"
Z = foo.(x[:,NA], y[NA,:], sigma=10);
hm = heatmap(x, y, Z, xlabel="x", ylabel="y", c=cgrad(:Blues_9), clim=(0,1))
plot(hm, tickfontsize=10, labelfontsize=14)
Leads to:
The posts I found so far suggested that this was not possible:
https://discourse.julialang.org/t/plots-jl-modify-frame-thickness/24258/4
https://github.com/JuliaPlots/Plots.jl/issues/1099
It this still so?
The actual code for my plot is much longer.
I would not like to rewrite all of it in a different plot library.
Currently, there does not seem to be an attribute for axes thickness in Plots.jl.
As a workaround, you may use the attribute thickness_scaling, which will scale the thickness of everything: lines, grid lines, axes lines, etc. Since you only want to change the thickness of axes, you need to scale down the others. Here is your example code doing that using pyplot backend.
using Plots
pyplot() # use pyplot backend
Nx, Ny = 101,101
x = LinRange(0, 100, Nx)
y = LinRange(0, 100, Ny)
foo(x,y; x0=50, y0=50, sigma =1) = exp(- ((x-x0)^2 + (y-y0)^2)/(2*sigma^2) )
NA = [CartesianIndex()] # for "newaxis"
Z = foo.(x[:,NA], y[NA,:], sigma=10);
hm = heatmap(x, y, Z, xlabel="x", ylabel="y", c=cgrad(:Blues_9), clim=(0,1))
plot(hm, tickfontsize=10, labelfontsize=14) # your previous plot
# here is the plot code that shows the same plot with thicker axes on a new window
# note that GR backend does not support `colorbar_tickfontsize` attribute
plot(hm, thickness_scaling=2, tickfontsize=10/2, labelfontsize=14/2, colorbar_tickfontsize=8/2, reuse=false)
See Julia Plots Documentation for more about plot attributes.
A simple workaround where you do not need to add attributes for all the fonts is to add verticle and horizontal lines at the limits for x and y of the plots. For example, if I have a figure fig with 4 subplots, each with the same bounds, I can use this to get a thicker box frame:
for i ∈ 1:4
vline!(fig[i], [xlim_lb, xlim_ub],
linewidth=3,
color=:black,
label=false)
hline!(fig[i], [ylim_lb, ylim_ub],
linewidth=3,
color=:black,
label=false)
end
or for the original example here, add this to the end:
frame_thickness = 5
vline!([x[1], x[end]], color=:black, linewidth=frame_thickness, label=false)
hline!([y[1], y[end]], color=:black, linewidth=frame_thickness, label=false)

Drawing an interval on the graph using vertical line in R ?

install.packages("devtools")
library(devtools)
devtools::install_github("google/CausalImpact")
library(CausalImpact)
set.seed(1)
x1 <- 100 + arima.sim(model = list(ar = 0.999), n = 100)
y <- 1.2 * x1 + rnorm(100)
y[71:100] <- y[71:100] + 10
data <- cbind(y, x1)
pre.period <- c(1, 70)
post.period <- c(71, 100)
impact <- CausalImpact(data, pre.period, post.period)
plot(impact, "cumulative")
Say i want the graph to show an interval from 71-100 with the x scales starting at 1 from the first dotted line any ideas on how to do this?
Does anyone have any idea how to add a second vertical dotted line depicting an interval on the graph? Thanks.
You can use abline() to add lines to a graph, with the argument v = 70 setting a vertical line at x = 70. I'm not sure how to restart the x-scale from that point however - it doesn't seem like something that would be possible but perhaps someone else knows how.
You can reset the axes using this.
In your initial plot command, set xaxt = "n" This ensures that the plot function does not mark the axes.
You can then draw the abline(v=70) as mentioned above.
Then use axis(1,at=seq(60,80,by=1),las=1) 1 stands for x-axis and in the at attribute, mention the x limits you want. I've put in 60 to 80 as an example.

Colorfill boxplot in R-cran with lines, dots, or similar

I need to use black and white color for my boxplots in R. I would like to colorfill the boxplot with lines and dots. For an example:
I imagine ggplot2 could do that but I can't find any way to do it.
Thank you in advance for your help!
I thought this was a great question and pondered if it was possible to do this in base R and to obtain the checkered look. So I put together some code that relies on boxplot.stats and polygon (which can draw angled lines). Here's the solution, which is really not ready for primetime, but is a solution that could be tinkered with to make more general.
boxpattern <-
function(y, xcenter, boxwidth, angle=NULL, angle.density=10, ...) {
# draw an individual box
bstats <- boxplot.stats(y)
bxmin <- bstats$stats[1]
bxq2 <- bstats$stats[2]
bxmedian <- bstats$stats[3]
bxq4 <- bstats$stats[4]
bxmax <- bstats$stats[5]
bleft <- xcenter-(boxwidth/2)
bright <- xcenter+(boxwidth/2)
# boxplot
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[1], density=angle.density)
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[2], density=angle.density)
# lines
segments(bleft,bxmedian,bright,bxmedian,lwd=3) # median
segments(bleft,bxmin,bright,bxmin,lwd=1) # min
segments(xcenter,bxmin,xcenter,bxq2,lwd=1)
segments(bleft,bxmax,bright,bxmax,lwd=1) # max
segments(xcenter,bxq4,xcenter,bxmax,lwd=1)
# outliers
if(length(bstats$out)>0){
for(i in 1:length(bstats$out))
points(xcenter,bstats$out[i])
}
}
drawboxplots <- function(y, x, boxwidth=1, angle=NULL, ...){
# figure out all the boxes and start the plot
groups <- split(y,as.factor(x))
len <- length(groups)
bxylim <- c((min(y)-0.04*abs(min(y))),(max(y)+0.04*max(y)))
xcenters <- seq(1,max(2,(len*(1.4))),length.out=len)
if(is.null(angle)){
angle <- seq(-90,75,length.out=len)
angle <- lapply(angle,function(x) c(x,x))
}
else if(!length(angle)==len)
stop("angle must be a vector or list of two-element vectors")
else if(!is.list(angle))
angle <- lapply(angle,function(x) c(x,x))
# draw plot area
plot(0, xlim=c(.97*(min(xcenters)-1), 1.04*(max(xcenters)+1)),
ylim=bxylim,
xlab="", xaxt="n",
ylab=names(y),
col="white", las=1)
axis(1, at=xcenters, labels=names(groups))
# draw boxplots
plots <- mapply(boxpattern, y=groups, xcenter=xcenters,
boxwidth=boxwidth, angle=angle, ...)
}
Some examples in action:
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2)),
x=sort(rep(1:2,200)))
drawboxplots(mydat$y, mydat$x)
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2),
rnorm(200,3,3),rnorm(400,-2,8)),
x=sort(rep(1:5,200)))
drawboxplots(mydat$y, mydat$x)
drawboxplots(mydat$y, mydat$x, boxwidth=.5, angle.density=30)
drawboxplots(mydat$y, mydat$x, # specify list of two-element angle parameters
angle=list(c(0,0),c(90,90),c(45,45),c(45,-45),c(0,90)))
EDIT: I wanted to add that one could also obtain dots as a fill by basically drawing a pattern of dots, then covering them a "donut"-shaped polygon, like so:
x <- rep(1:10,10)
y <- sort(x)
plot(y~x, xlim=c(0,11), ylim=c(0,11), pch=20)
outerbox.x <- c(2.5,0.5,10.5,10.5,0.5,0.5,2.5,7.5,7.5,2.5)
outerbox.y <- c(2.5,0.5,0.5,10.5,10.5,0.5,2.5,2.5,7.5,7.5)
polygon(outerbox.x,outerbox.y, col="white", border="white") # donut
polygon(c(2.5,2.5,7.5,7.5,2.5),c(2.5,2.5,2.5,7.5,7.5)) # inner box
But mixing that with angled lines in a single plotting function would be a bit difficult, and is generally a bit more challenging, but it starts to get you there.
I think it is hard to do this with ggplot2 since it dont use shading polygon(gris limitatipn). But you can use shading line feature in base plot, paramtered by density and angle arguments in some plot functions ( ploygon, barplot,..).
The problem that boxplot don't use this feature. So I hack it , or rather I hack bxp internally used by boxplot. The hack consist in adding 2 arguments (angle and density) to bxp function and add them internally in the call of xypolygon function ( This occurs in 2 lines).
my.bxp <- function (all.bxp.argument,angle,density, ...) {
.....#### bxp code
xypolygon(xx, yy, lty = boxlty[i], lwd = boxlwd[i],
border = boxcol[i],angle[i],density[i])
.......## bxp code after
xypolygon(xx, yy, lty = "blank", col = boxfill[i],angle[i],density[i])
......
}
Here an example. It should be noted that it is entirely the responsibility of the user to ensure
that the legend corresponds to the plot. So I add some code to rearrange the legend an the boxplot code.
require(stats)
set.seed(753)
(bx.p <- boxplot(split(rt(100, 4), gl(5, 20))))
layout(matrix(c(1,2),nrow=1),
width=c(4,1))
angles=c(60,30,40,50,60)
densities=c(50,30,40,50,30)
par(mar=c(5,4,4,0)) #Get rid of the margin on the right side
my.bxp(bx.p,angle=angles,density=densities)
par(mar=c(5,0,4,2)) #No margin on the left side
plot(c(0,1),type="n", axes=F, xlab="", ylab="")
legend("top", paste("region", 1:5),
angle=angles,density=densities)

R arrowed labelling of data points on a plot

I am looking to label data points with indices -- to identify the index number easily by visual examination.
So for instance,
x<-ts.plot(rnorm(10,0,1)) # would like to visually identify the data point indices easily through arrow labelling
Of course, if there's a better way of achieving this, please suggest
You can use arrows function:
set.seed(1); ts.plot(x <-rnorm(10,0,1), ylim=c(-1.6,1.6)) # some random data
arrows(x0=1:length(x), y0=0, y1=x, code=2, col=2, length=.1) # adding arrows
text(x=1:10, y=x+.1, 0, labels=round(x,2), cex=0.65) # adding text
abline(h=0) # adding a horizontal line at y=0
Use my.symbols from package TeachingDemos to get arrows pointing to the locations you want:
require(TeachingDemos)
d <- rnorm(10,0,1)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
my.symbols(x=1:10, y=d, ms.arrows, angle=pi/2, add=T, symb.plots=TRUE, adj=1.5)
You can use text() for this
n <- 10
d <- rnorm(n)
plot(d, type="l", ylim=c(min(d)-1, max(d)+1))
text(1:n, d+par("cxy")[2]/2,col=2) # Upside
text(1:n, d-par("cxy")[2]/2,col=3) # Downside
Here a lattice version, to see the analogous of some base function.
set.seed(1234)
dat = data.frame(x=1:10, y = rnorm(10,0,1))
xyplot(y~x,data=dat, type =c('l','p'),
panel = function(x,y,...){
panel.fill(col=rgb(1,1,0,0.5))
panel.xyplot(x,y,...)
panel.arrows(x, y0=0,x1=x, y1=y, code=2, col=2, length=.1)
panel.text(x,y,label=round(y,2),adj=1.2,cex=1.5)
panel.abline(a=0)
})

R plotting frequency distribution

I know that we normally do in this way:
x=c(rep(0.3,100),rep(0.5,700))
plot(table(x))
However, we can only get a few dots or vertical lines in the graph.
What should I do if I want 100 dots above 0.3 and 700 dots above 0.5?
Something like this?
x <- c(rep(.3,100), rep(.5, 700))
y <- c(seq(0,1, length.out=100), seq(0,1,length.out=700))
plot(x,y)
edit: (following OP's comment)
In that case, something like this should work.
x <- rep(seq(1, 10)/10, seq(100, 1000, by=100))
x.t <- as.matrix(table(x))
y <- unlist(apply(x.t, 1, function(x) seq(1,x)))
plot(x,y)
You can lay with the linetype and linewidth settings...
plot(table(x),lty=3,lwd=0.5)
For smaller numbers (counts) you can use stripchart with method="stack" like this:
stripchart(c(rep(0.3,10),rep(0.5,70)), pch=19, method="stack", ylim=c(0,100))
But stripchart does not work for 700 dots.
Edit:
The dots() function from the package TeachingDemos is probably what you want:
require(TeachingDemos)
dots(x)

Resources