I need to create a plot that compares interval censored survival curves for three species. I am able to generate a plot that shows all three curves using the ic_np function in the icenReg package in R. When I plot the output of this ic_np fit using base R plot(), a legend appears in the bottom left corner.
This example from the icenReg package documentation yields a similar figure:
library(icenReg)
data(miceData)
fit <- ic_np(cbind(l, u) ~ grp, data = miceData) #Stratifies fit by group
plot(fit)
However, having the caption in the bottom left covers the most interesting comparison of my survival curves, so I would like to move the legend to the top right.
I have seen this question about setting a legend position for basic plots in base R. Answers to this question seem to assume that I can generate a plot without the legend, but I have not been able to do that.
I have also seen this question about adding a legend to other types of survival analysis that do not seem to generate a legend by default, but I have not been able to implement these methods with interval censored data.
I have read that I can't move a legend that has already been added to a plot, but I don't know how to generate this particular plot without a legend so that I can add one back in where I want it (top right).
How can I either (a) generate this plot of interval censored Kaplan-Meier survival curves using ic_np without a legend -- maybe using some hidden parameter of plot() -- OR (b) generate this figure using a different plotting device, assuming the plot legend is then moveable?
There doesn't seem to be a help page in the package for the plot function so you need to determine the class of the fit-object and look at the code:
class(fit)
#[1] "ic_npList"
#attr(,"package")
#[1] "icenReg"
plot.ic_npList
#Error: object 'plot.ic_npList' not found
So it's not exported and we need to dig deeper (not suprising since exported functions do need to have help pages.)
getAnywhere(plot.ic_npList)
#-----------
A single object matching ‘plot.ic_npList’ was found
It was found in the following places
registered S3 method for plot from namespace icenReg
namespace:icenReg
with value
function (x, fitNames = NULL, lgdLocation = "bottomleft", ...)
{
addList <- list(xlim = x$xRange, ylim = c(0, 1), xlab = "time",
ylab = "S(t)", x = NA)
dotList <- list(...)
#.........
#..........
legend(lgdLocation, legend = grpNames, col = cols, lty = 1)
}
<bytecode: 0x7fc9784fa660>
<environment: namespace:icenReg>
So there is a location parameter for the legend placement and the obvious alternative to try is:
plot(fit, lgdLocation = "topright")
Related
I plot periodogram using spec.pgram but I don't want to use logarithmic Y scale. How can I remove it?
[
If I understand your question correctly, then spec.pgram produces on object of the class spec which are plotted using the plot.spec method, which is described in the documentation as follows:
Plotting Spectral Densities Plotting method for objects of class
"spec". For multivariate time series it plots the marginal spectra of
the series or pairs plots of the coherency and phase of the
cross-spectra.
This method uses an argument log to specify whether or not the y-axis will use the logarithmic Y scale. You can lose the logarithmic scale by setting it to "no".
# code based on the example in the plot.spec documentation
require(graphics)
spectrum(mdeaths, spans = c(3,3))
par(mfrow = c(1,2))
plot(mfdeaths.spc, log = "yes")
plot(mfdeaths.spc, log = "no")
dev.off
I try to customize the multiline graphs from the effects package.
Is there anyway to position the legend in the example below within the plotting area and not above the graph?
Alternatively: Does anyone know how to plot the results of the multiline regressions calculated by the effects package using ggplot2?
I appreciate any help.
Andy
Example:
library(effects)
data(Prestige)
mod5 <- lm(prestige ~ income*type + education, data=Prestige)
eff_cf <- effect("income*type", mod5)
print(plot(eff_cf, multiline=TRUE))
This is how you plot effect object in ggplot
library(ggplot2)
## Change effect object to dataframe
eff_df <- data.frame(eff_cf)
## Plot ggplot with legend on the bottom
ggplot(eff_df)+geom_line(aes(income,fit,linetype=type))+theme_bw()+
xlab("Income")+ylab("Prestige")+coord_cartesian(xlim=c(0,25000),ylim=c(30,110))+
theme(legend.position="bottom")
You can change xlim and ylim depending on how you want to display your data.
The output is as follows:
From ?xyplot you read :
Alternatively, the key can be positioned inside the plot region by
specifying components x, y and corner. x and y determine the location
of the corner of the key given by corner, which is usually one of
c(0,0), c(1,0), c(1,1) and c(0,1), which denote the corners of the
unit square.
and from ?plot.eff you read
key.args additional arguments to be passed to the key trellis
argument to xyplot or densityplot, e.g., to position the key (legend)
in the plotting region.
So for example you can do the following:
plot(eff_cf, multiline=TRUE,
key.args=list(x=0.2,y=0.9,corner=c(x=1, y=1)))
Based on Ruben's answer, you can try following:
library(sjPlot)
sjp.int(mod5, type = "eff", swapPredictors = T)
which will reproduce the plot with ggplot, and sjp.int also returns the plot object for further customization. However, you can also set certain legend-parameters with the sjPlot-package:
sjp.setTheme(legend.pos = "bottom right",
legend.inside = T)
sjp.int(mod5, type = "eff", swapPredictors = T)
which gives you following plot:
See sjPlot-manual for examples on how to customize plot-appearance and legend-position/size etc.
For plotting estimates of your model as forest plot, or marginal effects of all model terms, see ?sjp.lm in the sjPlot-package, or you may even try out the latest features in my package from GitHub.
#Tom Wenseleers
You can use sjPlot::sjp.int with type='eff' for this.
However, it won't give you rug plots and no raw data points yet either.
mod5 <- lm(prestige ~ type * income + education, data=Prestige)
library(sjPlot)
sjp.int(mod5,showCI = T, type = 'eff')
There's an argument partial.residuals = T to the effect() function.
This gives you fitted values, partial.residuals.raw and partial.residuals.adjusted.
I suppose you could merge that data on the original dataset and then plot smooths by group, but I ran into some difficulties early on (e.g. na.action=na.exclude is not respected).
I generate a plot using the package hexbin:
# install.packages("hexbin", dependencies=T)
library(hexbin)
set.seed(1234)
x <- rnorm(1e6)
y <- rnorm(1e6)
hbin <- hexbin(
x = x
, y = y
, xbin = 50
, xlab = expression(alpha)
, ylab = expression(beta)
)
## Using plot method for hexbin objects:
plot(hbin, style = "nested.lattice")
abline(h=0)
This seems to generate an S4 object (hbin), which I then plot using plot.
Now I'd like to add a horizontal line to that plot using abline, but unfortunately this gives the error:
plot.new has not yet been called
I have also no idea, how I can manipulate e.g. the position of the axis labels (alpha and beta are within the numbers), change the position of the legend, etc.
I'm familiar with OOP, but so far I could not find out how plot() handles the object (does it call certain methods of the object?) and how I can manipulate the resulting plot.
Why can't I simply draw a line onto the plot?
How can I manipulate axis labels?
Use lattice version of hex bin - hexbinplot(). With panel you can add your line, and with style you can choose different ways of visualizing hexagons. Check help for hexbinplot for more.
library(hexbin)
library(lattice)
x <- rnorm(1e6)
y <- rnorm(1e6)
hexbinplot(x ~ y, aspect = 1, bins=50,
xlab = expression(alpha), ylab = expression(beta),
style = "nested.centroids",
panel = function(...) {
panel.hexbinplot(...)
panel.abline(h=0)
})
hexbin uses grid graphics, not base. There is a similar function, grid.abline, which can draw lines on plots by specifying a slope and intercept, but the co-ordinate system used is confusing:
grid.abline(325,0)
gets approximately what you want, but the intercept here was found by eye.
You will have more luck using ggplot2:
library(ggplot2)
ggplot(data,aes(x=alpha,y=beta)) + geom_hex(bins=10) + geom_hline(yintercept=0.5)
I had a lot of trouble finding a lot of basic plot adjustments (axis ranges, labels, etc.) with the hexbin library but I figured out how to export the points into any other plotting function:
hxb<-hexbin(x=c(-15,-15,75,75),
y=c(-15,-15,75,75),
xbins=12)
hxb#xcm #gives the x co-ordinates of each hex tile
hxb#ycm #gives the y co-ordinates of each hex tile
hxb#count #gives the cell size for each hex tile
points(x=hxb#xcm, y=hxb#ycm, pch=hxb#count)
You can just feed these three vectors into any plotting tool you normally use.. there is the usual tweaking of size scaling, etc. but it's far better than the stubborn hexplot function. The problem I found with the ggplot2 stat_binhex is that I couldn't get the hexes to be different sizes... just different colors.
if you really want hexagons, plotrix has a hexagon drawing function that i think is fine.
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.
I've long puzzled over a concise way to communicate significance of an interaction between numeric and categorical variables in a line plot (response on the Y-axis, numeric predictor variable on the X-axis, and each level of the categoric variable a line of a different color or pattern plotted on those axes). I finally came up with the idea of drawing the traditional "brackets and p-values" connecting legend keys instead of lines of data.
Here is a mockup of what I mean:
library(ggplot2);
mydat <- do.call(rbind,lapply(1:3,function(ii) data.frame(
y=seq(0,10)*c(.695,.78,1.39)[ii]+c(.322,.663,.847)[ii],
a=factor(ii-1),b=0:10)));
myplot <- ggplot(data=mydat,aes(x=b,y=y,colour=a,group=a)) +
geom_line()+theme(legend.position=c(.1,.9));
# Plotting with p-value bracket:
myplot +
# The three line segments making up the bracket
geom_segment(x=1.2,xend=1.2,y=13.8,yend=13) +
geom_segment(x=1.1,xend=1.2,y=13,yend=13) +
geom_segment(x=1.1,xend=1.2,y=13.8,yend=13.8) +
# The text accompanying the bracket.
geom_text(label='p < 0.001',x=2,y=13.4);
This is less cluttered than trying to plot brackets someplace on the line-plot itself.
The problem is that the x and y values for the geom_segments and geom_text were obtained by trial and error and for another dataset these coordinates would be completely wrong. That's a problem if I'm trying to write a function whose purpose is to automate the process of pulling these contrasts out of models and plotting them (kind of like the effects package, but with more flexibility about how to represent the data).
My question is: is there a way to somehow pull the actual coordinates of each box comprising the legend and convert them to the scale used by geom_segment and geom_text, or manually specify the coordinates of each box when creating the myplot object, or reliably predict where the individual boxes will be and convert them to the plot's scale given that myplot$theme$legend.position returns 0.1 0.9?
I'd like to do this within ggplot2, because it's robust, elegant, and perfect for all the other things I want to do with my script. I'm open to using additional packages that extend ggplot2 and I'm also open to other approaches to visually indicating significance level on line-plots. However, suggestions that amount to "you shouldn't even do that" are not constructive-- because whether or not I personally agree with you, my collaborators and their editors don't read Stackoverflow (unfortunately).
Update:
This question kind of simplifies to: if the myplot$theme$legend.key.height is in lines and myplot$theme$legend.position seems to be roughly in fractions of the overall plot area (but not exactly) how can I convert these to the units in which the x and y axes are delineated, or alternatively, convert the x and y axis scales to the units of legend.key.height and legend.position?
I don't know the answer to your question as posed. But, another, definitely quickly do-able if less fancy approach to convey the information is to change the names of the levels so that the level names include significance codes. In your first example, you could use
levels(mydat$a) <- list("0" = "0", "1 *" = "1", "2 *" = "2")
And then the legend will reflect this:
With more levels and combos of significance, you could probably work out a set of symbols. Then mention in your figure legend the p level reflected in each set of symbols.
This might be a related way to convey the information: The figure below is produced by rxnNorm in HandyStuff here. Unfortunately, this is another non-answer as I have not been able to make this work with the new version of ggplot2. Hopefully I can figure it out soon.
My answer is not using ggplot2, but the lattice package. I think dotplot is what I would use if I want to compare a continuous variable versus categorical variables.
Here I use dotplot in 2 manners, one where I reproduce your plot, and another where
library(lattice)
library(latticeExtra) ## to get ggplot2 theme
#y versus levels of B, in different panel of A
p1 <- dotplot(b~y|a ,
data = mydat,
groups = a,
type = c("p", "h"),
main = "interaction between numeric and categorical variables ",
xlab = "continuous value",
par.settings = ggplot2like())
#y versus levels of B , grouped by a(color and line are defined by a)
p2 <- dotplot(b~y, groups= a ,
data = mydat,
type = c("l"),
main = "interaction between numeric and categorical variables ",
xlab = "continuous value",
par.settings = ggplot2like())
library(gridExtra) ## to arrange many grid plots
grid.arrange(p1,p2)