Я создал несколько функций, которые создают графики. Цель состоит в том, чтобы сохранить результат функции в одном списке 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
Nneka 25 Сен 2020 в 17:04

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 для списка объектов, вы можете создать свой список и назначить класс.

3
Edo 28 Сен 2020 в 12:51