The function absoluteGrob {ggplot2} shows a behavior that I am not able to debug. I have ggplot2 installed and I can see the help page by ?absoluteGrob.
However, R does not find it when I try to use it:
> absoluteGrob
Error: object 'absoluteGrob' not found
More particularly, I try to execute the following code (from this answer to plot some graphs as x-labels):
library(grid)
library(ggplot2)
library(grImport)
library(igraph)
npoints <- 3
y <- rexp(npoints)
x <- seq(npoints)
pics <- vector(mode="list", length=npoints)
for(i in 1:npoints){
fileps <- paste0("motif",i,".ps")
filexml <- paste0("motif",i,".xml")
# Postscript file
postscript(file = fileps, fonts=c("serif", "Palatino"))
plot(graph.ring(i), vertex.label.family="serif", edge.label.family="Palatino")
dev.off()
# Convert to xml accessible for symbolsGrob (my_axis)
PostScriptTrace(fileps, filexml)
pics[i] <- readPicture(filexml)
}
my_axis <- function () {
function(label, x = 0.5, y = 0.5, ...) {
absoluteGrob(
do.call("gList", mapply(symbolsGrob, pics[label], x, y, SIMPLIFY = FALSE)),
height = unit(1.5, "cm")
)
}
}
qplot(factor(c("a", "b", "c")), 1:npoints) + scale_x_discrete(labels= my_axis())
But I get the error:
Error in scale$labels(breaks) : could not find function "absoluteGrob"
Any help (or alternatives) is welcome.
ggplot2 version:
ggplot2_1.0.1
Edit
Even in the simple case...
It does not work:
library(ggplot2)
absoluteGrob
It does:
library(ggplot2)
ggplot2:::absoluteGrob
The answer you linked to in your post was posted 3 years ago as of this posting, and many things in ggplot2 have changed since then. At that point, ggplot2 version 0.9.0 had not yet been released.
According to the 1.0.0 documentation for absoluteGrob, it's still experimental, which means that it was certainly experimental at the time of the linked answer. At that point it was likely exported from the ggplot2 namespace and thus available to the user. That's why the linked answer worked at the time.
However, as of version 1.0.1, it is not exported from the ggplot2 namespace. So while you're able to view the source and documentation with ggplot2:::absoluteGrob (which works for non-exported objects) and ?absoluteGrob, you will not be able to use it, even by explicitly specifying the namespace via ggplot2::absoluteGrob.
According to the source, it simply calls gTree(), which is from the grid package, with cl="absoluteGrob". You can try that in place of calling absoluteGrob() directly. For example, try the following, which will hopefully mimic the desired behavior from absoluteGrob():
grlist <- do.call("gList", mapply(symbolsGrob, pics[label], x, y, SIMPLIFY = FALSE))
gTree(children = grlist,
cl = "absoluteGrob",
height = unit(1.5, "cm"),
width = NULL,
xmin = NULL,
ymin = NULL,
vp = NULL)
Related
This appears to be similar to an issue that #baptiste was trying to work around back in 2014. I am revisiting code I wrote back in June that involved creating three ggplotGrobs and combining them with a call to cbind. Now, this code fails with the message: "Error in mmm < each : comparison of these types is not implemented."
I thought that this would be specific to my particular application, but I was able to make a very simple, reproducible example. Even if the cbind is performed on two identical ggplotGrobs, this code still fails.
library(ggplot2)
library(gtable)
# Make some plots
pl1 <- ggplot(mtcars, aes(x = disp, y = mpg)) +
geom_point()
pl2 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, colour = Species)) +
geom_point()
# Convert to grobs
pl1_grob <- ggplotGrob(pl1)
pl2_grob <- ggplotGrob(pl2)
# Bind them together -- Error!
combined_grob <- cbind(pl1_grob, pl2_grob)
The error and relevant traceback are here:
> combined_grob <- cbind(pl1_grob, pl2_grob)
Error in mmm < each : comparison of these types is not implemented
> traceback()
8: comp(x_val, y_val)
7: unit(comp(x_val, y_val), x_unit)
6: compare_unit(x$heights, y$heights, pmax)
5: cbind_gtable(x, y, size = size)
4: f(init, x[[i]])
3: Reduce(function(x, y) cbind_gtable(x, y, size = size), gtables)
2: cbind(deparse.level, ...)
1: cbind(pl1_grob, pl2_grob)
This code fails with R-3.4.2 on OS X 10.11.6 with grid_3.4.2, gtable_0.2.0, and ggplot2_2.2.1, AND with R-3.3.2 on Linux (compiled from source on Ubuntu 16.04) with grid_3.3.2, gtable_0.2.0, and ggplot2_2.2.1.
Before making the example above, I noticed that one of the vignettes in the "lemon" package ("gtable_show_lemonade") fails near the very end with the same error upon a cbind of two gtables. I confirmed this error by running the code for this vignette from source, which goes something like:
library(lemon)
edit(vignette('gtable_show_lemonade', package = 'lemon'))
# Then use whichever editor you opened to copy the temporary filename
# of the vignette source, and run this with source()
The compiled vignette (with failed cbind) is here: https://cran.r-project.org/web/packages/lemon/vignettes/gtable_show_lemonade.html
The failure during vignette compilation was obtained using the aforementioned R-3.4.2 instance running on OS X 10.11 (El Capitan).
I would appreciate any help with working around this! Since I would like to use my Mac to generate the figures (better font situation), I'm hoping there is a way I can overload a function to fix this.
UPDATE:
This is "only" a problem if size = 'max', which is the default, is specified (or size = 'min'). I think that I can work around it using size = 'first' or size = 'last', which do not perform the offending height comparisons, but it is certainly not convenient that the default behavior doesn't work (and there are certain situations size = 'max' is useful).
I never managed to get this fixed in gtable so gridExtra has a modified version called gtable_cbind.
I've had some success extending ggflags to use SVGs, and now I'm trying to make this extension play nice with ggplotly(). The Plotly documentation has a nice section on translating custom geoms, and it essentially comes down to either providing a to_basic method for your geom or a geom2trace method.
I'm gonna be honest: I'm outta my depth here, and I'm trying to cobble something together based on the two named examples: ggmosaic and ggalt. But something simple like:
#' #name plotly_helpers
#' #title Plotly helpers
#' #description Helper functions to make it easier to automatically create plotly charts
#' #importFrom plotly to_basic
#' #export
to_basic.GeomFlag <- getFromNamespace("to_basic.GeomPoint", asNamespace("plotly"))
Gives me an error when running devtools::document():
object 'to_basic.GeomPoint' not found
if I change it to GeomLine the package documents and installs, but a simple plot displays like this:
df = data_frame(
f = rep(1:5, each = 4),
x = rep(1:4, times = 5),
y = c(1:2, 2:1, 2:1, 1:2, 1:2, 2:1, 2:1, 1:2, 2:1, 1:2),
s = c(1:4, 4:1, 1:4, 4:1, 1:4),
c = rep(c('au', 'us', 'gb', 'de'), 5))
p1 = ggplot(df) + geom_flag(aes(x = x, y = y, size = s, frame = f, country = c)) + scale_country()
ggplotly(p1)
Obviously GeomLine isn't right—GeomPoint seems like a more natural point for this extension—but it could simply be that I can't reduce it and I need to implement geom2trace. But I don't really have a great idea of what needs to go in or out of either method. Can anyone help? Are there other examples of ggplot2 extensions that implement the Plotly interfaces?
EDIT: I've had a look at some of the built-in examples of geom2trace, but I'm not sure I can go much further here. I was kind of hoping I might be able to substitute custom grobs for the GeomPoint points, like with grid, but it seems like the Plotly API expects just basic point attributes.
I have downloaded and can successfully load the VennDiagram package in R. I'm currently just trying to generate some sample diagrams using this package with the example code provided by the authors of this package. However, for any of the example code that I attempt to use, no graphic is generated, and the only screen output is:
[1] 1
I located the following informative thread: Problems with VennDiagram?
...but the first suggested troubleshooting step, to confirm that we can draw the plot onscreen by assigning the call to venn.diagram() to a temporary variable and then calling grid.draw(temp) [full code shown below], results in the error message
Error in UseMethod("grid.draw") :
no applicable method for 'grid.draw' applied to an object of class "c('double', 'numeric')"
My test code is:
library(VennDiagram);
library(grid);
temp <- venn.diagram(
x = list(
A = 1:100,
B = 1:10
),
filename = "2-2_special_case_pairwise-inclusion.tiff",
cex = 2.5,
cat.cex = 2.5,
cat.pos = 0
);
grid.draw(temp)
Other threads that address this error message suggest that the arguments to grid.draw() should be modified, but I'm not sure why the example above would have been suggested and accepted if it didn't work, and the grid.draw documentation (https://www.rdocumentation.org/packages/grid/versions/3.4.1/topics/grid.draw) doesn't seem to suggest that modification is required.
The above issues apply to all of the examples provided by the authors of this package, so this is not a problem with one particular call to venn.diagram().
Thanks in advance for any advice that you might have.
temp is numeric in your example because you're just creating a file on your filesystem and getting a 1 back to let you know that it worked.
Instead of giving grid.draw the number 1, let's give it something to draw :)
temp <- venn.diagram(
x = list(
A = 1:100,
B = 1:10
),
filename =NULL,
cex = 2.5,
cat.cex = 2.5,
cat.pos = 0
);
grid.draw(temp)
I want to define a plot saving function that uses the gridsvg device from the package gridSVG.
library(ggplot2)
library(gridExtra)
mtcars$gear <- factor(mtcars$gear,levels=c(3,4,5),
labels=c("3gears","4gears","5gears"))
mtcars$am <- factor(mtcars$am,levels=c(0,1),
labels=c("Automatic","Manual"))
mtcars$cyl <- factor(mtcars$cyl,levels=c(4,6,8),
labels=c("4cyl","6cyl","8cyl"))
myPlot <- qplot(mpg, data=mtcars, geom="density", fill=gear, alpha=I(.5),
main="Distribution of Gas Milage", xlab="Miles Per Gallon",
ylab="Density")
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridSVG:::gridsvg(name = filename, width = plotWidth, height = plotHeight)
print(plot)
dev.off(which = dev.cur())
}
However if I then try to use the function it does not work. An error results:
savePlot("~/Desktop/myplot.svg", myPlot)
Show Traceback
Rerun with Debug
Error in eval(expr, envir, enclos) : object 'filename' not found
However if I do those steps from the console it works:
gridSVG::gridsvg(name = "~/Desktop/myPlot.svg", width = 15, height = 10)
myPlot
dev.off()
Is there a way I might be able to use the gridsvg function from within another function?
I wonder if I might be able to do it with eval from some environment.
Thanks,
Ben.
Here's a roundabout, but slightly more principled, way without sticking everything in the global environment (inspired by the scoping discussion in R Inferno):
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridsvg(sys.frame(1))
print(plot)
grid.export(filename)
grDevices::dev.off(which = dev.cur())
}
sys.frame(1) gives us the evaluation frame of the parent context (there's an ok explanation here for all variations on functions that access the call stack).
I separated out the call to grid.export() from the call to dev.off(), because essentially all the dev.off from gridSVG does is call grid.export, then call the grDevices::dev.off. This also lets us explicitly feed the file name to grid.export.
How curious. gridsvg seems to have an eval(fncall[[i]]) step where it walks through all the arguments and assigns them, and it must be looking in the wrong environment or something?? I am not sure if this is a problem with the gridSVG package; eval-semantics always confuse me.
Here's a workaround: if you make sure the argument -values- get passed to gridsvg (rather than the argument names) it works, though I agree this isn't particularly elegant. And you have to explicitly library(gridSVG).
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
eval(call('gridsvg', name=filename, width=plotWidth, height=plotHeight))
print(plot)
dev.off(which = dev.cur())
}
All it does is essentially call gridsvg with width=15 rather than width=plotWidth and so on.
Many R objects have S3 methods to plot associated with them. For instance, every R regression tutorial contains something like this:
dat <- data.frame(x=runif(10))
dat$y <- dat$x+runif(10)
my.lm <- lm( y~x, dat )
plot(my.lm)
Which displays regression diagnostics.
Similarly, I have an S3 object for a package which consists of a list which basically holds a few time series. I have a plot.myobject method for it which reaches into the list, yanks out the time series, and plots them on the same graph. I would like to rewrite this as a ggplot2 function so that it will be prettier and perhaps more extensible as well.
Because this package is intended to get people without much R experience up and running quickly, I'd like this to be a one-liner with one argument, as in plot(myobject), ggplot(myobject), or whatever the appropriate version might be. Then once they get hooked, they can learn more about ggplot2 and customize the graph to their heart's content.
My initial temptation was to simply replace the internals of the plot.myobject method to use ggplot2. This, however, seems like it might lose me major style points.
Is this a bad idea, and if so why and what alternative should I use?
There is an existing idiom in ggplot2 to do exactly what you propose. It is called fortify. It takes an object and produces a version of the object in a form that ggplot can work with, i.e. a data.frame. Section 9.3 in Hadley's ggplot2 book describes how to do this, using the S3 object class lm as an example. To see this in action, type fortify.lm into your console to get the following code:
function (model, data = model$model, ...)
{
infl <- influence(model, do.coef = FALSE)
data$.hat <- infl$hat
data$.sigma <- infl$sigma
data$.cooksd <- cooks.distance(model, infl)
data$.fitted <- predict(model)
data$.resid <- resid(model)
data$.stdresid <- rstandard(model, infl)
data
}
<environment: namespace:ggplot2>
Here is my own example of writing a fortify method for tree, originally published on the ggplot2 mailing list
fortify.tree <- function(model, data, ...){
require(tree)
# Uses tree:::treeco to extract data frame of plot locations
xy <- tree:::treeco(model)
n <- model$frame$n
# Lines copied from tree:::treepl
x <- xy$x
y <- xy$y
node = as.numeric(row.names(model$frame))
parent <- match((node%/%2), node)
sibling <- match(ifelse(node%%2, node - 1L, node + 1L), node)
linev <- data.frame(x=x, y=y, xend=x, yend=y[parent], n=n)
lineh <- data.frame(x=x[parent], y=y[parent], xend=x,
yend=y[parent], n=n)
rbind(linev[-1,], lineh[-1,])
}
theme_null <- opts(
panel.grid.major = theme_blank(),
panel.grid.minor = theme_blank(),
axis.text.x = theme_blank(),
axis.text.y = theme_blank(),
axis.ticks = theme_blank(),
axis.title.x = theme_blank(),
axis.title.y = theme_blank(),
legend.position = "none"
)
And the plot code. Notice that the data passed to ggplot is not a data.frame but a tree object.
library(ggplot2)
library(tree)
data(cpus, package="MASS")
cpus.ltr <- tree(log10(perf) ~ syct+mmin+mmax+cach+chmin+chmax, cpus)
p <- ggplot(data=cpus.ltr) +
geom_segment(aes(x=x,y=y,xend=xend,yend=yend,size=n),
colour="blue", alpha=0.5) +
scale_size("n", to=c(0, 3)) +
theme_null
print(p)
As per Hadley's suggestion in comments, I have submitted a generic S3 autoplot() to the ggplot2 Github repository. So if it's accepted and checks out, there should be an autoplot available for this use in the future.
Update
autoplot is now available in ggplot2.
Using plot.myobject is easy to remember and execute. However, if you're talking about myobjects that already have plot.myobject functions, you have to possibly worry about the different versions in the different namespaces. But if it's just for your own myobjects, you don't lose any style points with me. The nlme package, for one, does this extensively, though with lattice graphs instead of ggplot.
Using ggplot.myobject is an alternative; you shouldn't have to worry about other versions, unless other people start doing the same thing. However, as you note, it does break the ggplot usage paradigm.
Another alternative is to use a new name, say, gsk3plot; you never have to worry about other versions, it's not too hard to remember, and you can make alternatives to plot to your heart's content without having to worry about conflicts. This is probably what I'd choose as it makes it clear to the audience that these plots are customizable and this is a function that makes the plot the way that you prefer, and that if they are so inclined, they could dig in and do the same thing.
ggplot and ggplot2 methods generally expect the data to come to them in melt()-ed form. So your methods may need to do a melt (from package plyr) and then "map" the resulting column names to arguments in the ggplot methods.