[R-br] Processar uma base de dados grande - ENEM 2012 - 6 milhões de registros - só 40 segundos

Elias T. Krainski eliaskrainski em yahoo.com.br
Domingo Outubro 26 09:23:48 BRST 2014


Walmes,

Muito legal a dica de usar o shell para isso. O shell e' sempre mais 
rapido que qualquer coisa!

Sobre ler dados do ENEM, usando fread() do pacote 'data.table' demorou 
210 segundos no meu laptop para dados 2012 do ENEM. Eu nao gostei muito 
do INEP ter disponibilizado o formato .csv. Anteriormente, dados de 2010 
por exemplo, usava-se o formato fixed width. Usando uma funcao (descrita 
em http://www.leg.ufpr.br/doku.php/disciplinas:ce223101:historico2010) 
demorou 238 segundos para ler os dados de 2010 (4.6 milhoes de linhas) e 
selecionar prova de matematica dos alunos do Parana.

Essa funcao que usei para ler (e SELECIONAR linhas, colunas) fixed width 
usa a mesma ideia que voce fez usando read.table(). A diferenca e' que 
eu abro uma coneccao apenas e uso readLines(). O que voce fez com 
read.table() e' ineficiente porque ao ler cada bloco e' feita uma nova 
coneccao com o arquivo (via funcao scan()) e ao ler o i-e'simo bloco a 
funcao scan() precisa percorrer todos os dados dos blocos anteriores 
antes de comecar a ler. Assim, para ler os ultimos blocos demora-se um 
tempo enorme percorrendo...

Abs,
Elias.

On 25/10/14 00:22, walmes . wrote:
> Saudações,
>
> Essa mensagem é apenas para compartilhar uma experiência que tive e 
> considero que o problema aqui discutido não seja tão particular.
>
> Fui procurado por alguém que precisava processar um arquivo com 
> resultados da prova do ENEM 2012 para aplicar teoria de resposta ao 
> item. Precisava-se processar o arquivo original para chegar em um que 
> tivesse apenas 5 das 80 colunas (notas para as competências sobre a 
> redação) e somente às linhas que tivessem o conteúdo "P" (de presente) 
> para a coluna "IN_STATUS_REDACAO". O arquivo é um csv, delimitador 
> vírgula, aspas nas strings, 5791066 de linhas, 3.8 GB. Fiz o que me 
> veio na cabeça de primeira: "dividir para conquistar". Lia porções de 
> 30 mil linhas do arquivo (usando na read.table() o skip= e nrow=), 
> separava as 5 colunas, mantinha só as linhas com registro P e escrevia 
> para um arquivo com write.table(..., append=TRUE). Isso dentro de um 
> for(). Na minha implementação, um tanto ingenua e corrida porque fiz 
> enquanto era assistido a programar, quanto apliquei ao arquivo alvo 
> levou 3h40. Esperava que fosse demorar mas não tanto. Minha máquina 
> tem 16 GB de RAM. Sabendo que o bash do linux é muito eficiente para 
> tarefas como operações em arquivos texto, eu fui buscar na internet 
> como fazer o mesmo. Resultado é que tudo se resolveu com 37 
> segundos!!! É por isso que achei interessante compartilhar com a 
> lista, inclusive para saber se alguem tem meios alternativos para 
> solucionar o problema. Seguem os comandos que apliquei no terminal do 
> Linux. Os dados fornecidos no exemplo são apenas as 10 mil primeiras 
> linhas do arquivo original.
>
> ##-----------------------------------------------------------------------------
> ## 0. Aquisição dos dados. Apenas 10000 registros dos 5791066 do arquivo
> ## original.
>
> wget 
> http://www.leg.ufpr.br/~walmes/data/DADOS_ENEM_2012_10millinhas.csv 
> <http://www.leg.ufpr.br/%7Ewalmes/data/DADOS_ENEM_2012_10millinhas.csv> -O 
> DADOS_ENEM_2012.csv
> ls
> wc -l DADOS_ENEM_2012.csv
>
> ##-----------------------------------------------------------------------------
> ## 1. Filtrar só às colunas de interesse. Da 74 à 79. Na 74 tem-se os
> ## valores P, F e outros.
>
> cut -d , -f 74-79 DADOS_ENEM_2012.csv > file.csv
> head -10 file.csv
> wc -l file.csv
>
> ##-----------------------------------------------------------------------------
> ## 2. Manter só as linhas com ocorrência do P.
>
> grep "P" file.csv > fileP.csv
> wc -l fileP.csv
> head -10 fileP.csv
>
> ##-----------------------------------------------------------------------------
> ## 3. Remover à colunas com P, ou seja, manter da 2 à 6.
>
> cut -d , -f 2-6 fileP.csv > file.csv
> head -10 file.csv
> wc -l file.csv
>
> ##-----------------------------------------------------------------------------
> ## 4. Remover as aspas para salvar espaço em disco.
>
> sed 's/\"//g' file.csv > fileP.csv
> head -10 fileP.csv
>
> ##-----------------------------------------------------------------------------
> ## 5. Como contar o número de linhas repetidas. Gerar tabela de 
> frequência dos
> ## padrões de resposta. Economiza espaço e é a informação mínima
> ## necessária para ajustar TRI. Remover o cabeçalho.
>
> sed 1d fileP.csv > file.csv
> sort file.csv | uniq --count > fileP.csv
> head -10 fileP.csv
> tail -10 fileP.csv
>
> ##-----------------------------------------------------------------------------
> ## 6. Eliminar espaços no ínicio e colocar uma vírgula entre a
> ## frequência e o primeiro registro para que todos os campos sejam
> ## separados por `,`.
>
> sed -e 's/^ *//;s/ /,/' fileP.csv > fileFreq.csv
> head -10 fileFreq.csv
> wc -l fileFreq.csv
>
> ##-----------------------------------------------------------------------------
>
> À disposição.
> Walmes.
>
> ==========================================================================
> Walmes Marques Zeviani
> LEG (Laboratório de Estatística e Geoinformação, 25.450418 S, 49.231759 W)
> Departamento de Estatística - Universidade Federal do Paraná
> fone: (+55) 41 3361 3573
> skype: walmeszeviani
> homepage: http://www.leg.ufpr.br/~walmes 
> <http://www.leg.ufpr.br/%7Ewalmes>
> linux user number: 531218
> ==========================================================================
>
>
> _______________________________________________
> R-br mailing list
> R-br em listas.c3sl.ufpr.br
> https://listas.inf.ufpr.br/cgi-bin/mailman/listinfo/r-br
> Leia o guia de postagem (http://www.leg.ufpr.br/r-br-guia) e forneça código mínimo reproduzível.

-------------- Próxima Parte ----------
Um anexo em HTML foi limpo...
URL: <http://listas.inf.ufpr.br/pipermail/r-br/attachments/20141026/11e9636d/attachment.html>


Mais detalhes sobre a lista de discussão R-br