добавить несколько кадров данных вместе в списке - PullRequest
0 голосов
/ 26 сентября 2018

Я чувствую, что у этого должно быть действительно простое / элегантное решение, но я просто не могу его найти.(Я относительно новичок в r, так что это не удивительно.)

У меня есть (большой) вложенный список, содержащий data.frames, которые я пытаюсь добавить вместе.Вот код для создания примера данных:

#Create data frames nested in a list
for (i in 1:6) {
  for (j in 1:4) {
    assign(paste0("v", j), sample.int(100,4))
  }
  assign(paste0("df", i), list(cbind(v1, v2, v3, v4)))
}

inner1 <- list(data1 = df1, data2 = df2)
inner2 <- list(data1 = df3, data2 = df4)
inner3 <- list(data1 = df5, data2 = df6)

outer <- list(group1 = inner1, group2 = inner2, group3 = inner3)

Мне нужно сложить вместе все кадры данных, помеченные data1, и все data2 вместе.Если бы они не были в этом формате вложенного списка, я бы сделал это:

data1.tot <- df1 + df3 + df5
data2.tot <- df2 + df4 + df6

Поскольку они находятся в списке, я подумал, что может быть решение lapply, и попытался:

grp <- c("group1", "group2", "group3") #vector of groups to sum across
datas <- lapply(outer, "[[", "data1") #select "data1" from all groups
tot.datas <- lapply(datas[grp], "+") #to sum across selected data
#I know these last two steps can be combined into one but it helps me keep everything straight to separate them

Но возвращается Error in FUN(left): invalid argument to unary operator, потому что я передаю список данных как x.

Я также смотрел на другие решения, подобные этому: Добавление выбранных фреймов данных вместе из списка фреймов данных

Но вложенная структура моих данных делаетя не уверен, как перевести это решение в мою проблему.

И просто хочу отметить, что данные, с которыми я работаю, являются данными GCHN Daily, поэтому структура не является моей конструкцией.Любая помощь будет принята с благодарностью.

ОБНОВЛЕНИЕ: Я частично нашел исправление, используя предложение Reduce @Parfait, но теперь мне нужно его автоматизировать.Я работаю над решением, использующим цикл for, потому что это дает мне больше контроля над элементами, к которым я обращаюсь, но я открыт для других идей.Вот ручное решение, которое работает:

get.df <- function(x, y, z) {
# function to pull out the desired data.frame from the list
# x included as argument to make function applicable to my real data
  output <- x[[y]][[z]]
  output[[1]]
}

output1 <- get.df(x = outer, y = "group1", z = "data1")
output2 <- get.df(x = outer, y = "group2", z = "data1")
data1 <- list(output1, output2)
data1.tot <- Reduce(`+`, data1)

Используя мои образцы данных, я хотел бы зациклить это по 2 типам данных («data1» и «data2») и 3 группам («group1»,"group2", "group3").Я работаю над решением for loop, но пытаюсь сохранить output1 и output2 в списке.Мой цикл выглядит следующим образом:

dat <- c("data1", "data2")
grp <- c("group1", "group2", "group3")

for(i in 1:length(dat)) {
  for(j in 1:length(grp)) {
    assign(paste0("out", j), get.df(x = outer, y = grp[j], z = dat[i]))
  }
list(??? #clearly this is where I'm stuck!
}

Есть предложения по проблеме цикла for или для лучшего метода?

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Рассмотрим Reduce, которые работают от списков.Эта функция более высокого порядка представляет собой компактный способ выполнения вложенных вызовов: ((df1 + df2) + df3) + ....

data1.tot <- Reduce(`+`, lapply(outer, "[[", "data1"))

data2.tot <- Reduce(`+`, lapply(outer, "[[", "data2"))

Для демонстрации со случайными данными

Данные

set.seed(9262018)

dfList <- setNames(replicate(6, data.frame(NUM1=runif(50),
                                           NUM2=runif(50),
                                           NUM3=runif(50)), simplify = FALSE),
                   paste0("df", 1:6))

list2env(dfList, .GlobalEnv)

inner1 <- list(data1 = df1, data2 = df2)
inner2 <- list(data1 = df3, data2 = df4)
inner3 <- list(data1 = df5, data2 = df6)

outer <- list(group1 = inner1, group2 = inner2, group3 = inner3)

Выход

data1.tot <- Reduce(`+`, lapply(outer, "[[", "data1"))
head(data1.tot, 10)
#         NUM1      NUM2      NUM3
# 1  2.0533870 1.3821609 1.0702992
# 2  2.6046584 1.7260646 1.9699774
# 3  2.2510810 1.6690353 1.4495476
# 4  1.7636879 1.2357098 1.9483906
# 5  1.0189969 2.1191041 1.7466040
# 6  1.3933982 0.7541027 1.0971724
# 7  1.8058803 2.4608417 0.7291335
# 8  1.0763517 1.2494739 1.0480818
# 9  0.7069873 1.5496575 1.2264486
# 10 0.9522526 2.1407523 1.2597422

data2.tot <- Reduce(`+`, lapply(outer, "[[", "data2"))
head(data2.tot, 10)    
#         NUM1      NUM2      NUM3
# 1  1.7568578 0.9322930 1.5579897
# 2  0.9455063 0.9211592 1.7067779
# 3  1.2698614 0.4623059 0.9426310
# 4  1.6791964 1.4304953 1.2435480
# 5  0.8088625 2.6107952 1.2308862
# 6  1.8202400 2.3511104 1.5676112
# 7  0.9765578 0.8870206 0.6725699
# 8  2.6448770 1.8931751 1.8188512
# 9  1.6114870 1.8632245 0.7452924
# 10 0.9710550 1.8367305 2.0994788

Проверка на равенство

all.equal(data1.tot, df1 + df3 + df5)
# [1] TRUE
all.equal(data2.tot, df2 + df4 + df6)
# [1] TRUE

identical(data1.tot, df1 + df3 + df5)
# [1] TRUE
identical(data2.tot, df2 + df4 + df6)
# [1] TRUE
0 голосов
/ 26 сентября 2018

Это то, что вы хотите?

sapply(
  X = names(outer[[1]]),
  FUN = function(d) {
    Reduce(x = unlist(lapply(outer, "[[", d), recursive = F), f = "+")
  },
  simplify = F,
  USE.NAMES = T
)
0 голосов
/ 26 сентября 2018

Вот решение, которое отлично работает, если каждый внутренний список содержит только несколько фреймов данных:

sum_df1 <- sum(unlist(lapply(outer, "[[", 1)))
sum_df2 <- sum(unlist(lapply(outer, "[[", 2)))

Если каждый внутренний список содержит, например, 1000 фреймов данных, используйте:

dfs <- seq(1 : 1000)
lapply(dfs, function(x) sum(unlist(lapply(outer, "[[", x))))

Это даст вам список, где каждый элемент является суммой внутренних фреймов данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...