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, более старые версии могут вести себя по-разному.
Создание именованных объектов.
Для создания новых объектов в глобальной среде используйте
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
Аргумент оставил бы глобальную среду без изменений.Использование
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, "*"))))})