Às vezes, preciso obter apenas a primeira linha de um conjunto de dados agrupada por um identificador, como ao recuperar idade e sexo, quando há várias observações por indivíduo. Qual é a maneira mais rápida (ou mais rápida) de fazer isso no R? Usei o agregate () abaixo e suspeito que existem maneiras melhores. Antes de postar essa pergunta, pesquisei um pouco no google, encontrei e tentei o ddply, e fiquei surpreso que era extremamente lento e me deu erros de memória no meu conjunto de dados (400.000 linhas x 16 cols, 7.000 IDs exclusivos), enquanto a versão agregada () foi razoavelmente rápido.
(dx <- data.frame(ID = factor(c(1,1,2,2,3,3)), AGE = c(30,30,40,40,35,35), FEM = factor(c(1,1,0,0,1,1))))
# ID AGE FEM
# 1 30 1
# 1 30 1
# 2 40 0
# 2 40 0
# 3 35 1
# 3 35 1
ag <- data.frame(ID=levels(dx$ID))
ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
ag
# ID AGE FEM
# 1 30 1
# 2 40 0
# 3 35 1
#same result:
library(plyr)
ddply(.data = dx, .var = c("ID"), .fun = function(x) x[1,])
ATUALIZAÇÃO: Veja a resposta de Chase e o comentário de Matt Parker sobre o que considero a abordagem mais elegante. Veja a resposta de @Matthew Dowle para a solução mais rápida que usa o data.table
pacote.
fonte
diff()
para que você possa escolher o primeiro IDdx
.Respostas:
A sua coluna de identificação é realmente um fator? Se é de fato numérico, acho que você pode usar a
diff
função a seu favor. Você também pode forçá-lo a numérico comas.numeric()
.fonte
dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)], ]
usar dados não numéricos - recebo 0,03 por caractere e 0,05 por fatores. PS: há um extra)
na sua primeirasystem.time()
função, após o segundo zero.data.table
solução abaixo deve ser a mais rápida, então eu verificaria se fosse você (provavelmente deve ser a resposta aceita aqui).Após a resposta de Steve, existe uma maneira muito mais rápida de data.table:
Se você apenas precisa da primeira linha de cada grupo, é muito mais rápido ingressar nessa linha diretamente. Por que criar o objeto .SD de cada vez, apenas para usar a primeira linha dele?
Compare o 0,064 de data.table com "A alternativa de Matt Parker à solução de Chase" (que parecia ser a mais rápida até agora):
Tão ~ 5 vezes mais rápido, mas é uma pequena mesa com menos de 1 milhão de linhas. À medida que o tamanho aumenta, o mesmo acontece com a diferença.
fonte
[.data.table
função pode ficar ... Acho que não percebi que você não criou um.SD
objeto se realmente não precisava dele. Agradável!dxt <- data.table(dx, key='ID')
na chamada para system.time (), é mais rápido que a solução de @ Matt.SD[1L]
foi totalmente otimizado e, na verdade, a resposta @SteveLianoglou seria duas vezes mais rápida para 5e7 linhas.Você não precisa de várias
merge()
etapas, apenas asaggregate()
duas variáveis de interesse:Tempos de comparação:
1) Solução de Matt:
2) Solução de remodelação2 de Zach:
3) solução data.table de Steve:
4) A solução rápida do Chase usando numérico, não fator
ID
:e 5) A alternativa de Matt Parker à solução de Chase, por caráter ou fator
ID
, que é um pouco mais rápida que a numérica de ChaseID
:fonte
dx$ID <- sample(as.numeric(dx$ID)) #assuming IDs arent presorted system.time(replicate(1000, { dy <- dx[order(dx$ID),] dy[ diff(c(0,dy$ID)) != 0, ] })) user system elapsed 0.58 0.00 0.58
ID
os resultados para que o resultado fosse comparável a outras soluções.Você pode tentar usar o pacote data.table .
Para o seu caso em particular, a vantagem é que é (insanamente) rápido. A primeira vez que fui apresentado a ele, estava trabalhando em objetos data.frame com centenas de milhares de linhas. "Normal"
aggregate
ouddply
métodos foram tomados ~ 1-2 minutos para serem concluídos (isso foi antes de Hadley introduzir oidata.frame
mojoddply
). Usandodata.table
, a operação foi literalmente realizada em questão de segundos.A desvantagem é que é tão rápido, porque ele recorre à sua tabela de dados (é como um arquivo de dados) por "colunas-chave" e usa uma estratégia de pesquisa inteligente para encontrar subconjuntos de dados. Isso resultará em uma reordenação dos seus dados antes de você coletar estatísticas sobre eles.
Dado que você deseja apenas a primeira linha de cada grupo - talvez a reordenação atrapalhe qual linha é a primeira, e é por isso que pode não ser adequada à sua situação.
De qualquer forma, você terá que julgar se
data.table
é apropriado ou não aqui, mas é assim que você o usaria com os dados que você apresentou:Atualização: Matthew Dowle (o principal desenvolvedor do pacote data.table) forneceu uma maneira melhor / mais inteligente / (extremamente) mais eficiente de usar o data.table para resolver esse problema como uma das respostas aqui ... definitivamente verifique isso .
fonte
Tente remodelar2
fonte
Você poderia tentar
Eu não tenho idéia se isso será mais rápido do que
plyr
, no entanto.fonte