Come si crea un elenco di frame di dati?


186

Come faccio a creare un elenco di frame di dati e come accedo a ciascuno di quei frame di dati dall'elenco?

Ad esempio, come posso inserire questi frame di dati in un elenco?

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
Questo è in un paio di risposte, ma vale la pena avere anche un commento visibile qui: usare =non <-dentro data.frame(). Usando <-tu crei y1e y2nel tuo ambiente globale e il tuo frame di dati non è quello che vuoi che sia.
Gregor Thomas,

37
Guarda quel pasticcio di codice senza spazi <-all'interno di data.frame (). Che principiante ero.
Ben

5
Non più. Ho appena modificato la tua domanda per correggere la formattazione del codice. Sentiti libero di tornare se ti senti nostalgico.
Claus Wilke l'

Risposte:


133

Questo non è correlato alla tua domanda, ma si desidera utilizzare =e non <-all'interno della chiamata di funzione. Se lo usi <-, finirai per creare variabili y1e y2in qualsiasi ambiente in cui lavori:

d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
y1
# [1] 1 2 3
y2
# [1] 4 5 6

Ciò non avrà l'effetto apparentemente desiderato di creare nomi di colonne nel frame di dati:

d1
#   y1....c.1..2..3. y2....c.4..5..6.
# 1                1                4
# 2                2                5
# 3                3                6

L' =operatore, d'altra parte, assocerà i tuoi vettori con argomenti a data.frame.

Per quanto riguarda la tua domanda, creare un elenco di frame di dati è semplice:

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)

Si accede ai frame di dati proprio come se si accedesse a qualsiasi altro elemento dell'elenco:

my.list[[1]]
#   y1 y2
# 1  1  4
# 2  2  5
# 3  3  6

344

Le altre risposte ti mostrano come creare un elenco di data.frames quando si dispone già di un sacco di data.frames, ad es d1. d2, ... Avere frame di dati con nome sequenziale è un problema e inserirli in un elenco è un buona soluzione, ma la migliore pratica è quella di evitare di avere un sacco di data.frames non in un elenco in primo luogo.

Le altre risposte forniscono molti dettagli su come assegnare i frame di dati per elencare gli elementi, accedervi, ecc. Ne tratteremo anche qui un po ', ma il punto principale è dire che non aspettate di avere un gruppo di data.framesper aggiungerli a un elenco. Inizia con l'elenco.

