Wie erstelle ich eine Liste von Datenrahmen?

185

Wie erstelle ich eine Liste von Datenrahmen und wie greife ich über die Liste auf jeden dieser Datenrahmen zu?

Wie kann ich diese Datenrahmen beispielsweise in eine Liste aufnehmen?

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))
Ben
quelle
13
Dies ist in ein paar Antworten, aber es lohnt sich auch hier einen sichtbaren Kommentar zu haben: =nicht <-drinnen verwenden data.frame(). Durch die Verwendung <-erstellen Sie y1und y2in Ihrer globalen Umgebung und Ihr Datenrahmen ist nicht das, was Sie wollen.
Gregor Thomas
37
Schauen Sie sich das Durcheinander von Code ohne Leerzeichen und <-s in data.frame () an. Was für ein Neuling ich war.
Ben
5
Nicht länger. Ich habe gerade Ihre Frage bearbeitet, um die Code-Formatierung zu korrigieren. Fühlen Sie sich frei, zurückzukehren, wenn Sie sich nostalgisch fühlen.
Claus Wilke

Antworten:

133

Dies hängt nicht mit Ihrer Frage zusammen, aber Sie möchten sie verwenden =und nicht <-innerhalb des Funktionsaufrufs. Wenn Sie verwenden <-, werden Sie am Ende Variablen erstellen y1und y2in welcher Umgebung auch immer Sie arbeiten:

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

Dies hat nicht den scheinbar gewünschten Effekt, Spaltennamen im Datenrahmen zu erstellen:

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

Der =Operator hingegen ordnet Ihre Vektoren Argumenten zu data.frame.

Was Ihre Frage betrifft, ist das Erstellen einer Liste von Datenrahmen einfach:

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)

Sie greifen auf die Datenrahmen genauso zu wie auf jedes andere Listenelement:

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

Die anderen Antworten zeigen Ihnen , wie Sie eine Liste von data.frames zu machen , wenn Sie bereits eine Reihe von data.frames, zum Beispiel d1, d2.... Mit Rahmen sequentiell benannt Daten ein Problem, und sie in einer Liste setzen a Gute Lösung, aber es wird empfohlen, zu vermeiden, dass eine Reihe von data.frames überhaupt nicht in einer Liste enthalten sind .

Die anderen Antworten enthalten viele Details zum Zuweisen von Datenrahmen zu Listenelementen, zum Zugriff darauf usw. Wir werden dies auch hier ein wenig behandeln, aber der Hauptpunkt ist, dass Sie nicht warten müssen, bis Sie ein paar a haben data.framesum sie einer Liste hinzuzufügen. Beginnen Sie mit der Liste.

