Як скласти список кадрів даних?


186

Як зробити список кадрів даних і як отримати доступ до кожного з цих кадрів зі списку?

Наприклад, як я можу помістити ці рамки даних у список?

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))

13
Це в парах відповідей, але варто також мати тут помітний коментар: використовуйте =не <-всередині data.frame(). Використовуючи <-ви створюєте y1і y2у вашому глобальному середовищі, і ваш кадр даних - це не те, що ви хочете.
Грегор Томас

37
Подивіться на цей безлад коду без пробілів і <-s всередині data.frame (). Яким новачком я був.
Бен

5
Більше не. Я щойно відредагував ваше запитання, щоб виправити форматування коду. Сміливо повертайтеся, якщо відчуваєте ностальгію.
Клаус Вільке

Відповіді:


133

Це не пов’язано з вашим запитанням, але ви хочете використовувати, =а не <-в межах виклику функції. Якщо ви використовуєте <-, ви в кінцевому підсумку створюєте змінні 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

344

Інші відповіді показати вам , як зробити список data.frames , коли ви вже маєте купу data.frames, наприклад, d1, d2, .... Маючи послідовно іменовані кадри даних є проблемою, і помістивши їх в список є добре виправити, але найкраща практика полягає в тому, щоб уникнути того, що в першу чергу є купа фреймів data.frames .

Інші відповіді дають безліч деталей про те, як призначити кадри даних спискам елементів, отримати доступ до них і т. Д. Ми також трохи це висвітлимо тут, але головний пункт - це сказати , не чекайте, поки у вас буде купа data.framesщоб додати їх до списку. Почніть зі списку.

