Eu gostaria de construir um dataframe linha por linha em R. Fiz algumas pesquisas e tudo o que encontrei foi a sugestão de criar uma lista vazia, manter um escalar de índice de lista e, a cada vez, adicionar à lista um dataframe de uma única linha e avançar o índice da lista em um. Finalmente, do.call(rbind,)
na lista.
Embora isso funcione, parece muito complicado. Não existe uma maneira mais fácil de atingir o mesmo objetivo?
Obviamente, me refiro a casos em que não posso usar alguma apply
função e preciso criar explicitamente o dataframe linha por linha. Pelo menos, há uma maneira de push
entrar no final de uma lista em vez de manter explicitamente o controle do último índice usado?
append()
[que provavelmente deve ter o nome de inserir] ouc()
adicionar itens ao final de uma lista, embora não vá ajudá-lo aqui.lapply()
,Map()
e assim por diante, mas você também pode querer dar uma olhadaaggregate()
,dapply() {heR.Misc}
ecast() {reshape}
para ver se as suas tarefas não podem ser tratadas por estes funções (todos estes retornam frames de dados).Respostas:
Você pode aumentá-los linha por linha, acrescentando ou usando
rbind()
.Isso não significa que você deva. Estruturas de crescimento dinâmico é uma das maneiras menos eficientes de codificar em R.
Se possível, aloque todo o seu data.frame antecipadamente:
e então, durante suas operações, insira uma linha de cada vez
Isso deve funcionar para data.frame arbitrário e ser muito mais eficiente. Se você ultrapassar N, você sempre poderá reduzir as linhas vazias no final.
fonte
data.table
parece ser ainda mais rápido do que a pré-alocação usando data.frames. Testando aqui: stackoverflow.com/a/11486400/636656Pode-se adicionar linhas a
NULL
:por exemplo
fonte
sapply
(ou vetorizar) e transpor.Este é um exemplo bobo de como usar
do.call(rbind,)
na saída deMap()
[que é semelhante alapply()
]Eu uso essa construção com bastante frequência.
fonte
O motivo pelo qual gosto tanto do Rcpp é que nem sempre entendo como o R Core pensa, e com o Rcpp, na maioria das vezes, não preciso.
Falando filosoficamente, você está em um estado de pecado com relação ao paradigma funcional, que tenta garantir que cada valor apareça independente de todos os outros valores; alterar um valor nunca deve causar uma alteração visível em outro valor, como acontece com os ponteiros que compartilham representação em C.
Os problemas surgem quando a programação funcional sinaliza para a pequena nave sair do caminho e a pequena nave responde "Eu sou um farol". Fazer uma longa série de pequenas alterações em um objeto grande que você deseja processar nesse meio tempo coloca você no território do farol.
No C ++ STL,
push_back()
é um modo de vida. Ele não tenta ser funcional, mas tenta acomodar os idiomas de programação comuns com eficiência .Com alguma inteligência nos bastidores, às vezes você pode organizar para ter um pé em cada mundo. Os sistemas de arquivos baseados em instantâneos são um bom exemplo (que evoluíram de conceitos como montagens de união, que também dobram os dois lados).
Se o R Core quisesse fazer isso, o armazenamento vetorial subjacente poderia funcionar como uma montagem de união. Uma referência ao armazenamento vetorial pode ser válida para subscritos
1:N
, enquanto outra referência ao mesmo armazenamento é válida para subscritos1:(N+1)
. Pode haver armazenamento reservado ainda sem referência válida por nada, mas conveniente para uma rápidapush_back()
. Você não viola o conceito funcional ao anexar fora da faixa que qualquer referência existente considera válida.Eventualmente, anexando linhas de forma incremental, você fica sem armazenamento reservado. Você precisará criar novas cópias de tudo, com o armazenamento multiplicado por algum incremento. As implementações de STL que uso tendem a multiplicar o armazenamento por 2 ao estender a alocação. Pensei ter lido no R Internals que existe uma estrutura de memória onde o armazenamento aumenta em 20%. De qualquer maneira, as operações de crescimento ocorrem com frequência logarítmica em relação ao número total de elementos anexados. Em uma base amortizada, isso geralmente é aceitável.
No que se refere aos truques nos bastidores, já vi coisas piores. Cada vez que você
push_back()
cria uma nova linha no dataframe, uma estrutura de índice de nível superior precisa ser copiada. A nova linha pode ser anexada à representação compartilhada sem afetar os valores funcionais antigos. Eu nem acho que complicaria muito o catador de lixo; uma vez que não estou propondo quepush_front()
todas as referências sejam referências de prefixo à frente do armazenamento vetorial alocado.fonte
A resposta de Dirk Eddelbuettel é a melhor; aqui, apenas observo que você pode escapar sem pré-especificar as dimensões do dataframe ou tipos de dados, o que às vezes é útil se você tiver vários tipos de dados e muitas colunas:
fonte
df<-rbind(df, row2)
?Eu descobri essa maneira de criar dataframe por raw sem matriz.
Com nome de coluna automático
Com nome de coluna
fonte
Se você tem vetores destinados a se tornarem linhas, concatene-os usando
c()
, passe-os para uma matriz linha por linha e converta essa matriz em um dataframe.Por exemplo, linhas
pode ser convertido em um quadro de dados assim:
Reconhecidamente, vejo 2 limitações principais: (1) isso só funciona com dados de modo único e (2) você deve saber suas # colunas finais para que isso funcione (ou seja, estou assumindo que você não está trabalhando com um matriz irregular cujo maior comprimento de linha é desconhecido a priori ).
Esta solução parece simples, mas pela minha experiência com conversões de tipo em R, tenho certeza de que cria novos desafios no futuro. Alguém pode comentar sobre isso?
fonte
Dependendo do formato de sua nova linha, você pode usar
tibble::add_row
se sua nova linha for simples e puder ser especificada em "pares de valores". Ou você poderia usardplyr::bind_rows
"uma implementação eficiente do padrão comum de do.call (rbind, dfs)".fonte