web-dev-qa-db-fra.com

Comment créer une liste de trames de données?

Comment créer une liste de trames de données et comment accéder à chacune de ces trames de données à partir de la liste?

Par exemple, comment puis-je mettre ces trames de données dans une liste?

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

Cela n’est pas lié à votre question, mais vous souhaitez utiliser = et non pas <- dans l’appel de la fonction. Si vous utilisez <-, vous créez des variables y1 et y2 dans l’environnement dans lequel vous travaillez:

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

Cela n'aura pas l'effet apparemment souhaité de créer des noms de colonne dans le bloc de données:

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

L'opérateur =, par contre, associera vos vecteurs à des arguments avec data.frame.

En ce qui concerne votre question, il est facile de dresser une liste de trames de données:

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)

Vous accédez aux trames de données comme vous le feriez pour tout autre élément de la liste:

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

Les autres réponses vous montrent comment créer une liste de données.frames lorsque vous avez déjà une série de données.frames, par exemple , d1, d2, .... Avoir des trames de données nommées de manière séquentielle est un problème, et les mettre dans une liste est une bonne solution, mais il est recommandé de évitez d’avoir un tas de data.frames ne figurant pas dans une liste en premier lieu.

Les autres réponses donnent beaucoup de détails sur la manière d'assigner des blocs de données à la liste des éléments, d'y accéder, etc. Nous allons en parler un peu ici aussi, mais le point principal est de say n'attendez pas d'avoir un tas de data.frames pour les ajouter à une liste. Commencez par la liste.

