Как мне составить список фреймов данных?

Как составить список фреймов данных и как получить доступ к каждому из этих фреймов данных из списка?

Например, как я могу поместить эти фреймы данных в список?

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, вы можете также прочитать В чем разница между [[ а также [ в доступе к элементам списка?,


Списки с самого начала

Никогда не создавай d1d2d3,..., на первом месте. Создать список 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, sapplydo.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() вызов.

Доступ к данным

Затем, чтобы получить nth 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

Это может быть полезно при составлении списков, на которые вы позже захотите ссылаться по имени.

Другие вопросы по тегам