Я создал несколько функций, которые создают графики. Цель состоит в том, чтобы сохранить результат функции в одном списке analysisObjects
. analysisObjects
- это список списков. Приведенный ниже код дает результат точно так, как ожидалось.
createPlot1 <- function(data = mtcars){
plot1 <- ggplot(data, aes(factor(cyl))) +
geom_boxplot() + coord_flip()
plot2 <- ggplot(data, aes(factor(cyl))) +
geom_boxplot()
res = list("mycars" = list(list("objType" = "plot",
"object" = plot1),
list("objType" = "plot",
"object" = plot2)))
analysisObjects <<- append(analysisObjects, res)
}
createPlot2 <- function(data = iris){
plot1 <- ggplot(data, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(color=Species, shape=Species))
plot2 <- ggplot(data, aes(x=Species, y=Sepal.Length)) +
geom_boxplot(aes(fill=Species))
res <- list("myflowers" = list(list("objType" = "plot",
"object" = plot1),
list("objType" = "plot",
"object" = plot2)))
analysisObjects <<- append(analysisObjects, res)
}
Проблема в том, что analysisObject должен быть создан заранее, и он перезаписывается каждый раз, когда я запускаю функцию. Я пытался избежать этого, написав отдельную функцию, которая добавляет результат функции в analysisObjects
, если он существует. Если analysisObjects
не существует, создается список:
appendResults <- function(object = res){
if(exists("analysisObjects")){
analysisObjects <<- append(analysisObjects, object)
}else{
analysisObjects <<- list()
analysisObjects <<- append(analysisObjects, object)
}
}
Эта функция заменяет строку analysisObjects <<- append(analysisObjects, res)
, но процесс все еще кажется довольно уродливым. Есть ли лучший способ добавить результаты функции в список? В идеале такой, где порядок выполнения функций не имеет значения.
1 ответ
Я считаю, что вы подходите к своей проблеме с неверными предпосылками.
Почему бы вам не попробовать что-то вроде этого:
library(ggplot2)
# generic for methods dispatch
createPlot <- function(data){
UseMethod("createPlot")
}
# method for class "someclass"
createPlot.someclass <- function(data){
plot1 <- ggplot(data, aes(factor(cyl))) +
geom_boxplot() + coord_flip()
plot2 <- ggplot(data, aes(factor(cyl))) +
geom_boxplot()
list(list("objType" = "plot", "object" = plot1),
list("objType" = "plot", "object" = plot2))
}
# method for class "someotherclass"
createPlot.someotherclass <- function(data){
plot1 <- ggplot(data, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(color=Species, shape=Species))
plot2 <- ggplot(data, aes(x=Species, y=Sepal.Length)) +
geom_boxplot(aes(fill=Species))
list(list("objType" = "plot", "object" = plot1),
list("objType" = "plot", "object" = plot2))
}
# prepare your data and assign the right class. It will be needed for dispatch
class(mtcars) <- c("someclass" , class(mtcars))
class(iris) <- c("someotherclass", class(iris))
# make a list of your dataframes and give it a name to each one (if you want to)
mylist <- list(mycars = mtcars, myflowers = iris)
# create your list
analysisObjects <- lapply(mylist, createPlot)
Идея в том, что вы создаете analysisObjects
один раз и создаете его самым лучшим и чистым способом.
Вы хотите иметь список в конце, поэтому я полагаю, вы хотите перебрать несколько фреймов данных, чтобы получить окончательный результат.
Что вы можете сделать, так это подготовить все свои фреймы данных в начале, а затем в конце грамотно построить их все.
Например, вы можете использовать классы и методы.
createPlot
- это универсальный вариант, который управляет вашими классами.
Я только что придумал несколько названий для ваших классов, но вы можете придумать более красивые названия.
В конце вы перебираете свои данные. Дайте имя каждому фрейму данных: это будет имя каждого элемента списка.
ИЗМЕНИТЬ, чтобы ответить на ваши вопросы
Если вы дублируете фрейм данных для назначения разных классов, вы не получите намного больше места в вашей памяти.
Взгляните на этот пример. Мы используем функцию lobstr::obj_size
, чтобы увидеть фактический размер оперативной памяти, используемой R.
i2 <- i1 <- iris
lobstr::obj_size(i1)
#> 7,200 B
lobstr::obj_size(i2)
#> 7,200 B
lobstr::obj_size(list(i1,i2))
#> 7,264 B
class(i1) <- c("someclass", class(i1))
class(i2) <- c("someotherclass", class(i2))
lobstr::obj_size(i1)
#> 7,272 B
lobstr::obj_size(i2)
#> 7,272 B
lobstr::obj_size(list(i1,i2))
#> 7,728 B
Как видите, R оптимизирован так, что даже если вы «создаете» другой объект, он фактически вызывает то же пространство в оперативной памяти и не копирует все без причины.
Если вы редактируете столбец i2
, он будет реплицировать только этот столбец.
Так что, если вам нужно назначить разные классы и «реплицировать» ваш набор данных, это не проблема.
Однако, если для одного конкретного набора данных вам нужно вызвать два разных метода, вы можете сделать это следующим образом:
createPlot.someotherclassyet <- function(data){
out1 <- createPlot.someclass(data)
out2 <- createPlot.someotherclass(data)
c(out1, out2)
}
class(iris) <- c("someotherclassyet", class(iris))
createPlot(iris)
Это выполняет свою работу, но это не совсем чисто.
Было бы лучше иметь функцию, которая преобразует класс в другой, на всякий случай в будущем вам нужно будет создавать проверки или дополнительные преобразования, чтобы сделать класс как таковой.
### someclass
as_someclass <- function(x){
UseMethod("as_someclass")
}
as_someclass.someotherclassyet <- function(x){
class(x) <- setdiff(class(x), "someotherclassyet")
as_someclass(x)
}
as_someclass.data.frame <- function(x){
class(x) <- c("someclass", class(x))
x
}
### someotherclass
as_someotherclass <- function(x){
UseMethod("as_someotherclass")
}
as_someotherclass.someotherclassyet <- function(x){
class(x) <- setdiff(class(x), "someotherclassyet")
as_someotherclass(x)
}
as_someotherclass.data.frame <- function(x){
class(x) <- c("someotherclass", class(x))
x
}
### someotherclassyet
as_someotherclassyet <- function(x){
UseMethod("as_someotherclassyet")
}
as_someotherclassyet.data.frame <- function(x){
class(x) <- c("someotherclassyet", class(x))
x
}
createPlot.someotherclassyet <- function(data){
out1 <- createPlot(as_someclass(data))
out2 <- createPlot(as_someotherclass(data))
c(out1, out2)
}
lapply(list(myflowers = as_someclass(iris),
myotherflowers = as_someotherclass(iris),
allmyflowers = as_someotherclassyet(iris),
mycars = as_someotherclass(mtcars)),
createPlot)
Если вы хотите выполнить createPlot
для списка объектов, вы можете создать свой список и назначить класс.
Похожие вопросы
Новые вопросы
r
R — это бесплатный язык программирования с открытым исходным кодом и программная среда для статистических вычислений, биоинформатики, визуализации и общих вычислений. Пожалуйста, используйте минимально воспроизводимые примеры, которые другие могут запустить с помощью копирования и вставки. Показать желаемый результат полностью. Используйте dput() для данных и укажите все небазовые пакеты с помощью library(). Не вставляйте изображения для данных или кода, вместо этого используйте блоки кода с отступом. Для вопросов по статистике используйте https://stats.stackexchange.com.