Le reste de cette réponse couvrira des cas courants dans lesquels vous pourriez être tenté de créer des variables séquentielles et vous montrera comment accéder directement à des listes. Si vous êtes nouveau dans les listes en R, vous voudrez peut-être aussi lire Quelle est la différence entre [[ et [ dans l'accès aux éléments d'une liste? .


Listes du début

Ne créez jamais d1d2d3, ..., dn en premier lieu. Créez une liste d avec les éléments n.

Lecture de plusieurs fichiers dans une liste de trames de données

Cela se fait assez facilement lors de la lecture de fichiers. Vous avez peut-être des fichiers data1.csv, data2.csv, ... dans un répertoire. Votre objectif est une liste de data.frames appelée mydata. La première chose dont vous avez besoin est un vecteur avec tous les noms de fichiers. Vous pouvez construire ceci avec coller (par exemple, my_files = paste0("data", 1:5, ".csv")), mais il est probablement plus facile d'utiliser list.files pour récupérer tous les fichiers appropriés: my_files <- list.files(pattern = "\\.csv$"). Vous pouvez utiliser des expressions régulières pour faire correspondre les fichiers et en savoir plus sur les expressions régulières dans d'autres questions si vous avez besoin d'aide. De cette façon, vous pouvez récupérer tous les fichiers CSV même s’ils ne suivent pas un schéma de nommage de Nice. Vous pouvez également utiliser un modèle de regex plus sophistiqué si vous devez sélectionner certains fichiers CSV parmi d’autres.

À ce stade, la plupart des débutants R utiliseront une boucle for, et il n’ya rien de mal à cela, cela fonctionne parfaitement.

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

Une façon plus R-like de le faire est avec lapply, qui est un raccourci pour ce qui précède

my_data <- lapply(my_files, read.csv)

Bien entendu, remplacez read.csv par une autre fonction d'importation de données, selon le cas. readr::read_csv ou data.table::fread sera plus rapide ou vous aurez peut-être besoin d'une fonction différente pour un type de fichier différent.

Quoi qu'il en soit, il est pratique de nommer les éléments de la liste pour qu'ils correspondent aux fichiers.

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

Fractionnement d'un bloc de données en une liste de blocs de données

C’est très facile, la fonction de base split() le fait pour vous. Vous pouvez diviser par une colonne (ou des colonnes) des données, ou par tout ce que vous voulez

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

C'est également un bon moyen de décomposer un bloc de données en plusieurs éléments pour une validation croisée. Peut-être souhaitez-vous scinder mtcars en éléments de formation, de test et de validation.

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

Simuler une liste de trames de données

Peut-être que vous simulez des données, quelque chose comme ceci:

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

Mais qui fait une seule simulation? Vous voulez faire cela 100 fois, 1000 fois, plus! Mais vous ne voulez pas vouloir 10 000 trames de données dans votre espace de travail. Utilisez replicate et mettez-les dans une liste:

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

Dans ce cas en particulier, vous devez également déterminer si vous avez vraiment besoin de trames de données distinctes ou si un seul cadre de données avec une colonne "groupe" fonctionne tout aussi bien. En utilisant data.table ou dplyr, il est assez facile de faire des choses "par groupe" dans un bloc de données.

Je n'ai pas mis mes données dans une liste :( je le ferai la prochaine fois, mais que puis-je faire maintenant?

S'il s'agit d'un assortiment étrange (ce qui est inhabituel), vous pouvez simplement les assigner:

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

Si vous avez des trames de données nommées dans un modèle, par exemple, df1, df2, df3 et que vous les voulez dans une liste, vous pouvez get si vous pouvez écrire une expression régulière pour faire correspondre les noms. Quelque chose comme

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.

Généralement, mget est utilisé pour obtenir plusieurs objets et les renvoyer dans une liste nommée. Son homologue get est utilisé pour obtenir un seul objet et le renvoyer (pas dans une liste).

Combinaison d'une liste de trames de données en un seul.

Une tâche courante consiste à combiner une liste de trames de données en un seul grand. Si vous voulez les empiler les uns sur les autres, vous utiliseriez rbind pour une paire d'entre eux, mais pour une liste de trames de données, voici trois bons choix:

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

(De même, en utilisant cbind ou dplyr::bind_cols pour les colonnes.)

Pour fusionner (rejoindre) une liste de trames de données, vous pouvez voir ces réponses . Souvent, l’idée est d’utiliser Reduce avec merge (ou une autre fonction de jonction) pour les réunir.

Pourquoi mettre les données dans une liste?

Placez des données similaires dans des listes car vous souhaitez appliquer des actions similaires à chaque trame de données et fonctionner comme lapply, sapplydo.call, le package purrr =, et les anciennes fonctions plyrl*ply facilitent cette tâche. Des exemples de personnes faisant facilement des choses avec des listes sont partout SO.

Même si vous utilisez une faible boucle for, il est beaucoup plus facile de parcourir les éléments d'une liste que de construire des noms de variables avec paste et d'accéder aux objets avec get. Plus facile à déboguer aussi.

Pensez à l'évolutivité . Si vous n'avez vraiment besoin que de trois variables, vous pouvez utiliser d1, d2, d3. Mais s'il s'avère que vous avez vraiment besoin de 6, cela signifie beaucoup plus de dactylographie. Et la prochaine fois, quand vous aurez besoin de 10 ou 20, vous vous retrouverez à copier-coller des lignes de code, en utilisant peut-être find/replace pour changer d14 en d15 et vous penserez ce n'est pas comme cela que la programmation devrait être . Si vous utilisez une liste, la différence entre 3 cas, 30 cas et 300 cas correspond à au plus une ligne de code - aucun changement du tout si votre nombre de cas est automatiquement détecté par, par exemple, .csv les fichiers sont dans votre répertoire.

Vous pouvez nommer les éléments d'une liste si vous souhaitez utiliser autre chose que des index numériques pour accéder à vos trames de données (et vous pouvez utiliser les deux, ce n'est pas un choix XOR).

Globalement, l'utilisation de listes vous amènera à écrire un code plus propre et plus facile à lire, ce qui réduira le nombre de bogues et la confusion.

312
Gregor

Vous pouvez également accéder à des colonnes et à des valeurs spécifiques dans chaque élément de la liste avec [ et [[. Voici quelques exemples. Premièrement, nous ne pouvons accéder qu'à la première colonne de chaque trame de données de la liste avec lapply(ldf, "[", 1), où 1 signifie le numéro de colonne.

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

De même, nous pouvons accéder à la première valeur de la deuxième colonne avec

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

Ensuite, nous pouvons également accéder directement aux valeurs de colonne, en tant que vecteur, avec [[

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

Si vous avez un grand nombre de trames de données nommées de manière séquentielle, vous pouvez créer une liste du sous-ensemble souhaité de trames de données, comme suit:

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 renvoie une liste contenant les deuxième, troisième et quatrième trames de données.

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

Notez cependant que les trames de données de la liste ci-dessus ne sont plus nommées. Si vous souhaitez créer une liste contenant un sous-ensemble de trames de données et conserver leurs noms, vous pouvez essayer ceci:

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

qui retourne:

> 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
13
Mark Miller

En prenant pour acquis, vous avez un "grand" nombre de données.frames avec des noms similaires (ici d # où # est un entier positif), voici une légère amélioration de la méthode de @ mark-miller. Il est plus concis et renvoie une liste de data.frames nommée , où chaque nom de la liste est le nom du nom de données data.frame correspondant.

La clé utilise mget avec ls. Si les trames de données d1 et d2 fournies dans la question étaient les seuls objets portant le nom d # dans l'environnement, alors

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

qui reviendrait

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

Cette méthode tire parti de l’argument pattern de ls, ce qui nous permet d’utiliser des expressions régulières pour analyser plus précisément les noms d’objets de l’environnement. Une alternative à l'expression régulière "^d[0-9]+$" est "^d\\d+$".

Comme @gregor souligne , il est généralement préférable de configurer votre processus de construction de données de manière à ce que les données.frames soient placées dans des listes nommées au début.

data

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

C'est peut-être un peu tard, mais pour revenir à votre exemple, j'ai pensé étendre la réponse un peu.

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

Ensuite, vous faites facilement votre liste:

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

Maintenant, vous avez une liste mais au lieu d’y accéder à l’ancienne, telle que

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

vous pouvez utiliser cette fonction pour obtenir et affecter le cadre de données de votre choix.

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

Maintenant, prenez celui que vous voulez.

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

J'espère que ça aide.

À votre santé!

3
ML_for_now

Très simple ! Voici ma suggestion:

Si vous voulez sélectionner des images dans votre espace de travail, essayez ceci:

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

ou

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

tout cela donnera le même résultat.

Vous pouvez changer is.data.frame pour vérifier d'autres types de variables comme is.function

1
Cs20