Il resto di questa risposta tratterà alcuni casi comuni in cui potresti essere tentato di creare variabili sequenziali e ti mostrerà come andare direttamente agli elenchi. Se non conosci gli elenchi in R, potresti voler leggere anche Qual è la differenza tra [[e [nell'accesso agli elementi di un elenco? .


Elenchi dall'inizio

Non creare mai d1 d2 d3 , ..., dnin primo luogo. Crea un elenco dcon nelementi.

Lettura di più file in un elenco di frame di dati

Questo viene fatto abbastanza facilmente durante la lettura di file. Forse hai i file data1.csv, data2.csv, ...in una directory. Il tuo obiettivo è un elenco di data.frames chiamati mydata. La prima cosa che ti serve è un vettore con tutti i nomi dei file. Puoi costruirlo con incolla (ad es.my_files = paste0("data", 1:5, ".csv") ), ma è probabilmente più facile da usare list.filesper afferrare tutti i file appropriati: my_files <- list.files(pattern = "\\.csv$"). Puoi usare espressioni regolari per abbinare i file, leggere di più sulle espressioni regolari in altre domande se hai bisogno di aiuto lì. In questo modo puoi prendere tutti i file CSV anche se non seguono un buon schema di denominazione. Oppure puoi usare un modello regex più elaborato se devi scegliere alcuni file CSV da un gruppo di essi.

A questo punto, la maggior parte dei principianti R utilizzerà un forciclo, e non c'è niente di sbagliato in questo, funziona bene.

my_data <- list()
for (i in seq_along(my_files)) {
    my_data[[i]] <- read.csv(file = my_files[i])
}

Un modo più simile a R per farlo è con lapply, che è una scorciatoia per quanto sopra

my_data <- lapply(my_files, read.csv)

Ovviamente, sostituire le altre funzioni di importazione dei dati read.csvcome appropriato. readr::read_csvo data.table::freadsarà più veloce o potresti anche aver bisogno di una funzione diversa per un diverso tipo di file.

In entrambi i casi, è utile nominare gli elementi dell'elenco in modo che corrispondano ai file

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

Dividere un frame di dati in un elenco di frame di dati

È semplicissimo, la funzione base split()lo fa per te. Puoi dividere per una colonna (o colonne) dei dati, o per qualsiasi altra cosa tu voglia

mt_list = split(mtcars, f = mtcars$cyl)
# This gives a list of three data frames, one for each value of cyl

Questo è anche un buon modo per suddividere un frame di dati in pezzi per la convalida incrociata. Forse vuoi dividere mtcarsin pezzi di addestramento, test e validazione.

groups = sample(c("train", "test", "validate"),
                size = nrow(mtcars), replace = TRUE)
mt_split = split(mtcars, f = groups)
# and mt_split has appropriate names already!

Simulazione di un elenco di frame di dati

Forse stai simulando dati, qualcosa del genere:

my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))

Ma chi esegue una sola simulazione? Vuoi farlo 100 volte, 1000 volte, di più! Ma non vuoi 10.000 frame di dati nell'area di lavoro. Usali replicatee mettili in un elenco:

sim_list = replicate(n = 10,
                     expr = {data.frame(x = rnorm(50), y = rnorm(50))},
                     simplify = F)

In questo caso, in particolare, dovresti anche considerare se hai davvero bisogno di frame di dati separati o un singolo frame di dati con una colonna "gruppo" funziona altrettanto bene? Usare data.tableo dplyrè abbastanza facile fare le cose "per gruppo" in un frame di dati.

Non ho inserito i miei dati in un elenco :( Lo farò la prossima volta, ma cosa posso fare ora?

Se sono un assortimento strano (il che è insolito), puoi semplicemente assegnarli:

mylist <- list()
mylist[[1]] <- mtcars
mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
...

Se si dispone di frame di dati con nome in un modello, ad esempio, df1, df2, df3, e li vuole in un elenco, si può getloro se è possibile scrivere un'espressione regolare per corrispondere ai nomi. Qualcosa di simile a

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.

Generalmente, mgetviene utilizzato per ottenere più oggetti e restituirli in un elenco denominato. La sua controparte getviene utilizzata per ottenere un singolo oggetto e restituirlo (non in un elenco).

Combinazione di un elenco di frame di dati in un singolo frame di dati

Un'attività comune è combinare un elenco di frame di dati in un frame di grandi dimensioni. Se vuoi impilarli uno sopra l'altro, li useresti rbindper un paio di essi, ma per un elenco di frame di dati qui ci sono tre buone scelte:

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

(Allo stesso modo usando cbindo dplyr::bind_colsper le colonne.)

Per unire (unire) un elenco di frame di dati, puoi vedere queste risposte . Spesso, l'idea è quella di utilizzare Reduceconmerge (o qualche altra funzione di unione) per metterli insieme.

Perché mettere i dati in un elenco?

Mettere dati simili negli elenchi perché si vuole fare cose simili a ogni frame di dati, e funzioni come lapply, sapply do.call, il purrrpacchetto , e le vecchie plyr l*plyfunzioni lo rendono facile da fare. Esempi di persone che fanno facilmente cose con elenchi sono in tutto il SO.

Anche se usi un lowly per loop, è molto più facile eseguire il loop degli elementi di un elenco piuttosto che costruire nomi di variabili con pastee accedere agli oggetti con get. Anche più facile da eseguire il debug.

Pensa alla scalabilità . Se si ha realmente bisogno solo tre variabili, va bene per l'uso d1, d2, d3. Ma poi se risulta che hai davvero bisogno di 6, è molto più digitando. E la prossima volta, quando hai bisogno di 10 o 20, ti ritrovi a copiare e incollare righe di codice, magari usando find / sostituisci per passare d14a d15, e stai pensando che non dovrebbe essere così la programmazione . Se usi un elenco, la differenza tra 3 casi, 30 casi e 300 casi è al massimo una riga di codice --- nessuna modifica se il tuo numero di casi viene rilevato automaticamente da, ad esempio, quanti .csvfile sono presenti nel tuo directory.

Puoi nominare gli elementi di un elenco, nel caso in cui desideri utilizzare qualcosa di diverso dagli indici numerici per accedere ai tuoi frame di dati (e puoi utilizzare entrambi, questa non è una scelta XOR).

Complessivamente, l'utilizzo degli elenchi ti porterà a scrivere codice più pulito e più facile da leggere, con conseguenti meno bug e meno confusione.


2
Quale libro mi consiglia di utilizzare le copertine con le liste?
Abbandonato il

15
Consiglio di leggere domande e risposte su Stack Overflow che sono etichettate con entrambi re list.
Gregor Thomas,

2
@Gregor Vorrei aggiungere che possiamo evitare di nominare gli elementi della lista in modo che corrispondano ai file semplicemente assegnando il my_data <- NULLcomando piuttosto che `my_data <- list () '! :)
Daniel

6
È possibile, ma my_data <- list()chiarisce che stai creando un elenco, il che è positivo! Il codice chiaro è una buona cosa. Non vedo alcun vantaggio nell'usare my_data <- NULLinvece.
Gregor Thomas,

3
Sono d'accordo su ciò che hai detto, ma come ho detto, facendo così puoi sfuggire alla fase di denominazione dei file. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Ma rispetto i tuoi consigli perché sto imparando molto da loro come novizio e lo apprezzo davvero :)
Daniel

21

Puoi anche accedere a colonne e valori specifici in ciascun elemento dell'elenco con [e [[. Qui ci sono un paio di esempi. Innanzitutto, possiamo accedere solo alla prima colonna di ciascun frame di dati nell'elenco lapply(ldf, "[", 1), dove 1indica il numero di colonna.

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

Allo stesso modo, possiamo accedere al primo valore nella seconda colonna con

lapply(ldf, "[", 1, 2)
# $d1
# [1] 4
# 
# $d2
# [1] 6

Quindi possiamo anche accedere direttamente ai valori della colonna, come vettore, con [[

lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1

13

Se si dispone di un numero elevato di frame di dati con nome sequenziale, è possibile creare un elenco del sottoinsieme desiderato di frame di dati come questo:

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

dove my.list2restituisce un elenco contenente il 2 °, 3 ° e 4 ° frame di dati.

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

Si noti, tuttavia, che i frame di dati nell'elenco sopra non hanno più un nome. Se si desidera creare un elenco contenente un sottoinsieme di frame di dati e si desidera conservare i loro nomi, è possibile provare questo:

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

che ritorna:

> 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
Invece di lapply(foo, get), basta usaremget(foo)
Gregor Thomas il

9

Considerando che hai un "grande" numero di data.frames con nomi simili (qui d # dove # è un numero intero positivo), il seguente è un leggero miglioramento del metodo @ mark-miller. È più conciso e restituisce un elenco denominato di data.frames, in cui ciascun nome nell'elenco è il nome del data.frame originale corrispondente.

La chiave sta usando mgetinsieme a ls. Se i frame di dati d1 e d2 forniti nella domanda fossero gli unici oggetti con nomi d # nell'ambiente, allora

my.list <- mget(ls(pattern="^d[0-9]+"))

che sarebbe tornato

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

Questo metodo sfrutta l'argomento modello in ls, che ci consente di utilizzare espressioni regolari per eseguire un'analisi più accurata dei nomi degli oggetti nell'ambiente. Un'alternativa alla regex "^d[0-9]+$"è"^d\\d+$" .

Come sottolinea @gregor , è meglio impostare il processo di costruzione dei dati in modo che i data.frames vengano inseriti negli elenchi nominati all'inizio.

dati

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

Potrebbe essere un po 'tardi, ma tornando al tuo esempio ho pensato che avrei esteso la risposta solo un po'.

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

Quindi fai facilmente la tua lista:

mylist <- list(D1,D2,D3,D4)

Ora hai un elenco ma invece di accedere all'elenco alla vecchia maniera come

mylist[[1]] # to access 'd1'

è possibile utilizzare questa funzione per ottenere e assegnare il frame di dati desiderato.

GETDF_FROMLIST <- function(DF_LIST, ITEM_LOC){
   DF_SELECTED <- DF_LIST[[ITEM_LOC]]
   return(DF_SELECTED)
}

Ora prendi quello che vuoi.

D1 <- GETDF_FROMLIST(mylist, 1)
D2 <- GETDF_FROMLIST(mylist, 2)
D3 <- GETDF_FROMLIST(mylist, 3)
D4 <- GETDF_FROMLIST(mylist, 4)

Spero che quel qualcosa in più aiuti.

Saluti!


2
Sì, lo so, ma per qualche motivo, quando ho copiato e incollato, tutto è andato a posto. :( In ogni caso il codice in minuscolo funziona.
ML_for_now

4
Sono curioso di sapere perché preferiresti GETDF_FROMLIST(mylist, 1)farlo mylist[[1]]? Se preferisci la sintassi della funzione, potresti persino fare a "[["(mylist, 1)meno di definire una funzione personalizzata.
Gregor Thomas,

4
Potresti anche semplificare la definizione della tua funzione, l'intero corpo della funzione potrebbe essere return(DF_LIST[[ITEM_LOC]]), senza bisogno di assegnare una variabile intermedia.
Gregor Thomas,

1

Molto semplice ! Ecco il mio suggerimento:

Se si desidera selezionare i frame di dati nell'area di lavoro, provare questo:

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

o

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

tutti questi daranno lo stesso risultato.

Puoi cambiare is.data.frameper controllare altri tipi di variabili comeis.function


1

Mi considero un principiante completo, ma penso di avere una risposta estremamente semplice a una delle domande secondarie originali che non è stata dichiarata qui: accedere ai frame di dati o parti di esso.

Cominciamo creando l'elenco con i frame di dati come indicato sopra:

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)

Quindi, se si desidera accedere a un valore specifico in uno dei frame di dati, è possibile farlo utilizzando le doppie parentesi in sequenza. Il primo set ti porta nel frame di dati e il secondo set ti porta alle coordinate specifiche:

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

[1] 6
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.