Как мне составить список фреймов данных?
Как составить список фреймов данных и как получить доступ к каждому из этих фреймов данных из списка?
Например, как я могу поместить эти фреймы данных в список?
d1 <- data.frame(y1 = c(1, 2, 3),
y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1),
y2 = c(6, 5, 4))
10 ответов
Это не связано с вашим вопросом, но вы хотите использовать =
и не <-
в вызове функции. Если вы используете <-
в итоге вы создадите переменные y1
а также y2
в какой бы среде вы ни работали:
d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
y1
# [1] 1 2 3
y2
# [1] 4 5 6
Это не будет иметь желаемого эффекта при создании имен столбцов во фрейме данных:
d1
# y1....c.1..2..3. y2....c.4..5..6.
# 1 1 4
# 2 2 5
# 3 3 6
=
оператор, с другой стороны, будет связывать ваши векторы с аргументами data.frame
,
Что касается вашего вопроса, составить список фреймов данных легко:
d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
my.list <- list(d1, d2)
Вы получаете доступ к фреймам данных так же, как и к любому другому элементу списка:
my.list[[1]]
# y1 y2
# 1 1 4
# 2 2 5
# 3 3 6
Другие ответы показывают, как составить список data.frames, когда у вас уже есть куча data.frames, например, d1
, d2
,.... Последовательное именование фреймов данных является проблемой, и размещение их в списке является хорошим решением, но лучше всего избегать того, чтобы куча фреймов данных отсутствовала в списке.
Другие ответы дают много подробностей о том, как назначить фреймы данных элементам списка, получить к ним доступ и т. Д. Мы также рассмотрим это немного здесь, но главное - сказать , не ждите, пока у вас не появится куча data.frames
добавить их в список. Начните со списка.
В оставшейся части этого ответа будут рассмотрены некоторые распространенные случаи, когда у вас может возникнуть желание создать последовательные переменные, и будет показано, как переходить прямо к спискам. Если вы новичок в списках в R, вы можете также прочитать В чем разница между [[
а также [
в доступе к элементам списка?,
Списки с самого начала
Никогда не создавай d1
d2
d3
,..., на первом месте. Создать список d
с 3 элементами.
Чтение нескольких файлов в список фреймов данных
Это делается довольно легко при чтении в файлах. Может быть, у вас есть файлы data1.csv, data2.csv, ...
в каталоге. Ваша цель - список data.frames под названием mydata
, Первое, что вам нужно, это вектор со всеми именами файлов. Вы можете построить это с помощью пасты (например, my_files = paste0("data", 1:5, ".csv")
), но это, вероятно, проще в использовании list.files
чтобы захватить все соответствующие файлы: my_files <- list.files(pattern = "\\.csv$")
, Вы можете использовать регулярные выражения для сопоставления файлов, читайте больше о регулярных выражениях в других вопросах, если вам нужна помощь там.
На данный момент большинство начинающих R будут использовать for
цикл, и в этом нет ничего плохого, он работает просто отлично.
my_data <- list()
for (i in seq_along(my_files)) {
my_data[[i]] <- read.csv(file = my_files[i])
}
Более R-подобный способ сделать это с lapply
, который является ярлыком для вышеупомянутого
my_data <- lapply(my_files, read.csv)
В любом случае, удобно именовать элементы списка в соответствии с файлами
names(my_data) <- gsub("\\.csv$", "", my_files)
# or, if you prefer the consistent syntax of stringr
names(my_data) <- stringr::str_replace(my_files, pattern = ".csv", replacement = "")
Разделение фрейма данных на список фреймов данных
Это супер-просто, базовая функция split()
делает это для вас. Вы можете разделить на столбец (или столбцы) данных, или на что угодно
mt_list = split(mtcars, f = mtcars$cyl)
# This gives a list of three data frames, one for each value of cyl
Это также хороший способ разбить фрейм данных на части для перекрестной проверки. Может быть, вы хотите разделить mtcars
в обучающие, тестовые и проверочные части.
groups = sample(c("train", "test", "validate"),
size = nrow(mtcars), replace = TRUE)
mt_split = split(mtcars, f = groups)
# and mt_split has appropriate names already!
Имитация списка фреймов данных
Может быть, вы имитируете данные, что-то вроде этого:
my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))
Но кто делает только одну симуляцию? Вы хотите сделать это 100 раз, 1000 раз, больше! Но вам не нужно 10 000 фреймов данных в вашем рабочем пространстве. использование replicate
и поместите их в список:
sim_list = replicate(n = 10,
expr = {data.frame(x = rnorm(50), y = rnorm(50))},
simplify = F)
Особенно в этом случае вам следует также подумать, действительно ли вам нужны отдельные фреймы данных, или же сработал бы один фрейм данных со столбцом "группа"? С помощью data.table
или же dplyr
довольно просто делать вещи "по группам" с фреймом данных.
Я не поместил свои данные в список:(Я сделаю это в следующий раз, но что мне теперь делать?
Если это нечетный ассортимент (что необычно), вы можете просто назначить их:
mylist <- list()
mylist[[1]] <- mtcars
mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
...
Если у вас есть фреймы данных, названные в шаблоне, например, df1
, df2
, df3
и вы хотите их в списке, вы можете get
их, если вы можете написать регулярное выражение, чтобы соответствовать именам. Что-то вроде
df_list = mget(ls(pattern = "df[0-9]"))
# this would match any object with "df" followed by a digit in its name
# you can test what objects will be got by just running the
ls(pattern = "df[0-9]")
# part and adjusting the pattern until it gets the right objects.
В общем-то, mget
используется для получения нескольких объектов и возврата их в именованный список. Его коллега get
используется, чтобы получить один объект и вернуть его (не в списке).
Объединение списка фреймов данных в один фрейм данных
Распространенной задачей является объединение списка фреймов данных в один большой фрейм данных. Если вы хотите сложить их друг на друга, вы должны использовать rbind
для пары из них, но для списка фреймов данных есть три хороших варианта:
# base option - slower but not extra dependencies
big_data = do.call(what = rbind, args = df_list)
# data table and dplyr have nice functions for this
# they will be faster and can also add id columns to identify
# which list item they came from. They can also fill in
# missing values if some data frames have more columns than others
big_data = data.table::rbindlist(df_list)
big_data = dplyr::bind_rows(df_list)
(Аналогично используя cbind
или же dplyr::bind_cols
для столбцов.)
Чтобы объединить (объединить) список фреймов данных, вы можете увидеть эти ответы. Часто идея состоит в том, чтобы использовать Reduce
с merge
(или некоторая другая присоединяющаяся функция), чтобы собрать их вместе.
Зачем помещать данные в список?
Поместите аналогичные данные в списки, потому что вы хотите сделать аналогичные вещи для каждого фрейма данных, и функции, такие как lapply
, sapply
do.call
, purrr
пакет и старый plyr
l*ply
функции позволяют легко это сделать. Примеры людей, легко делающих вещи со списками, повсюду.
Даже если вы используете простой цикл for, гораздо проще зацикливать элементы списка, чем создавать имена переменных с помощью paste
и получить доступ к объектам с get
, Проще отлаживать тоже.
Подумайте о масштабируемости. Если вам действительно нужны только три переменные, это нормально использовать d1
, d2
, d3
, Но потом, если окажется, что вам действительно нужно 6, это гораздо больше печатать. И в следующий раз, когда вам понадобится 10 или 20, вы обнаружите, что копируете и вставляете строки кода, возможно, используете поиск / замену для изменения d14
в d15
и вы думаете, что программирование не должно быть таким. Если вы используете список, разница между 3 случаями, 30 случаями и 300 случаями составляет не более одной строки кода - без изменений, если количество случаев автоматически определяется, например, по количеству .csv
файлы находятся в вашем каталоге.
Вы можете назвать элементы списка, если вы хотите использовать что-то кроме числовых индексов для доступа к вашим фреймам данных (и вы можете использовать оба, это не выбор XOR).
В целом, использование списков приведет к написанию более чистого и удобного для чтения кода, что приведет к уменьшению количества ошибок и путанице.
Вы также можете получить доступ к определенным столбцам и значениям в каждом элементе списка с помощью [
а также [[
, Вот пара примеров. Во-первых, мы можем получить доступ только к первому столбцу каждого фрейма данных в списке с помощью lapply(ldf, "[", 1)
, где 1
обозначает номер столбца.
ldf <- list(d1 = d1, d2 = d2) ## create a named list of your data frames
lapply(ldf, "[", 1)
# $d1
# y1
# 1 1
# 2 2
# 3 3
#
# $d2
# y1
# 1 3
# 2 2
# 3 1
Точно так же мы можем получить доступ к первому значению во втором столбце с помощью
lapply(ldf, "[", 1, 2)
# $d1
# [1] 4
#
# $d2
# [1] 6
Тогда мы также можем получить доступ к значениям столбца напрямую, как вектор, с [[
lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1
Если у вас есть большое количество последовательно именованных фреймов данных, вы можете создать список нужного подмножества фреймов данных, например:
d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
my.list <- list(d1, d2, d3, d4)
my.list
my.list2 <- lapply(paste('d', seq(2,4,1), sep=''), get)
my.list2
где my.list2
возвращает список, содержащий 2-й, 3-й и 4-й кадры данных.
[[1]]
y1 y2
1 3 6
2 2 5
3 1 4
[[2]]
y1 y2
1 6 3
2 5 2
3 4 1
[[3]]
y1 y2
1 9 8
2 9 8
3 9 8
Однако обратите внимание, что кадры данных в приведенном выше списке больше не называются. Если вы хотите создать список, содержащий подмножество фреймов данных и хотите сохранить их имена, вы можете попробовать это:
list.function <- function() {
d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
sapply(paste('d', seq(2,4,1), sep=''), get, environment(), simplify = FALSE)
}
my.list3 <- list.function()
my.list3
который возвращает:
> my.list3
$d2
y1 y2
1 3 6
2 2 5
3 1 4
$d3
y1 y2
1 6 3
2 5 2
3 4 1
$d4
y1 y2
1 9 8
2 9 8
3 9 8
> str(my.list3)
List of 3
$ d2:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 3 2 1
..$ y2: num [1:3] 6 5 4
$ d3:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 6 5 4
..$ y2: num [1:3] 3 2 1
$ d4:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 9 9 9
..$ y2: num [1:3] 8 8 8
> my.list3[[1]]
y1 y2
1 3 6
2 2 5
3 1 4
> my.list3$d4
y1 y2
1 9 8
2 9 8
3 9 8
Принимая как данность, у вас есть "большое" количество data.frames с похожими именами (здесь d#, где # - некоторое положительное целое число), следующее является небольшим улучшением метода @mark-miller, так как он автоматически возвращает именованный список data.frames и более кратко.
Ключ использует mget
вместе с ls
, Если фреймы данных d1 и d2, представленные в вопросе, были единственными объектами с именами d# в среде, то
my.list <- mget(ls(pattern="^d[0-9]+"))
который бы вернулся
my.list
$d1
y1 y2
1 1 4
2 2 5
3 3 6
$d2
y1 y2
1 3 6
2 2 5
3 1 4
Этот метод использует аргумент шаблона в ls
, что позволяет нам использовать регулярные выражения для более точного анализа имен объектов в среде.
Как указывает@gregor, в целом лучше настроить процесс построения данных, чтобы в начале они помещались в именованные списки.
данные
d1<-data.frame(y1 = c(1,2,3),y2 = c(4,5,6))
d2<-data.frame(y1 = c(3,2,1),y2 = c(6,5,4))
Я считаю себя полным новичком, но думаю, что у меня есть чрезвычайно простой ответ на один из исходных подвопросов, который здесь не сформулирован: доступ к фреймам данных или их частям.
Начнем с создания списка с фреймами данных, как было сказано выше:
d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
my.list <- list(d1, d2)
Затем, если вы хотите получить доступ к определенному значению в одном из фреймов данных, вы можете сделать это, последовательно используя двойные скобки. Первый набор вводит вас во фрейм данных, а второй набор дает вам определенные координаты:
my.list[[1]][[3,2]]
[1] 6
моделирование петли
Если у меня есть цикл, генерирующий фреймы данных, я начинаю с пустого
list()
и добавлять фреймы данных по мере их создания.
# Empty list
dat_list <- list()
for(i in 1:5){
# Generate dataframe
dat <- data.frame(x=rnorm(10), y=rnorm(10))
# Add to list
dat_list <- append(dat_list, list(dat))
}
Обратите внимание, что это
list(dat)
внутри нашего
append()
вызов.
Доступ к данным
Затем, чтобы получить
n
th dataframe из списка, который мы используем
dat_list[[n]]
. Вы можете получить доступ к данным в этом фрейме данных обычным способом, например
dat_list[[2]]$x
.
Или, если вам нужна одна и та же часть из всех ваших фреймов данных
sapply(dat_list, "[", "x")
.
См. Ответ @Gregor Thomas , чтобы сделать это без
for
петли.
Это может быть немного поздно, но возвращаясь к вашему примеру, я подумал, что я бы немного расширил ответ.
D1 <- data.frame(Y1=c(1,2,3), Y2=c(4,5,6))
D2 <- data.frame(Y1=c(3,2,1), Y2=c(6,5,4))
D3 <- data.frame(Y1=c(6,5,4), Y2=c(3,2,1))
D4 <- data.frame(Y1=c(9,9,9), Y2=c(8,8,8))
Тогда вы легко составите свой список:
mylist <- list(D1,D2,D3,D4)
Теперь у вас есть список, но вместо доступа к списку старый способ, такой как
mylist[[1]] # to access 'd1'
Вы можете использовать эту функцию, чтобы получить и назначить выбранный вами фрейм данных.
GETDF_FROMLIST <- function(DF_LIST, ITEM_LOC){
DF_SELECTED <- DF_LIST[[ITEM_LOC]]
return(DF_SELECTED)
}
Теперь получите тот, который вы хотите.
D1 <- GETDF_FROMLIST(mylist, 1)
D2 <- GETDF_FROMLIST(mylist, 2)
D3 <- GETDF_FROMLIST(mylist, 3)
D4 <- GETDF_FROMLIST(mylist, 4)
Надеюсь, что это поможет.
Ура!
Очень просто! Вот мое предложение:
Если вы хотите выбрать кадры данных в своем рабочем пространстве, попробуйте это:
Filter(function(x) is.data.frame(get(x)) , ls())
или же
ls()[sapply(ls(), function(x) is.data.frame(get(x)))]
все это даст тот же результат.
Ты можешь измениться is.data.frame
проверить другие типы переменных, таких как is.function
В tidyverse вы можете использовать функцию
lst()
для автоматического присвоения имен спискам на основе объектов.
library(tibble)
d1 <- data.frame(y1 = c(1, 2, 3),
y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1),
y2 = c(6, 5, 4))
lst(d1, d2)
# $d1
# y1 y2
# 1 1 4
# 2 2 5
# 3 3 6
#
# $d2
# y1 y2
# 1 3 6
# 2 2 5
# 3 1 4
Это может быть полезно при составлении списков, на которые вы позже захотите ссылаться по имени.