Como faço para fazer uma lista de quadros de dados?


186

Como faço para fazer uma lista de quadros de dados e como faço para acessar cada um desses quadros da lista?

Por exemplo, como posso colocar esses quadros de dados em uma lista?

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
Isso está em algumas respostas, mas vale a pena ter um comentário visível aqui também: use =não por <-dentro data.frame(). Usando <-você cria y1e y2em seu ambiente global e seu quadro de dados não é o que você deseja que seja.
Gregor Thomas

37
Veja essa bagunça de código sem espaços <-es dentro de data.frame (). Que novidade eu era.
24415 Ben

5
Não mais. Acabei de editar sua pergunta para corrigir a formatação do código. Sinta-se à vontade para reverter se sentir nostalgia.
amigos estão

Respostas:


133

Isso não está relacionado à sua pergunta, mas você deseja usar =e não <-dentro da chamada de função. Se você usar <-, acabará criando variáveis y1e y2em qualquer ambiente em que estiver trabalhando:

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

Isso não terá o efeito aparentemente desejado de criar nomes de colunas no quadro de dados:

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

O =operador, por outro lado, associará seus vetores a argumentos para data.frame.

Quanto à sua pergunta, é fácil fazer uma lista de quadros de dados:

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)

Você acessa os quadros de dados da mesma maneira que acessaria qualquer outro elemento da lista:

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

344

As outras respostas mostrar-lhe como fazer uma lista de data.frames quando você já tem um monte de data.frames, por exemplo, d1, d2, .... Ter quadros de dados sequencialmente nomeado é um problema, e colocá-los em uma lista é um boa correção, mas a melhor prática é evitar ter um monte de dados. quadros que não estão em uma lista em primeiro lugar.

As outras respostas fornecem muitos detalhes de como atribuir quadros de dados a elementos de lista, acessá-los etc. Também abordaremos um pouco aqui, mas o ponto principal é dizer: não espere até que você tenha um monte dedata.frames para adicioná-los a uma lista. Comece com a lista.

