I want to illustrate R's par() graphics parameter command with multiple graphs, so I did a simple 2×2 layout that's graphed great. I added a single par (col = "green") command to cause the one barplot() and three hist()ograms, but it did nothing that I could see.
Here's my R script, which should be safe since I save and restore your graphics settings at the top and bottom. Apologies for the long dput() but I want you to have the data I have.
savedGraphicsParams <- par(no.readonly=TRUE)
layout(matrix(c(1, 2, 3, 4), nrow=2, byrow=TRUE))
par(col = "green") # doesn't work
attach(Lakes)
# GRAPH 1:
barplot(table(N_of_Fish), main="Fish", xlab = "No. of Fish")
# GRAPH 2:
hist(Elevation, main = "Elevation", xlab = "ft")
# GRAPH 3
hist(Surface_Area, main="Surface Area", xlab = parse(text="ft^2"))
# GRAPH 4
hist(`, main="Max Depth", xlab = "ft")
detach(Lakes)
par(savedGraphicsParams) # Reset the graphics
tl;dr Unfortunately, as far as I know, you just can't do this; you have to use col= in the individual plot calls. Picking through ?par, we find:
Several parameters can only be set by a call to ‘par()’: ...
The remaining parameters can also be set as arguments (often via
‘...’) to high-level plot functions ...
However, see the comments on ‘bg’, ‘cex’, ‘col’, ‘lty’,
‘lwd’ and ‘pch’ which may be taken as arguments to certain plot
functions rather than as graphical parameters.
(emphasis added).
I interpret this as meaning that bg et al. cannot be set globally by a call to par() (even though they're described and discussed in ?par), but must be set as arguments to individual plotting calls. I would write the code this way (also avoiding the use of attach(), which is advised against even in its own manual page ...)
plot_col <- "green"
with (Lakes,
{
barplot(table(N_of_Fish), main="Fish", xlab = "No. of Fish", col=plot_col)
hist(Elevation, main = "Elevation", xlab = "ft", col=plot_col)
hist(Surface_Area, main="Surface Area", xlab = parse(text="ft^2"), col=plot_col)
hist(Maximum_Depth, main="Max Depth", xlab = "ft", col=plot_col)
})
Related
I'm trying to produce a cumulative incidence plot for a competing hazards survival analysis using plot() in R. For some reason, the plot that is produced has a legend that I have not called. The legend is intersecting with the lines on my graph and I can't figure out how to get rid of it. Please help!
My code is as follows:
CompRisk2 <- cuminc(ftime=ADI$time_DeathTxCensor, fstatus=ADI$status, group=ADI$natADI_quart)
cols <- c("darkorange","coral1","firebrick1","firebrick4","lightskyblue","darkturquoise","dodgerblue","dodgerblue4")
par(bg="white")
plot(CompRisk2,
col=cols,
xlab="Years",
ylab="Probability of Mortality or Transplant",
xlim=c(0,10),
ylim=c(0,0.6))
Which produces the following plot:
I tried adding the following code to move the legend out of the frame, but I got an error:
legend(0,5, legend=c(11,21,31,41,12,22,32,42),
col=c("darkorange","coral1","firebrick1","firebrick4","lightskyblue","darkturquoise","dodgerblue","dodgerblue4"),
lty=1:2, cex=0.8, text.font=4, box.lty=0)
Error: Error in title(...) : invalid graphics parameter
Any help would be much appreciated!
You are using the cuminc function from the cmprsk package. This produces an object of class cuminc, which has an S3 plot method. ?plot.cuminc shows you the documentation and typing plot.cuminc shows you the code.
There is some slightly obscure code that suggests a workaround:
u <- list(...)
if (length(u) > 0) {
i <- pmatch(names(u), names(formals(legend)), 0)
do.call("legend", c(list(x = wh[1], y = wh[2], legend = curvlab,
col = color, lty = lty, lwd = lwd, bty = "n", bg = -999999),
u[i > 0]))
}
This says that any additional arguments passed in ... whose names match the names of arguments to legend will be passed to legend(). legend() has a plot argument:
plot: logical. If ‘FALSE’, nothing is plotted but the sizes are returned.
So it looks like adding plot=FALSE to your plot() command will work.
In principle you could try looking at the other arguments to legend() and see if any of them will adjust the legend position/size as you want. Unfortunately the x argument to legend (which would determine the horizontal position) is masked by the first argument to plot.cuminc.
I don't think that the ellipsis arguments are intended for the legend call inside plot.cuminc. The code offered in Ben's answer suggests that there might be a wh argument that determines the location of the legend. It is not named within the parameters as "x" in the code he offered, but is rather given as a positionally-defined argument. If you look at the plot.cuminc function you do in fact find that wh is documented.
I cannot test this because you have not offered us access to the ADI-object but my suggestion would be to try:
opar <- par(xpd=TRUE) # xpd lets graphics be placed 'outside'
plot(CompRisk2,
col=cols, wh=c(-.5, 7),
xlab="Years",
ylab="Probability of Mortality or Transplant",
xlim=c(0,10),
ylim=c(0,0.6))
par(opar) # restores original graphics parameters
It's always a bit risky to put out a code chunk without testing, but I'm happy to report that I did find a suitable test and it seems to work reasonably as predicted. Using the code below on the object in the SO question prior question about using the gg-packages for cmprsk:
library(cmprsk)
# some simulated data to get started
comp.risk.data <- data.frame("tfs.days" = rweibull(n = 100, shape = 1, scale = 1)*100,
"status.tfs" = c(sample(c(0,1,1,1,1,2), size=50, replace=T)),
"Typing" = sample(c("A","B","C","D"), size=50, replace=T))
# fitting a competing risks model
CR <- cuminc(ftime = comp.risk.data$tfs.days,
fstatus = comp.risk.data$status.tfs,
cencode = 0,
group = comp.risk.data$Typing)
opar <- par(xpd=TRUE) # xpd lets graphics be placed 'outside'
plot(CR,
wh=c(-15, 1.1), # obviously different than the OP's coordinates
xlab="Years",
ylab="Probability of Mortality or Transplant",
xlim=c(0,400),
ylim=c(0,1))
par(opar) # restores graphics parameters
I get the legend to move up and leftward from its original position.
I am using the plot function of a particular package, namely the SPEI library. This function does not appear to accept any parameters to change the way the plot looks when it is generated.
I would like to know how to remove axis values, add new ones, and (ideally) rename the x-axis after the plot has already been created.
Please note that I have seen the other similar topics (e.g: Remove plot axis values) and they are not applicable to my situation. I know that when calling the base plot functions in R, you can set xaxt = "n", axes= FALSE, etc.
Here is a quick version of what I mean:
library(SPEI)
data(wichita)
x <- spei(wichita[,'PRCP'], 1)
plot.spei(x, main = "Here's a plot")
plot.spei(x, main = "Also a plot", xaxt = "n") #Note that xaxt does not affect output
That function uses base graphics and does not allow for any parameter to be passed through the function. There is no way to remove the x-axis labels without editing the function. Here's a way to make a copy and change just the one line that needs to be edited. (Note, since this method uses line numbers it's pretty fragile, this was tested with SPEI_1.7)
my_plot_spei <- plot.spei
my_plot_spei_body <- as.list(body(my_plot_spei))
my_plot_spei_body[[c(14,4,5)]] <- quote(plot(datt, type = "n", xlab = "", ylab = label, main = main[i], ...))
body(my_plot_spei) <- as.call(my_plot_spei_body)
then this will work
x <- spei(wichita[,'PRCP'], 1)
my_plot_spei(x, main = "Here's a plot", xaxt="n")
Wanting to show the distribution of participants in a survey by level, I came upon the recently-released pyramid package and tried it. As the font on the x-axis is too large and there seem to be no other formatting choices to fix it, I realized I don't know how to add "other options" as permitted by the ... in the pyramid call.
install.packages("pyramid")
library(pyramid)
level.pyr <- data.frame(left = c(1, 4, 6, 4, 41, 17),
right = c(1, 4, 6, 4, 41, 17),
level = c("Mgr", "Sr. Mgr.", "Dir.", "Sr. Dir.", "VP", "SVP+"))
pyramid(level.pyr, Laxis = seq(2,50,6), Cstep = 1, Cgap = .5, Llab = "", Rlab = "", Clab = "Title", GL = T, Lcol = "deepskyblue", Rcol = "deepskyblue", Ldens = -1, main = "Distribution of Participants in Survey")
Agreed, the plot below looks odd because the left and the right sides are the same, not male and female. But my question remains as to how to invoke the options and do something like "Laxis.size = 2" of "Raxis.font = "bold".
Alternatives to this new package for creating pyramid plots include plotrix, grid, and base R, as demonstrated here:
population pyramid density plot in r
By the way, if there were a ggplot method, I would welcome trying it.
Contrary to Roland's and now nrussell's guesses (without apparently looking at the code) expressed in comments, the dots arguments will not be passed to pyramid's axis plotting routine, despite this being a base graphics function. The arguments are not even passed to an axis call, although that would have seemed reasonable. The x-axis tick labels are constructed with a call to text(). You could hack the text calls to accept a named argument of your choosing and it would be passed via the dots mechanism. You seem open to other options and I would recommend using plotrix::pyramid.plot since Jim Lemon does a better job of documenting his routines and it's more likely they will be using standard R plotting conventions:
library(plotrix)
pyramid.plot(lx,rx,labels=NA,top.labels=c("Male","Age","Female"),
main="",laxlab=NULL,raxlab=NULL,unit="%",lxcol,rxcol,gap=1,space=0.2,
ppmar=c(4,2,4,2),labelcex=1,add=FALSE,xlim,show.values=FALSE,ndig=1,
do.first=NULL)
with( level.pyr, pyramid.plot(lx=left, rx=right, labels=level,
gap =5, top.labels=c("", "Title", ""), labelcex=0.6))
Hoping for some pointers or some experiences insight as i'm literally losing my mind over this, been trying for 2 full days to set up the right values to have a function spit out clean simple line plots from the gbm.plot function (packages dismo & gbm).
Here's where I start. bty=n in par to turn off the box & leave me with only left & bottom axes. Gbm.plot typically spits out one plot per explanatory variable, so usually 6 plots etc, but I'm tweaking it to do one per variable & looping it. I've removed the loop & lots of other code so it's easy to see what's going on.
png(filename = "whatever.png",width=4*480, height=4*480, units="px", pointsize=80, bg="white", res = NA, family="", type="cairo-png")
par(mar=c(2.6,2,0.4,0.5), fig=c(0,1,0.1,1), las=1, bty="n", mgp=c(1.6,0.5,0))
gbm.plot(my_gbm_model,
n.plots=1,
plot.layout = c(1,1),
y.label = "",
write.title=F,
variable.no = 1, #this is part of the multiple plots thing, calls the explanatory variable
lwd=8, #this controls the width of the main result line ONLY
rug=F)
dev.off()
So this is what the starting condition looks like. Aim: make the axes & ticks thicker. That's it.
Putting "lwd=20" in par does nothing.
Adding axes=F into gbm.plot() turns the axes and their numbers off. So I conclude that the control of these axes is handled by gbm.plot, not par. Here's where it get's frustrating and crap. Accepted wisdom from searches says that lwd should control this but it only controls the wiggly centre line as per my note above. So maybe I could add axis(side=1, lwd=8) into gbm.plot() ?
It runs but inexplicably adds a smoother! (which is very thin & hard to see on the web but it's there, I promise). It adds these warnings:
In if (smooth & is.vector(predictors[[j]])) { ... :
the condition has length > 1 and only the first element will be used
Fine, R's going to be a dick for seemingly no reason, I'll keep plugging the leaks as they come up. New code with axis as before and now smoother turned off:
png(filename = "whatever.png",width=4*480, height=4*480, units="px", pointsize=80, bg="white", res = NA, family="", type="cairo-png")
par(mar=c(2.6,2,0.4,0.5), fig=c(0,1,0.1,1), las=1, bty="n", mgp=c(1.6,0.5,0))
gbm.plot(my_gbm_model,
n.plots=1,
plot.layout = c(1,1),
y.label = "",
write.title=F,
variable.no = 1,
lwd=8,
rug=F,
smooth=F,
axis(side=1,lwd=8))
dev.off()
Gives error:
Error in axis(side = 1, lwd = 8) : plot.new has not been called yet
So it's CLEARLY drawing axes within plot since I can't affect the axes from par and I can turn them off in plot. I can do what I want and make one axis bold, but that results in a smoother and warnings. I can turn the smoother off, but then it fails because it says plot.new hadn't been called. And this doesn't even account for the other axis I have to deal with, which also causes the plot.new failure if I call 2 axis sequentially and allow the smoother.
Am I the butt of a big joke here, or am I missing something obvious? It took me long enough to work out that par is supposed to be before all plots unless you're outputting them with png etc in which case it has to be between png & plot - unbelievably this info isn't in ?par. I know I'm going off topic by ranting, sorry, but yeah, 2 full days. Has this been everyone's experience of plotting in R?
I'm going to open the vodka in the freezer. I appreciate I've not put the full reproducible code here, apologies, I can do if absolutely necessary, but it's such a huge timesuck to get to reproducible stage and I'm hoping someone can see a basic logical/coding failure screaming out at them from what I've given.
Thanks guys.
EDIT: reproducibility
core data csv: https://drive.google.com/file/d/0B6LsdZetdypkWnBJVDJ5U3l4UFU
(I've tried to make these data reproducible before and I can't work out how to do so)
samples<-read.csv("data.csv", header = TRUE, row.names=NULL)
my_gbm_model<-gbm.step(data=samples, gbm.x=1:6, gbm.y=7, family = "bernoulli", tree.complexity = 2, learning.rate = 0.01, bag.fraction = 0.5))
Here's what will widen your axis ticks:
..... , lwd.ticks=4 , ...
I predict on the basis of no testing because I keep getting errors with what limited code you have provided) that it will get handled correctly in either gbm.plot or in a subsequent axis call. There will need to be a subsequent axis call, two of them in fact (because as you noted 'lwd' gets passed around indiscriminately):
png(filename = "whatever.png",width=4*480, height=4*480, units="px", pointsize=80, bg="white", res = NA, family="", type="cairo-png")
par(mar=c(2.6,2,0.4,0.5), fig=c(0,1,0.1,1), las=1, bty="n", mgp=c(1.6,0.5,0))
gbm.plot(my_gbm_model,
n.plots=1,
plot.layout = c(1,1),
y.label = "",
write.title=F,
variable.no = 1,
lwd=8,
rug=F,
smooth=F, axes="F",
axis(side=1,lwd=8))
axis(1, lwd.ticks=4, lwd=4)
# the only way to prevent `lwd` from also affecting plot line
axis(2, lwd.ticks=4, lwd=4)
dev.off()
This is what I see with a simple example:
png(); Speed <- cars$speed
Distance <- cars$dist
plot(Speed, Distance,
panel.first = lines(stats::lowess(Speed, Distance), lty = "dashed"),
pch = 0, cex = 1.2, col = "blue", axes=FALSE)
axis(1, lwd.ticks=4, lwd=4)
axis(2, lwd.ticks=4, lwd=4)
dev.off()
Why is R inconsistent with the add parameter in the plot() function?
It sometimes works and sometimes doesn't!
In this example, it takes the parameter add=TRUE with no problem:
plot(0:10, 0:10*3)
plot(identity, add=TRUE, xlim=c(0,10))
plot(function (x) { sin(x)*10 }, add=TRUE, xlim=c(0,10))
But when I issue
plot(c(2, 3, 4), c(20,10,15), add=TRUE, pch="A")
It doesn't work!! It says that "add" is not a graphical parameter.
Please do not write that I should use points() instead. I know I can use it.
I want to understand the strange behaviour of R - why does it sometimes work and sometimes not?
This is admittedly annoying and inconsistent, but it's explicable.
identity is an object of a class — function — that has a plot method (plot.function) with an add argument, while the default plot method does not have an add argument.
In general, when trying to plot object bar, you should try class(bar); if it is of class foo then try methods(class="foo") to see that it has a plot method, or methods("plot") to see that plot.foo exists. Try ?plot.foo (or help("plot.foo") to see help, or plot.foo to see the function itself. (If the method is a private function in the package mypkg you may need mypkg:::plot_foo or or getAnywhere(plot.foo) to find it.)
This is because when you call plot(0:10, 0:10*3) or plot(c(2, 3, 4), c(20,10,15)), you are indirectly calling plot.default(), which in turn calls plot.xy(), whereas the other two calls you mention are running plot.function(). add is an argument for plot.function(), but not for plot.xy().
You can get around this inconsistency by setting par(new = TRUE), but then you need to make sure that you don't add fresh axis labels or redraw the axes. EDIT: As pointed out in the comment, you have to make sure that the range is the same as the previous plot. e.g.:
plot(0:10, 0:10*3)
plot(identity, add=T, xlim=c(0,10))
plot(function (x) { sin(x)*10 }, add=T, xlim=c(0,10))
par(new = TRUE)
plot(c(2, 3, 4), c(20,10,15), pch="A",
axes = FALSE, ## don't redraw the axes
xlab = '', ylab = '', ## no fresh axis labels
xlim = c(0,10), ylim = c(0,30)) ## keep the same limits as before
As Ben Bolker mentions, methods('plot') will show you what methods can be called when running plot() - the different methods have different arguments, which are listed when you call args(plot.foo) or in the help page ?plot.foo