Der Rest dieser Antwort behandelt einige häufige Fälle, in denen Sie möglicherweise versucht sind, sequentielle Variablen zu erstellen, und zeigt Ihnen, wie Sie direkt zu Listen gelangen. Wenn Sie mit Listen in R noch nicht vertraut sind, lesen Sie möglicherweise auch Was ist der Unterschied zwischen [[und [beim Zugriff auf Elemente einer Liste? .


Listen von Anfang an

Erschaffe niemals d1 d2 d3, ... überhaupt nicht dn. Erstellen Sie eine Liste dmit nElementen.

Einlesen mehrerer Dateien in eine Liste von Datenrahmen

Dies ist beim Einlesen von Dateien ziemlich einfach. Vielleicht haben Sie Dateien data1.csv, data2.csv, ...in einem Verzeichnis. Ihr Ziel ist eine Liste der aufgerufenen data.frames mydata. Als erstes benötigen Sie einen Vektor mit allen Dateinamen. Sie können dies mit Einfügen (z. B. my_files = paste0("data", 1:5, ".csv")) list.fileserstellen , aber es ist wahrscheinlich einfacher, alle entsprechenden Dateien abzurufen : my_files <- list.files(pattern = "\\.csv$"). Sie können reguläre Ausdrücke verwenden, um die Dateien abzugleichen. Weitere Informationen zu regulären Ausdrücken finden Sie in anderen Fragen, wenn Sie dort Hilfe benötigen. Auf diese Weise können Sie alle CSV-Dateien abrufen, auch wenn sie keinem netten Namensschema folgen. Oder Sie können ein schickeres Regex-Muster verwenden, wenn Sie bestimmte CSV-Dateien aus einer Reihe von Dateien auswählen müssen.

Zu diesem Zeitpunkt verwenden die meisten R-Anfänger eine forSchleife, und daran ist nichts auszusetzen. Sie funktioniert einwandfrei.

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

Ein eher R-ähnlicher Weg, dies zu tun, ist mit lapply, was eine Abkürzung für das Obige ist

my_data <- lapply(my_files, read.csv)

Ersetzen Sie natürlich read.csvgegebenenfalls andere Datenimportfunktionen . readr::read_csvoder data.table::freadwird schneller sein, oder Sie benötigen möglicherweise auch eine andere Funktion für einen anderen Dateityp.

In beiden Fällen ist es praktisch, die Listenelemente so zu benennen, dass sie mit den Dateien übereinstimmen

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

Aufteilen eines Datenrahmens in eine Liste von Datenrahmen

Das ist super einfach, die Basisfunktion split()erledigt das für Sie. Sie können durch eine Spalte (oder Spalten) der Daten oder durch alles andere, was Sie möchten, teilen

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

Dies ist auch eine gute Möglichkeit, einen Datenrahmen zur Kreuzvalidierung in Teile zu zerlegen. Vielleicht möchten Sie sich mtcarsin Trainings-, Test- und Validierungsstücke aufteilen .

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

Simulieren einer Liste von Datenrahmen

Vielleicht simulieren Sie Daten, ungefähr so:

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

Aber wer macht nur eine Simulation? Sie möchten dies 100 Mal, 1000 Mal, mehr tun! Sie möchten jedoch nicht 10.000 Datenrahmen in Ihrem Arbeitsbereich. Verwenden Sie sie replicateund fügen Sie sie in eine Liste ein:

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

Insbesondere in diesem Fall sollten Sie auch überlegen, ob Sie wirklich separate Datenrahmen benötigen oder ob ein einzelner Datenrahmen mit einer "Gruppen" -Spalte genauso gut funktioniert. Verwenden data.tableoder dplyres ist ziemlich einfach, Dinge "nach Gruppe" mit einem Datenrahmen zu tun.

Ich habe meine Daten nicht in eine Liste aufgenommen :( Ich werde es beim nächsten Mal tun, aber was kann ich jetzt tun?

Wenn es sich um ein seltsames Sortiment handelt (was ungewöhnlich ist), können Sie sie einfach zuweisen:

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

Wenn Sie in einem Muster namens Datenrahmen haben, zum Beispiel df1, df2, df3und Sie diese in einer Liste möchten, können Sie getsie , wenn Sie einen regulären Ausdruck , die Namen übereinstimmen schreiben können. Etwas wie

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.

Wird im Allgemeinen mgetverwendet, um mehrere Objekte abzurufen und in einer benannten Liste zurückzugeben. Sein Gegenstück getwird verwendet, um ein einzelnes Objekt abzurufen und zurückzugeben (nicht in einer Liste).

Kombinieren einer Liste von Datenrahmen zu einem einzelnen Datenrahmen

Eine häufige Aufgabe besteht darin, eine Liste von Datenrahmen zu einem großen Datenrahmen zu kombinieren. Wenn Sie sie übereinander stapeln möchten, würden Sie sie rbindfür ein Paar verwenden, aber für eine Liste von Datenrahmen gibt es drei gute Möglichkeiten:

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

(Ähnlich mit cbindoder dplyr::bind_colsfür Spalten.)

Um eine Liste von Datenrahmen zusammenzuführen (zu verbinden), können Sie diese Antworten sehen . Oft besteht die Idee darin, sie Reducemit merge(oder einer anderen Verbindungsfunktion) zu verwenden, um sie zusammenzubringen.

Warum die Daten in eine Liste aufnehmen?

Setzen Sie ähnliche Daten in Listen , weil Sie ähnliche Dinge zu jedem Datenrahmen tun wollen, und Funktionen wie lapply, sapply do.call, das purrrPaket und die alten plyr l*plyFunktionen machen es einfach , das zu tun. Beispiele für Leute, die leicht Dinge mit Listen tun, sind überall in SO.

Selbst wenn Sie eine Low-for-Schleife verwenden, ist es viel einfacher, die Elemente einer Liste zu durchlaufen, als Variablennamen mit zu erstellen pasteund auf die Objekte mit zuzugreifen get. Auch einfacher zu debuggen.

Denken Sie an Skalierbarkeit . Wenn Sie wirklich nur drei Variablen benötigen, es ist in Ordnung zu verwenden d1, d2, d3. Aber wenn sich herausstellt, dass Sie wirklich 6 brauchen, ist das viel mehr Tippen. Und das nächste Mal, wenn Sie 10 oder 20 brauchen, finden Sie sich selbst kopieren und Codezeilen einfügen, vielleicht finden mit / ersetzen Änderungen d14zu d15, und Sie denken , das ist nicht , wie die Programmierung sein sollte . Wenn Sie eine Liste verwenden, beträgt der Unterschied zwischen 3 Fällen, 30 Fällen und 300 Fällen höchstens eine Codezeile - keine Änderung, wenn Ihre Anzahl von Fällen automatisch erkannt wird, z. B. wie viele .csvDateien sich in Ihrer befinden Verzeichnis.

Sie können die Elemente einer Liste benennen, falls Sie für den Zugriff auf Ihre Datenrahmen etwas anderes als numerische Indizes verwenden möchten (und Sie können beide verwenden, dies ist keine XOR-Option).

Insgesamt führt die Verwendung von Listen dazu, dass Sie saubereren, besser lesbaren Code schreiben, was zu weniger Fehlern und weniger Verwirrung führt.

Gregor Thomas
quelle
2
Welches Buch, das Sie empfehlen, behandelt die Arbeit mit Listen?
Verfallen
15
Ich empfehle, Fragen und Antworten zu Stack Overflow zu lesen, die mit rund gekennzeichnet sind list.
Gregor Thomas
2
@Gregor Ich möchte hinzufügen, dass wir vermeiden können, die Listenelemente so zu benennen, dass sie mit den Dateien übereinstimmen, indem wir einfach my_data <- NULL"my_data <- list ()" zuweisen! :)
Daniel
6
Es ist möglich, my_data <- list()macht aber deutlich, dass Sie eine Liste erstellen, was gut ist! Klarer Code ist eine gute Sache. Ich sehe keinen Vorteil darin, my_data <- NULLstattdessen zu verwenden.
Gregor Thomas
3
Ich stimme dem zu, was Sie gesagt haben, aber wie ich bereits sagte, können Sie sich der Phase der Benennung der Dateien entziehen. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Aber ich respektiere deine Ratschläge, da ich als Neuling viel von ihnen lerne und ich es wirklich schätze :)
Daniel
21

Sie können auch mit [und auf bestimmte Spalten und Werte in jedem Listenelement zugreifen [[. Hier einige Beispiele. Erstens können wir nur auf die erste Spalte jedes Datenrahmens in der Liste zugreifen lapply(ldf, "[", 1), wobei 1die Spaltennummer bezeichnet wird.

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

Ebenso können wir mit auf den ersten Wert in der zweiten Spalte zugreifen

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

Dann können wir auch direkt als Vektor mit auf die Spaltenwerte zugreifen [[

lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1
Rich Scriven
quelle
13

Wenn Sie eine große Anzahl von nacheinander benannten Datenrahmen haben, können Sie eine Liste der gewünschten Teilmenge von Datenrahmen wie folgt erstellen:

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

Dabei wird my.list2eine Liste mit dem 2., 3. und 4. Datenrahmen zurückgegeben.

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

Beachten Sie jedoch, dass die Datenrahmen in der obigen Liste nicht mehr benannt sind. Wenn Sie eine Liste mit einer Teilmenge von Datenrahmen erstellen und deren Namen beibehalten möchten, können Sie Folgendes versuchen:

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

was zurückgibt:

> 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
Mark Miller
quelle
2
lapply(foo, get)Verwenden Sie stattdessen einfachmget(foo)
Gregor Thomas
9

Unter der Annahme, dass Sie eine "große" Anzahl von data.frames mit ähnlichen Namen haben (hier d #, wobei # eine positive ganze Zahl ist), ist das Folgende eine leichte Verbesserung der Methode von @ mark-miller. Es ist knapper und gibt eine benannte Liste von data.frames zurück, wobei jeder Name in der Liste der Name des entsprechenden ursprünglichen data.frame ist.

Der Schlüssel wird mgetzusammen mit verwendet ls. Wenn die in der Frage angegebenen Datenrahmen d1 und d2 die einzigen Objekte mit den Namen d # in der Umgebung waren, dann

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

was zurückkehren würde

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

Diese Methode nutzt das Musterargument in ls, mit dem wir reguläre Ausdrücke verwenden können, um die Namen von Objekten in der Umgebung genauer zu analysieren. Eine Alternative zum regulären Ausdruck "^d[0-9]+$"ist "^d\\d+$".

Wie @gregor weist darauf hin , ist es eine bessere Gesamt so Ihre Daten Bauprozess einzurichten , dass die data.frames am Anfang in benannte Listen gesetzt werden.

Daten

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))
lmo
quelle
3

Dies mag etwas spät sein, aber als ich auf Ihr Beispiel zurückkam, dachte ich, ich würde die Antwort nur ein bisschen erweitern.

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

Dann machen Sie Ihre Liste einfach:

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

Jetzt haben Sie eine Liste, aber anstatt auf die Liste auf die alte Weise zuzugreifen, wie z

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

Mit dieser Funktion können Sie den Datenrahmen Ihrer Wahl abrufen und zuweisen.

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

Holen Sie sich jetzt die, die Sie wollen.

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

Hoffe, dass extra bisschen hilft.

Prost!

ML_for_now
quelle
2
Ja, ich weiß, aber aus irgendeinem Grund ging beim Kopieren und Einfügen alles in Großbuchstaben. :( In jedem Fall funktioniert der Code in Kleinbuchstaben.
ML_for_now
4
Ich bin neugierig , warum würden Sie es vorziehen , GETDF_FROMLIST(mylist, 1)zu mylist[[1]]? Wenn Sie die Funktionssyntax bevorzugen, können Sie sogar auf die "[["(mylist, 1)Definition einer benutzerdefinierten Funktion verzichten.
Gregor Thomas
4
Sie können auch Ihre Funktionsdefinition vereinfachen, der gesamte Funktionskörper kann einfach sein return(DF_LIST[[ITEM_LOC]]), ohne dass eine Zwischenvariable zugewiesen werden muss.
Gregor Thomas
1

Sehr einfach ! Hier ist mein Vorschlag:

Wenn Sie Datenrahmen in Ihrem Arbeitsbereich auswählen möchten, versuchen Sie Folgendes:

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

oder

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

All dies führt zu demselben Ergebnis.

Sie können ändern is.data.frame, um andere Arten von Variablen wie zu überprüfenis.function

Soufiane Chami
quelle
1

Ich betrachte mich als absoluten Neuling, aber ich denke, ich habe eine äußerst einfache Antwort auf eine der ursprünglichen Unterfragen, die hier nicht angegeben wurden: Zugriff auf die Datenrahmen oder Teile davon.

Beginnen wir mit der Erstellung der Liste mit Datenrahmen wie oben angegeben:

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)

Wenn Sie dann auf einen bestimmten Wert in einem der Datenrahmen zugreifen möchten, können Sie dies tun, indem Sie die doppelten Klammern nacheinander verwenden. Der erste Satz bringt Sie in den Datenrahmen und der zweite Satz bringt Sie zu den spezifischen Koordinaten:

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

[1] 6
Loek van der Kallen
quelle