ggpubr can't find 'mean_se' unless ggpubr is attached via library() - r

Summary of problem: When I try to add summary stats to a ggpubr plot via the "add" parameter, ggpubr can't find the summary stat functions (example code below). For instance, if I am trying to add error bars with add="mean_se" I get an error message and no error bars.
Unsatisfactory solution: Attaching ggpubr by calling library(ggpubr) would fix this problem. See this answer.
Why the above solution is unsatisfactory: I am developing a package, and so would like to avoid attaching other packages via calls to library() - my understanding is that this is best practice, to avoid polluting the namespace with things the user might not have anticipated would get loaded.
MY QUESTION: Is there some way to get ggpubr to find mean_se without attaching the package?
Example code (in an .R file in my package):
make.plot = function(){
utils::data("iris")
ggpubr::ggbarplot(
data = iris,
x = "Species",
y = "Sepal.Length",
add = "mean_se")
}
Example output:
> devtools::load_all(".")
# i Loading MyPackage
> make.plot()
# Warning message:
# Computation failed in `stat_summary()`:
# object 'mean_se_' of mode 'function' was not found
One thing that should work, but doesn't, is passing "ggpubr::mean_se_" as the add argument. This avoids the error message, but produces an incorrect plot. The plot should look like this:
But passing "ggpubr::mean_se_" instead produces:
Additional weirdness: If I ever add a call to load ggpubr, build MyPackage with devtools::load_all("."), and run it, then the above code never fails until I quit and reload RStudio, even if I delete the call library(ggpubr) from my package and build it again.

Since you're creating a package you can ensure that mean_se_ is in the search path by importing it in your function.
If you use roxygen you can add the tag #importFrom ggpubr mean_se_:
#' #importFrom ggpubr mean_se_
make.plot = function(){
utils::data("iris")
ggpubr::ggbarplot(
data = iris,
x = "Species",
y = "Sepal.Length",
add = "mean_se")
}
Then you run devtools::document() and run your function, and your output should look like this:

If you look at the way the add parameter is handled inside ggpubr, it is actually matched as a string to one of the summary functions. It seems that the summary functions need to be on the search path when ggbarplot is called.
The easiest way round this is to copy the function over to your own package namespace:
mean_se_ <- ggpubr::mean_se_
make.plot = function(){
utils::data("iris")
ggpubr::ggbarplot(
data = iris,
x = "Species",
y = "Sepal.Length",
add = "mean_se_")
}
make.plot()
Created on 2022-05-04 by the reprex package (v2.0.1)

Related

R:"Note: no visible binding for global variable" after devtools:use_data

I want to make a dataframe available to users of a package, as well as to functions of the package.
Starting from a new package I've used
devtools::use_data_raw("my_data")
This creates the file data_raw/my_data.R, which I edit as
my_data <- data.frame(x = runif(3), y = runif(3))
devtools::use_data(my_data, overwrite = TRUE)
After running the code above, a file data/my_data.Rda is created.
According to Hadley Wickham's R Packages every file under data/ is exported, and if I try
load_all()
my_data
I can see that this is the case. However if I now try to use the my_data dataframe inside a function in the package, say like R/test_my_data.R
test_my_data <- function {
my_data
}
and then I run
devtools::check()
I get the infamous no visible binding for global variable my_data NOTE.
I appreciate there are many questions already on this topic but many are related to the cases where a tidy evaluation function is used, or data from another package is referred to. Why is R CMD check failing on the example above, and what's the correct way of sorting this out?
I'm aware that the
utils::globalVariables("my_data")
solution will avoid this NOTE, but I'd like to know if there's a proper way of informing R CMD check that my_data actually exists.

Function imported from dependency not found, requires library(dependency)

