rbind всех фреймов данных с общими именами на основе списка, используя lapply

У меня есть несколько фреймов данных, названных так:

orange_ABC
orange_BCD
apple_ABC
apple_BCD
grape_ABC
grape_BCD

Мне необходимо rbind те, которые имеют первую часть общего имени (оранжевый, яблочный, виноградный) и называют новые фреймы данных как таковые. Я получаю доступ к именам из списка фреймов данных names(fruitlist) (из которого я сделал вышеупомянутые кадры данных) и попытался использовать lapply с function(x) без удачи Я немного новичок в R, поэтому думаю, что я делаю простую ошибку, когда речь идет о динамическом именовании нового фрейма данных...

lapply(names(fruitlist),
       function(x){
         frame_nm <- toString((names(fruitlist[x])))
         frame_nm <- do.call(rbind, mget(ls(pattern=paste0((names(splitlist[x])),"*"))))
})

Я попробовал отдельную линию на один тип "фруктов", и это похоже на работу:

test_DF <- do.call(rbind, mget(ls(pattern="apple*")))

РЕДАКТИРОВАТЬ: я понимаю, что я забыл упомянуть, что пример списка из 6 фреймов данных был создан динамически, поэтому я не могу просто сгенерировать их список. Тем не менее, у меня есть список "фруктов", и все возможные концы новых имен фреймов данных известны ("_ABC" и "_BCD").

3 ответа

Если твой fruitlist это именованный список фреймов данных, возможно, это подойдет.

Во-первых, внесите похожие имена в их собственный список:

fruit.groups <- split(names(fruitlist), 
                      sapply(strsplit(names(fruitlist), split = "_"), "[[", 1))

> fruit.groups
$apple
[1] "apple_ABC" "apple_BCD"

$grape
[1] "grape_ABC" "grape_BCD"

$orange
[1] "orange_ABC" "orange_BCD"

Затем используйте lapply в rbind по группам:

fdf <- lapply(fruit.groups, function(x){
  out <- do.call(rbind, fruitlist[x])
  out$from <- gsub("(\\..*)", "", rownames(out))
  rownames(out) <- NULL
  return(out)
})

> fdf$apple
  a  b      from
1 1 11 apple_ABC
2 2 12 apple_ABC
3 3 13 apple_ABC
4 4 14 apple_ABC
5 1 11 apple_BCD
6 2 12 apple_BCD
7 3 13 apple_BCD
8 4 14 apple_BCD

Поддельные данные:

namelist <- paste(fruit = rep(c("orange", "apple", "grape"), 2), 
                  nums =  rep(c("_ABC", "_BCD"), each =  3), sep = "")

fruitlist <- llply(namelist, function(x){
  assign(as.character(x), data.frame(a = 1:4, b = 11:14))
})

РЕДАКТИРОВАТЬ:

Из правок к вашему вопросу выше:

Если у вас есть фрукты и суффиксы, используйте expand.grid получить все возможные комбинации (при условии, что все комбинации будут ссылаться на динамически генерируемые кадры данных).

fruits <- c("orange", "apple", "grape")
suffixes <- c("_ABC", "_BCD")
fullnames <- apply(expand.grid(fruits, suffixes), 1, paste, collapse = "")

Используя этот список имен, используйте mget сформировать список существующих фреймов данных.

new_fruit_df_list <- mget(fullnames)

Затем должен работать приведенный выше код, модифицированный здесь для отражения изменений имени:

fruit.groups <- split(names(new_fruit_df_list),
                      sapply(strsplit(names(new_fruit_df_list), split = "_"), "[[", 1))

fdf <- lapply(fruit.groups, function(x){
  out <- do.call(rbind, new_fruit_df_list[x])
  out$from <- gsub("(\\..*)", "", rownames(out))
  rownames(out) <- NULL
  return(out)
})

Посмотрите на заголовок каждого из них, добавив столбец (удалите, если он вам не нужен), показывающий имя исходного фрейма данных этой строки.

> lapply(fdf, head, 2)
$apple
  a  b      from
1 1 11 apple_ABC
2 2 12 apple_ABC

$grape
  a  b      from
1 1 11 grape_ABC
2 2 12 grape_ABC

$orange
  a  b       from
1 1 11 orange_ABC
2 2 12 orange_ABC

Как и предполагалось, предлагаемый способ присвоения значений объектам не работает. Кроме того, следует проявлять осторожность при использовании ls() а также mget() для перечисления и доступа к именованным объектам внутри функции, потому что они не поднимаются автоматически в родительские среды и только "видят" переменные в локальной области видимости, если не указано иное. Это относится к версии 3.4 R, более старые версии могут вести себя по-разному.

  1. Создание именованных объектов.

    Для создания новых объектов в глобальной среде используйте assign() (уже предложено в ответе Люка С.):

    > assign("foo", "some text")
    > foo
    [1] "some text"
    

    Размещение кода внутри функции создает локальную область видимости. Явное указание глобальной среды позволяет устанавливать глобальные переменные:

    > set_foo <- function (x) { assign("foo", x, envir=globalenv()) }
    > set_foo("other text")
    > foo
    [1] "other text"
    

    Обратите внимание, что опуская envir Аргумент оставил бы глобальную среду без изменений.

  2. Использование ls()/mget() в пределах локальной функции.

    По умолчанию в нем перечислены только имена из текущей (локальной) среды той функции, которая видит только аргумент x в примере кода, приведенного в вопросе. Как и выше, быстрое решение заключается в явном указании глобальной среды путем добавления аргумента. envir=globalenv(), То же самое относится и к mget(),

Поскольку MWE не было предоставлено, я позволю себе адаптировать пример кода "поддельные данные", представленный в ответе Люка С.

# Populate environment
namelist <- paste(fruit = rep(c("orange", "apple", "grape"), 2), 
                  nums =  rep(c("_ABC", "_BCD"), each =  3), sep = "")
for(x in namelist)
  assign(x, data.frame(a = 1:4, b = 11:14))

# The following re-generates the list of fruits used above
grouplist <- unique(unlist(lapply(strsplit(namelist, "_"), function (x) { x[[1]] })))
# Group and rbind by prefix, suppressing output
invisible(lapply(grouplist,
       function(x) {
         grouped <- do.call(rbind,
           mget(ls(pattern=paste0(x,"_*"), envir=globalenv()),
             envir=globalenv()))
         assign(x, grouped, envir=globalenv())
}))

Попробуйте это:

file_groups <- ls()[grep(".*_.*", ls())]
file_groups <- gsub("(.*)_.*", "\\1", file_groups)
df_list <- lapply(file_groups, 
                  function(x){ do.call(rbind, mget(ls(pattern = paste0(x, "*"))))})
Другие вопросы по тегам