У решті цієї відповіді будуть висвітлені деякі поширені випадки, коли вас може спокусити створити послідовні змінні, а також показано, як перейти безпосередньо до списків. Якщо ви новачок у списках на R, ви можете також прочитати, в чому різниця між елементами списку [[та [в доступі до них? .


Списки від початку

Ніколи не створюйте d1 d2 d3, ..., dnв першу чергу. Створіть список dз nелементами.

Читання декількох файлів у список кадрів даних

Це робиться досить легко під час читання у файлах. Можливо, у вас є файли data1.csv, data2.csv, ...в каталозі. Ваша мета - список кадрів data.frames mydata. Перше, що вам потрібно - вектор з усіма іменами файлів. Ви можете побудувати це з пастою (наприклад, my_files = paste0("data", 1:5, ".csv")), але це, ймовірно , простіше у використанні , list.filesщоб захопити всі відповідні файли: my_files <- list.files(pattern = "\\.csv$"). Ви можете використовувати регулярні вирази для відповідності файлам, детальніше про регулярні вирази читайте в інших питаннях, якщо вам там потрібна допомога. Таким чином, ви можете захопити всі файли CSV, навіть якщо вони не дотримуються приємної схеми іменування. Або ви можете скористатись вигадливішою схемою регулярних виразів, якщо вам потрібно вибрати певні файли 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)

Звичайно, замініть інші функції імпорту даних на read.csvнеобхідні. readr::read_csvабо data.table::freadбуде швидше, або вам може знадобитися інша функція для іншого типу файлів.

У будь-якому випадку зручно називати елементи списку, щоб відповідати файлам

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 that
#  - are much faster
#  - add id columns to identify the source
#  - fill in missing values if some data frames have more columns than others
# see their help pages for details
big_data = data.table::rbindlist(df_list)
big_data = dplyr::bind_rows(df_list)

(Аналогічно використовуючи cbindабо dplyr::bind_colsдля стовпців.)

Щоб об'єднати (приєднати) список кадрів даних, ви можете побачити ці відповіді . Часто, ідея полягає в тому , щоб використовувати Reduceз merge(або який - або іншою функцією з'єднання) , щоб отримати їх разом.

Навіщо ставити дані в список?

Поміщений аналогічні дані в списках , тому що ви хочете робити подібні речі для кожного кадру даних, а також функції , такі як lapply, sapply do.call, пакет , і старі функції дозволяють легко зробити це. Приклади людей, які легко роблять справи зі списками, є в УС.purrrplyr l*ply

Навіть якщо ви використовуєте низький цикл для циклу, набагато простіше перебирати елементи списку, ніж це створювати імена змінних pasteі отримувати доступ до об'єктів get. Простіше також налагоджувати.

Подумайте про масштабованість . Якщо вам дійсно потрібно тільки три змінні, це добре для використання d1, d2, d3. Але тоді, якщо виявиться, що вам справді потрібно 6, це набагато більше набору тексту. І в наступний раз, коли вам потрібно 10 або 20, ви опиняєтеся копіювання і вставки рядків коди, можливо , з допомогою пошуку / замін для зміни d14до d15, і ви думаєте , що це не так, як програмування має бути . Якщо ви використовуєте список, різниця між 3 випадками, 30 випадками та 300 випадками - це максимум один рядок коду --- взагалі не змінюється, якщо кількість випадків автоматично визначається, наприклад, скільки .csvфайлів у вашому каталог.

Ви можете назвати елементи списку, якщо ви хочете використовувати щось, крім числових індексів, для доступу до ваших кадрів даних (і ви можете використовувати обидва, це не вибір XOR).

Загалом, використання списків призведе до того, щоб ви писали чистіший, легший для читання код, що призведе до меншої кількості помилок та меншої плутанини.


2
Яку книгу ви рекомендуєте охоплювати для роботи зі списками?
Занедбане

15
Я рекомендую прочитати запитання та відповіді щодо переповнення стека, які позначені як rі, і list.
Грегор Томас

2
@Gregor Я хотів би додати, що ми можемо уникати назви елементів списку, щоб вони відповідали файлам просто шляхом призначення, my_data <- NULLа не `my_data <- list () '! :)
Даніель

6
Це можливо, але my_data <- list()дає зрозуміти, що ви створюєте список, що добре! Очистити код - це добре. Я не бачу жодної переваги використовувати my_data <- NULLнатомість.
Грегор Томас

3
Я погоджуюся з тим, що ви сказали, але, як я вже сказав, ви можете уникнути стадії іменування файлів. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Але я поважаю ваші поради, оскільки я багато чому вчусь у них як у новачків, і я дуже ціную це :)
Даніель

21

Ви також можете отримати доступ до певних стовпців та значень у кожному елементі списку за допомогою [та [[. Ось пара прикладів. По-перше, ми можемо отримати доступ лише до першого стовпця кожного кадру даних у списку 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

13

Якщо у вас є велика кількість послідовно названих фреймів даних, ви можете створити список потрібного підмножини кадрів даних на зразок цього:

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

2
Замість цього lapply(foo, get)просто скористайтесяmget(foo)
Грегор Томас

9

Враховуючи дані, ви маєте "велику" кількість фреймів data.fram з подібними іменами (тут d #, де # - деяке додатне ціле число), наступне - незначне вдосконалення методу @ mark-miller. Він більш короткий і повертає іменований список data.frames, де кожне ім’я у списку - це ім'я відповідного вихідного data.frame.

Ключ використовується 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 , який дозволяє нам використовувати регулярні вирази, щоб зробити більш точний аналіз імен об'єктів в оточенні. Альтернативою регулярного вираження "^d[0-9]+$"є "^d\\d+$".

Як вказує @gregor , що це в цілому краще , щоб налаштувати процес будівництва даних таким чином , що data.frames поміщаються в іменовані списки на старті.

дані

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))

3

Це може бути трохи пізно, але повернувшись до вашого прикладу, я подумав, що відповідь я б просто розширив.

 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)

Сподіваюся, що зайвий біт допоможе.

Ура!


2
Так, я знаю, але чомусь, коли я копіював і вставляв, все перейшло на літери. :( У будь-якому випадку код у нижньому регістрі працює.
ML_for_now

4
Мені цікаво , чому ви хотіли б , GETDF_FROMLIST(mylist, 1)щоб mylist[[1]]? Якщо ви віддаєте перевагу синтаксис функції, ви навіть можете це зробити "[["(mylist, 1)без визначення спеціальної функції.
Грегор Томас

4
Ви також можете спростити визначення функції, просто тіло функції може бути просто return(DF_LIST[[ITEM_LOC]]), не потрібно призначати проміжну змінну.
Грегор Томас

1

Дуже просто ! Ось моя пропозиція:

Якщо ви хочете вибрати фрейми даних у своїй робочій області, спробуйте:

Filter(function(x) is.data.frame(get(x)) , ls())

або

ls()[sapply(ls(), function(x) is.data.frame(get(x)))]

все це дасть однаковий результат.

Ви можете змінити, is.data.frameщоб перевірити інші типи зміннихis.function


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))

my.list <- list(d1, d2)

Потім, якщо ви хочете отримати доступ до певного значення в одному з кадрів даних, ви можете зробити це, використовуючи подвійні дужки послідовно. Перший набір потрапляє у кадр даних, а другий встановлює конкретні координати:

my.list[[1]][[3,2]]

[1] 6
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.