I am trying to create an R package that uses functions from another package (gamlss.tr).
The function I need from the dependency is gamlss.dist::TF (gamlss.dist is loaded alongside gamlss.tr), but it is referenced in my code as simply TF within a call to gamlss.tr::gen.trun.
When I load gamlss.tr manually with library(), this works. However, when I rely on the functions of the dependency automatically being imported by my package through #import, I get an "object not found" error as soon as TF is accessed.
My attempt to be more explicit and reference the function I need as gamlss.dist::TF resulted in a different error ("unexpected '::'").
Any tips on how to use this function in my package would be much appreciated!
The code below reproduces the problem if incorporated into a clean R package (as done in this .zip), built and loaded with document("/path/to/package"):
#' #import gamlss gamlss.tr gamlss.dist
NULL
#' Use GAMLSS
#'
#' Generate a truncated distribution and use it.
#' #export
use_gamlss <- function() {
print("gen.trun():")
gamlss.tr::gen.trun(par=0,family=TF)
#Error in inherits(object, "gamlss.family") : object 'TF' not found
#gamlss.tr::gen.trun(par=0,family=gamlss.dist::TF)
#Error in parse(text = fname) : <text>:1:1: unexpected '::'
y = rTFtr(1000,mu=10,sigma=5, nu=5)
print("trun():")
truncated_dist = gamlss.tr::trun(par=0,family=TF, local=TRUE)
model = gamlss(y~1, family=truncated_dist)
print(model)
}
use_gamlss() will only start working once a user calls library(gamlss.tr).
This is due to bad design of gamlss.tr in particular the trun.x functions (they take character vectors instead of family objects / they evaluate everything in the function environment instead of the calling environment).
To work around this, you have to make sure that gamlss.distr is in the search path of the execution environment of gamlss.tr functions (This is why ## import-ing it in your package does not help: it would need to be #' #import-ed in gamlss.tr).
This can be achieved by adding it to Depends: of your package.
If you want to avoid that attaching your package also attaches gamlss.distr, you could also add the following at the top of use_gamlss:
nsname <-"gamlss.dist"
attname <- paste0("package:", nsname)
if (!(attname %in% search())) {
attachNamespace(nsname)
on.exit(detach(attname, character.only = TRUE))
}
This would temporarily attach gamlss.dist if it is not attached already.
You can read more on namespaces in R in Hadley Wickham's "Advanced R"

Creating a custom Stat object in ggplot2

I'd like to create a custom Stat object for ggplot2. (Specifically I'd like to create a smoother that works differently than the ones stat_smooth allows- for instance, without a y~x modeling function- but there are other custom Stats I'd like to create even if there were a workaround for my specific case).
I found this suggested solution from Hadley Wickham:
StatExpo <- proto(Stat, {
objname <- "expo"
desc <- "Exponential smoothing"
default_geom <- function(.) GeomLine
calculate_groups <- function(., data, scales, variable="x", ...) {
data$y <- HoltWinters(data$x, ...)
}
})
stat_expo <- StatExpo$new
However, when I try it I get:
Error in proto(Stat, { : object 'Stat' not found
Upon looking around the ggplot code, I found where Stat is defined. However, the Stat object is, as far as I can tell, never exported from ggplot2.
I could write my new stat object within the ggplot2/R folder and then reinstall the package, but obviously this would be cumbersome and make the solution very difficult to share with others. How can I create a custom Stat object outside of the ggplot namespace?
ggplot2:::Stat can be used to access the non-exported object.
getFromNamespace('Stat','ggplot2')

Points3D Function in R

I have the scatterplot3d package installed in R. When I load it with library(scatterplot3d) or require(scatterplot3d) I am able to create a 3d scatter plot. However, when I try to use the points3d function I get the following error:
Error: could not find function "points3d"
I tried reinstalling the package to no avail (both inside R and as a tarball using R CMD INSTALL in the command line). I am running Xubuntu 12.10 kernel 3.8.7-030807-generic and R version 2.15.3 (2013-03-01).
Entering locate points3d in the command line gave me no results.
I also tried the par.mar default settings command as explained in the manual.
scatterplot3d does an interesting object-oriented twist on the usual R practice. The object returned from the function call includes the points3d function as built-in part of the object but it is not in the Global environment. It is intended that you add to the existing plot-object using that "attached" function that is not a free-living organism but rather a domesticated animal that only exists in the object corral, so you would use this as your syntax:
object$point3d(x,y,z)
I do "feel your pain" but can show you how to overcome the frustration, since I created a working example yesterday: Using scatterplot3d to plot a sphere
You need to intall the package plot3D in the usual way via
install.packages("plot3D")
Then you just need to import, generate the dataset and use the function points3D()
library(plot3D)
x = rnorm(100)
y = rnorm(100)
z = x + y + rnorm(100,0,1)
points3D(x, y, z, col = rainbow(1000))
This is the plot generated by the code above

R namespace access and match.fun

I'm working on an R package where one of the functions contains a match.fun call to a function in a package that's imported to the package namespace. But on loading the package, the match.fun call can't find the function name. From Hadley Wickham's description I think I'm doing everything right, but this is clearly not the case.
Example:
# in the package file header, for creation of the NAMESPACE via roxygen2:
##` #import topicmodels
# The function declaration in the package
ModelTopics <- function(doc.term.mat, num.topics, topic.method="LDA"){
topic.fun <- match.fun(topic.method)
output <- topic.fun(doc.term.mat, k=num.topics)
return(output)
}
And then in R:
> library(mypackage)
> sample.output <- ModelTopics(my.dtm, topic.method="LDA", num.topics=5)
Error in get(as.character(FUN), mode = "function", envir = envir) :
object 'LDA' of mode 'function' was not found
From my understanding of namespaces, the match.fun call should have access to the package namespace, which should include the topicmodels functions. But that doesn't appear to be the case here. If I import topicmodels directly to the global namespace for the R session, then this works.
Any help is much appreciated. This is R64 2.14.1 running on OSX.
UPDATE:
The package is here
Re the DESCRIPTION file, perhaps that's the problem: roxygen2 doesn't update the DESCRIPTION file with Imports: statements. But none of the other packages are listed there, either; only the match.fun calls appear to be affected.
Re the NAMESPACE extract, here's the import section:
import(catspec)
import(foreach)
import(gdata)
import(Hmisc)
import(igraph)
import(lsa)
import(Matrix)
import(plyr)
import(RecordLinkage)
import(reshape)
import(RWeka)
import(stringr)
import(tm)
import(topicmodels)
I believe this an issue of scope. Although you have imported topicmodels and thus LDA, you haven't exported these functions, so they aren't available in the search path.
From ?match.fun:
match.fun is not intended to be used at the top level since it will
perform matching in the parent of the caller.
Thus, since you are using ModelTopics in the global environment, and LDA isn't available in the global environment, the match.fun call fails.
It seems to me you have two options:
Option 1: use get
An alternative would be to use get where you can specify the environment. Consider this: try to use match.fun to find print.ggplot in the package ggplot2:
match.fun("print.ggplot")
Error in get(as.character(FUN), mode = "function", envir = envir) :
object 'print.ggplot' of mode 'function' was not found
Since print.ggplot isn't exported, match.fun can't find it.
However, get does find it:
get("print.ggplot", envir=environment(ggplot))
function (x, newpage = is.null(vp), vp = NULL, ...)
{
set_last_plot(x)
if (newpage)
grid.newpage()
data <- ggplot_build(x)
gtable <- ggplot_gtable(data)
if (is.null(vp)) {
grid.draw(gtable)
}
else {
if (is.character(vp))
seekViewport(vp)
else pushViewport(vp)
grid.draw(gtable)
upViewport()
}
invisible(data)
}
<environment: namespace:ggplot2>
Option 2: Export the functions from topicmodels that you need
If you make the necessary functions from topicmodels available in your NAMESPACE, your code should also work.
Do this by either:
Selective exporting LDA and other functions to the namespace using #export
Declare a dependency, using Depends: topicmodels - this is the same as calling library(topicmodels) in the global environment. This will work, but is possibly a bit of a brute force approach. It also means that any subsequent library call can mask your LDA function, thus leading to unexpected results.
Answering my own question: the DESCRIPTION file wasn't updating the Imports: and Depends: fields after re-roxygenizing the updated code. Hence the match.fun issues. Out of curiousity, why does this affect match.fun but not a range of other function calls made elsewhere to imported package functions?

Resources