I have been trying to create functions that return a list of ggplot and am having various problems. Fundamentally however, I do not understand why this is TRUE
"data.frame" == class(c(qplot(1:10,rnorm(10)))[[1]])
when this is [TRUE,TRUE]
c('gg','ggplot') == class(qplot(1:10,rnorm(10)))
I havent seen any questions similar to this. I see various questions that are solved by things like
lapply(someList, function(x) {
#make ggplot, then use print(...) or whatever
})
So I am guessing there is something about passing ggplot objects out of functions or between environments or something. Thanks for any clues about the ggplot or R that I am missing.
library(ggplot2)
p = c(qplot(1:10,rnorm(10)))
p2 = qplot(1:10,rnorm(10))
p[[1]] (same as p[['data']])) is supposed to be a dataframe. It usually holds the data for the plot.
p is a list because you used the c function.
p2 is a ggplot because that's what qplot returns.
Take a look at the attributes of each object.
attributes(p)
# $names
# [1] "data" "layers" "scales" "mapping" "theme" "coordinates"
# [7] "facet" "plot_env" "labels"
attributes(p2)
# $names
# [1] "data" "layers" "scales" "mapping" "theme" "coordinates"
# [7] "facet" "plot_env" "labels"
#
# $class
# [1] "gg" "ggplot"
To store many ggplot objects, use list.
ggplot.objects = list(p2,p2,p2)
The c help file
shows that ggplot is not a possible output.
It also states that
c is sometimes used for its side effect of removing attributes
except names, for example to turn an array into a vector
If you wanted
c to return ggplot objects, then you could try defining your own c.ggplot
function. You'll have to read a good deal about S3 and S4 functions to understand what's
going on.
If you have several ggplot objects in the global environment, you can make a list of them with :
ggplot_list <- Filter(function(x) is(x, "ggplot"), mget(ls()))
Related
so we know that R have list() variable, and also know that R has function call names() to give names for variable. For example :
a=30
names(a)="number"
a
# number
# 30
But now, I want to give a list variable a name, like this :
b=list()
names(b)="number"
and it returns error message like this :
Error in names(b) = "number" :
'names' attribute [1] must be the same length as the vector [0]
What I have suppose to do? I do this because I need many list variables. Or, do you have another way so I can make many list variables without playing with its name?
Since #akrun doesn't need any more points, here is an example showing how you can assign names to a list:
lst <- list(a="one", b="two", c=c(1:3))
names(lst)
[1] "a" "b" "c"
names(lst) <- c("x", "y", "z")
> lst
$x
[1] "one"
$y
[1] "two"
$z
[1] 1 2 3
It seems as though you are interested in labeling the object itself rather than the elements in it. The key is that the names attribute for a list object is necessarily assigned to its elements. One option, since you said you have many list objects, is to store the lists in a big list and then you can assign names to the big list, and the elements within the list-objects can be named too.
allLists <- list('number' = list())
> allLists
$number
list()
Another option, you can make use of the label feature in the Hmisc package. It modifies most common objects in R to have a subclass "labelled" and so whenever you print the list it shows the label. It is good for documentation and organizing the workspace a bit better, but caveat it's very easy to accidentally cast labelled objects to a non-labelled class and to confuse methods that don't think to search for more than one class.
library(Hmisc)
p <- list()
label(p) <- 'number'
> p
number
list()
Another option is to make the "name" of your list object an actual element of the list. You'll see in a lot of complex R data structures, this is the preferred way of storing labels, titles, or names when such a need arises and isn't met by the base R data structure.
b <- list('name' = 'number')
The last possibility is that you need a placeholder to store the "names" attribute of the elements you haven't yet populated the list with. If the elements are of known length and of known type, you can allocate such a vector using e.g. numeric(1) a sort-of "primed" vector which can be named. If you don't know the data structure of your output, I would not use this approach since it can be a real memory hog to "build" data structures in R.
Other possibilities are
as.list(a)
# $`number`
# [1] 30
# or
setNames(list(unname(a)),'number')
# $`number`
# [1] 30
# or named list with named vector
setNames(list(a), 'number')
# $`number`
# number
# 30
so we know that R have list() variable, and also know that R has function call names() to give names for variable. For example :
a=30
names(a)="number"
a
# number
# 30
But now, I want to give a list variable a name, like this :
b=list()
names(b)="number"
and it returns error message like this :
Error in names(b) = "number" :
'names' attribute [1] must be the same length as the vector [0]
What I have suppose to do? I do this because I need many list variables. Or, do you have another way so I can make many list variables without playing with its name?
Since #akrun doesn't need any more points, here is an example showing how you can assign names to a list:
lst <- list(a="one", b="two", c=c(1:3))
names(lst)
[1] "a" "b" "c"
names(lst) <- c("x", "y", "z")
> lst
$x
[1] "one"
$y
[1] "two"
$z
[1] 1 2 3
It seems as though you are interested in labeling the object itself rather than the elements in it. The key is that the names attribute for a list object is necessarily assigned to its elements. One option, since you said you have many list objects, is to store the lists in a big list and then you can assign names to the big list, and the elements within the list-objects can be named too.
allLists <- list('number' = list())
> allLists
$number
list()
Another option, you can make use of the label feature in the Hmisc package. It modifies most common objects in R to have a subclass "labelled" and so whenever you print the list it shows the label. It is good for documentation and organizing the workspace a bit better, but caveat it's very easy to accidentally cast labelled objects to a non-labelled class and to confuse methods that don't think to search for more than one class.
library(Hmisc)
p <- list()
label(p) <- 'number'
> p
number
list()
Another option is to make the "name" of your list object an actual element of the list. You'll see in a lot of complex R data structures, this is the preferred way of storing labels, titles, or names when such a need arises and isn't met by the base R data structure.
b <- list('name' = 'number')
The last possibility is that you need a placeholder to store the "names" attribute of the elements you haven't yet populated the list with. If the elements are of known length and of known type, you can allocate such a vector using e.g. numeric(1) a sort-of "primed" vector which can be named. If you don't know the data structure of your output, I would not use this approach since it can be a real memory hog to "build" data structures in R.
Other possibilities are
as.list(a)
# $`number`
# [1] 30
# or
setNames(list(unname(a)),'number')
# $`number`
# [1] 30
# or named list with named vector
setNames(list(a), 'number')
# $`number`
# number
# 30
I'm currently dealing with some objects that are a list of attributes that represents a statistical model. For example, let's say I have a matrix, a numeric vector and an integer.
myobj = list(amatrix = matrix(1:9,3,3),avector = c(1:3),aninteger = 1)
class(myobj) = 'myclass'
Suppose that, for some reason, I can create a plot that represents an object of this class. How can I make plot(myobj) recognizes that the object has the class 'myclass', and print it in the desired way, for example image(myobj$amatrix)?
I think the question is essentially how to 'modify' R's plot function so it knows how to handle a newly defined object class? Can I use functions of other packages like ggplot when executing this modification?
In a more general sense, how does functions that handle different classes of objects know how to act for each class?
I have little to none experience with classes in R, so even some simple guides about classes should be helpful.
As mentionned by #emilliman you can define your own method:
myobj = list(amatrix = matrix(1:9,3,3),avector = c(1:3),aninteger = 1)
class(myobj) <- 'myclass'
plot.myclass <- function(x) image(x$amatrix)
methods(plot) # check the 4th element of 3rd line :) (list will differ depending on what packages are loaded)
# [1] plot.acf* plot.data.frame* plot.decomposed.ts* plot.default plot.dendrogram* plot.density* plot.ecdf
# [8] plot.factor* plot.formula* plot.function plot.hclust* plot.histogram* plot.HoltWinters* plot.isoreg*
# [15] plot.lm* plot.medpolish* plot.mlm* plot.myclass plot.ppr* plot.prcomp* plot.princomp*
# [22] plot.profile.nls* plot.R6* plot.raster* plot.spec* plot.stepfun plot.stl* plot.table*
# [29] plot.ts plot.tskernel* plot.TukeyHSD*
#and plot :
plot(myobj)
I need to use ggbio::tracks function to have aligned Xaxis merged ggplots.
As ggplot scripts are complex they are saved as separate files which are then sourced to plot.
Here is one complex ggplot example script file, call it test.R, which will then be sourced:
# test.R
ggplot(cars,aes(speed,dist)) + geom_point(col="red")
Now, the problem:
library(ggbio)
library(ggplot2)
# complex ggplot script sourced text.R
x1 <- source("test.R")
# another complex ggplot script
x2 <- ggplot(cars,aes(speed,dist)) + geom_point(col="green")
# check classes
class(x1)
# [1] "list"
class(x2)
# [1] "gg" "ggplot"
# this works
print(x1)
# this doesn't work within tracks function
tracks(
print(x1),
x2,
heights=c(10,1)
)
Error: Objects of type list not supported by autoplot. Please use qplot() or ggplot() instead.
# below works - Note: x1$value
tracks(
x1$value,
x2,
heights=c(10,1)
)
I am surely missing something very simple, I tried to play with source() options, but couldn't find a way to avoid using $value or print(). Essentially, I want to be able to run below code and get above merged plot:
# ideal code
tracks(
x1,
x2,
heights=c(10,1)
)
Ad-hoc solution: modify your test.R by wrapping it into dummy function like so
# test.R
test_ggplot <- function() {
ggplot(cars,aes(speed,dist)) + geom_point(col="red")
}
and then
source("test.R")
x1 <- test_ggplot()
which obviously results in
class(x1)
#[1] "gg" "ggplot"
Honestly, I've never seen usage of xx <- source() so I doubt it is advised to do so. There is even no Value section in ?source...
Edit: source calls withVisible, which describes the return value exactly as a list:
This function evaluates an expression, returning it in a two element
list containing its value and a flag showing whether it would
automatically print.
In R, I have a list of nontrivial objects (they aren't simple objects like scalars that R can be expected to be able to define an order for). I want to sort the list. Most languages allow the programmer to provide a function or similar that compares a pair of list elements that is passed to a sort function. How can I sort my list?
To make this is as simple I can, say your objects are lists with two elements, a name and a value. The value is a numeric; that's what we want to sort by. You can imagine having more elements and needing to do something more complex to sort.
The sort help page tells us that sort uses xtfrm; xtfrm in turn tells us it will use == and > methods for the class of x[i].
First I'll define an object that I want to sort:
xx <- lapply(c(3,5,7,2,4), function(i) list(name=LETTERS[i], value=i))
class(xx) <- "myobj"
Now, since xtfrm works on the x[i]'s, I need to define a [ function that returns the desired elements but still with the right class
`[.myobj` <- function(x, i) {
class(x) <- "list"
structure(x[i], class="myobj")
}
Now we need == and > functions for the myobj class; this potentially could be smarter by vectorizing these properly; but for the sort function, we know that we're only going to be passing in myobj's of length 1, so I'll just use the first element to define the relations.
`>.myobj` <- function(e1, e2) {
e1[[1]]$value > e2[[1]]$value
}
`==.myobj` <- function(e1, e2) {
e1[[1]]$value == e2[[1]]$value
}
Now sort just works.
sort(xx)
It might be considered more proper to write a full Ops function for your object; however, to just sort, this seems to be all you need. See p.89-90 in Venables/Ripley for more details about doing this using the S3 style. Also, if you can easily write an xtfrm function for your objects, that would be simpler and most likely faster.
The order function will allow you to determine the sort order for character or numeric aruments and break ties with subsequent arguments. You need to be more specific about what you want. Produce an example of a "non-trivial object" and specify the order you desire in some R object. Lists are probably the most non-vectorial objects:
> slist <- list(cc=list(rr=1), bb=list(ee=2, yy=7), zz="ww")
> slist[order(names(slist))] # alpha order on names()
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7
$cc
$cc$rr
[1] 1
$zz
[1] "ww"
slist[c("zz", "bb", "cc")] # an arbitrary ordering
$zz
[1] "ww"
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7
$cc
$cc$rr
[1] 1
One option is to create a xtfrm method for your objects. Functions like order take multiple columns which works in some cases. There are also some specialized functions for specific cases like mixedsort in the gtools package.