O restante desta resposta abordará alguns casos comuns em que você pode ser tentado a criar variáveis ​​seqüenciais e mostra como ir direto para as listas. Se você é novo nas listas em R, também pode ler Qual é a diferença entre [[e [ao acessar elementos de uma lista? .


Listas desde o início

Nunca crie d1 d2 d3, ..., dnem primeiro lugar. Crie uma lista dcom nelementos.

Lendo vários arquivos em uma lista de quadros de dados

Isso é feito com bastante facilidade ao ler arquivos. Talvez você tenha arquivos data1.csv, data2.csv, ...em um diretório. Seu objetivo é uma lista de data.frames chamada mydata. A primeira coisa que você precisa é de um vetor com todos os nomes de arquivo. Você pode construir isso com pasta (por exemplo, my_files = paste0("data", 1:5, ".csv")), mas é provavelmente mais fácil de usar list.filespara pegar todos os arquivos apropriados: my_files <- list.files(pattern = "\\.csv$"). Você pode usar expressões regulares para corresponder aos arquivos. Leia mais sobre expressões regulares em outras perguntas, se precisar de ajuda. Dessa forma, você pode pegar todos os arquivos CSV, mesmo que eles não sigam um bom esquema de nomenclatura. Ou você pode usar um padrão regex mais sofisticado, se precisar escolher certos arquivos CSV dentre vários deles.

Neste ponto, a maioria dos iniciantes de R usará um forloop, e não há nada de errado nisso, ele funciona muito bem.

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

Uma maneira mais R-like de fazer isso é lapply, que é um atalho para o acima

my_data <- lapply(my_files, read.csv)

Obviamente, substitua por outras funções de importação de dados read.csvconforme apropriado. readr::read_csvou data.table::freadserá mais rápido, ou você também pode precisar de uma função diferente para um tipo de arquivo diferente.

De qualquer forma, é útil nomear os elementos da lista para corresponder aos arquivos

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

Dividindo um quadro de dados em uma lista de quadros de dados

Isso é super fácil, a função base split()faz isso por você. Você pode dividir por uma coluna (ou colunas) dos dados ou por qualquer outra coisa que desejar

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

Essa também é uma boa maneira de dividir um quadro de dados em partes para validação cruzada. Talvez você queira dividir mtcarsem partes de treinamento, teste e validação.

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

Simulando uma lista de quadros de dados

Talvez você esteja simulando dados, algo como isto:

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

Mas quem faz apenas uma simulação? Você quer fazer isso 100 vezes, 1000 vezes, mais! Mas você não deseja 10.000 quadros de dados em seu espaço de trabalho. Use replicatee coloque-os em uma lista:

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

Nesse caso, especialmente, você também deve considerar se realmente precisa de quadros de dados separados ou um único quadro de dados com uma coluna "grupo" funcionaria da mesma maneira? Usando data.tableou dplyré muito fácil fazer as coisas "por grupo" em um quadro de dados.

Não coloquei meus dados em uma lista :( farei da próxima vez, mas o que posso fazer agora?

Se eles são uma variedade ímpar (o que é incomum), você pode simplesmente atribuí-los:

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

Se você tem quadros de dados nomeados em um padrão, por exemplo, df1, df2, df3, e você quer que eles em uma lista, você pode getlhes se você pode escrever uma expressão regular para coincidir com os nomes. Algo como

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.

Geralmente, mgeté usado para obter vários objetos e retorná-los em uma lista nomeada. Sua contraparte geté usada para obter um único objeto e devolvê-lo (não em uma lista).

Combinando uma lista de quadros de dados em um único quadro de dados

Uma tarefa comum é combinar uma lista de quadros de dados em um grande quadro de dados. Se você deseja empilhá-las umas sobre as outras, você usaria rbindpara um par delas, mas para uma lista de quadros de dados, aqui estão três boas opções:

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

(Da mesma forma, usando cbindou dplyr::bind_colspara colunas.)

Para mesclar (ingressar) em uma lista de quadros de dados, você pode ver essas respostas . Muitas vezes, a ideia é usar Reducecommerge (ou alguma outra função de junção) para reuni-los.

Por que colocar os dados em uma lista?

Coloque dados semelhantes em listas porque você quer fazer coisas semelhantes a cada frame de dados, e funções como lapply, sapply do.call, o purrrpacote , e as antigas plyr l*plyfunções tornam mais fácil para fazer isso. Exemplos de pessoas que fazem coisas facilmente com listas estão por toda parte.

Mesmo se você usar um loop for lowly, é muito mais fácil fazer um loop sobre os elementos de uma lista do que construir nomes de variáveis pastee acessar os objetos com get. Mais fácil de depurar também.

Pense em escalabilidade . Se você realmente só precisa de três variáveis, é bom para usar d1, d2, d3. Mas se você realmente precisa de 6, é muito mais digitação. E da próxima vez, quando você precisa de 10 ou 20, você encontrar-se copiando e colando linhas de código, talvez usando find / substituir a mudança d14para d15, e você está pensando não é assim que a programação deve ser . Se você usar uma lista, a diferença entre 3 casos, 30 casos e 300 casos será no máximo uma linha de código - nenhuma alteração será alterada se o número de casos for detectado automaticamente por, por exemplo, quantos .csvarquivos existem no seu diretório.

Você pode nomear os elementos de uma lista, caso deseje usar algo diferente de índices numéricos para acessar seus quadros de dados (e você pode usar os dois, essa não é uma opção XOR).

No geral, o uso de listas o levará a escrever códigos mais limpos e fáceis de ler, o que resultará em menos bugs e menos confusão.


2
Qual livro você recomenda que cobre o trabalho com listas?
abandonado

15
Eu recomendo a leitura de perguntas e respostas no Stack Overflow que estão marcadas com ambos re list.
Gregor Thomas

2
@ Gregor Eu gostaria de acrescentar que podemos evitar o nome dos elementos da lista para corresponder aos arquivos simplesmente atribuindo o em my_data <- NULLvez de `my_data <- list () '! :)
Daniel

6
É possível, mas my_data <- list()deixa claro que você está criando uma lista, o que é bom! Código claro é uma coisa boa. Não vejo nenhuma vantagem em usar my_data <- NULL.
Gregor Thomas

3
Eu concordo com o que você disse, mas como eu disse, você pode escapar do estágio de nomear os arquivos. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Mas eu respeito seus conselhos, já que estou aprendendo muito com eles como novato e eu realmente aprecio isso :) #
Daniel Daniel

21

Você também pode acessar colunas e valores específicos em cada elemento da lista com [e [[. Aqui estão alguns exemplos. Primeiro, podemos acessar apenas a primeira coluna de cada quadro de dados na lista com lapply(ldf, "[", 1), onde 1significa o número da coluna.

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

Da mesma forma, podemos acessar o primeiro valor na segunda coluna com

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

Em seguida, também podemos acessar os valores da coluna diretamente, como um vetor, com [[

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

13

Se você tiver um grande número de quadros de dados nomeados sequencialmente, poderá criar uma lista do subconjunto desejado de quadros de dados como este:

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

onde my.list2retorna uma lista contendo os segundo, terceiro e quarto quadros de dados.

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

Observe, no entanto, que os quadros de dados na lista acima não são mais nomeados. Se você deseja criar uma lista contendo um subconjunto de quadros de dados e deseja preservar seus nomes, tente o seguinte:

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

que retorna:

> 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
Em vez de lapply(foo, get), basta usarmget(foo)
Gregor Thomas

9

Considerando que você tem um número "grande" de data.frames com nomes semelhantes (aqui d # onde # é algum número inteiro positivo), a seguir está uma pequena melhoria do método @ mark-miller's. É mais conciso e retorna uma lista nomeada de data.frames, em que cada nome na lista é o nome do data.frame original correspondente.

A chave está usando mgetjunto com ls. Se os quadros de dados d1 e d2 fornecidos na pergunta foram os únicos objetos com nomes d # no ambiente, então

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

que retornaria

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

Esse método aproveita o argumento padrão ls, que nos permite usar expressões regulares para fazer uma análise mais precisa dos nomes dos objetos no ambiente. Uma alternativa para o regex "^d[0-9]+$"é "^d\\d+$".

Como @gregor aponta , é melhor configurar o processo de construção de dados para que os data.frames sejam colocados em listas nomeadas no início.

dados

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

Pode ser um pouco tarde, mas voltando ao seu exemplo, pensei em estender a resposta um pouco.

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

Então você faz sua lista facilmente:

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

Agora você tem uma lista, mas em vez de acessar a lista da maneira antiga, como

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

você pode usar esta função para obter e atribuir o quadro de dados de sua escolha.

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

Agora pegue o que você deseja.

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

Espero que um pouco mais ajude.

Felicidades!


2
Sim, eu sei, mas, por alguma razão, quando copiei e colei, tudo deu certo. :( De qualquer forma, o código em letras minúsculas funciona.
ML_for_now

4
Estou curioso para saber por que você prefere GETDF_FROMLIST(mylist, 1)para mylist[[1]]? Se você preferir a sintaxe da função, poderá fazê-lo "[["(mylist, 1)sem definir uma função personalizada.
Gregor Thomas

4
Você também pode simplificar sua definição de função, todo o corpo da função pode ser apenas return(DF_LIST[[ITEM_LOC]]), sem necessidade de atribuir uma variável intermediária.
Gregor Thomas

1

Muito simples ! Aqui está a minha sugestão:

Se você deseja selecionar quadros de dados no seu espaço de trabalho, tente o seguinte:

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

ou

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

tudo isso dará o mesmo resultado.

Você pode alterar is.data.framepara verificar outros tipos de variáveis ​​comois.function


1

Eu me considero um novato completo, mas acho que tenho uma resposta extremamente simples para uma das subquestões originais que não foram declaradas aqui: acessar os quadros de dados ou partes dele.

Vamos começar criando a lista com quadros de dados, como foi afirmado acima:

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)

Então, se você quiser acessar um valor específico em um dos quadros de dados, poderá fazê-lo usando os colchetes duplos sequencialmente. O primeiro conjunto leva você ao quadro de dados e o segundo conjunto leva você às coordenadas específicas:

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

[1] 6
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.