Impulsione seus dados Munging com R
Publicados: 2022-03-11A linguagem R é frequentemente percebida como uma linguagem para estatísticos e cientistas de dados. Muito tempo atrás, isso era principalmente verdade. No entanto, ao longo dos anos, a flexibilidade que R fornece por meio de pacotes tornou o R em uma linguagem de propósito mais geral. R foi open source em 1995 e, desde então, os repositórios de pacotes R estão em constante crescimento. Ainda assim, comparado a linguagens como Python, R é fortemente baseado em dados.
Falando em dados, os dados tabulares merecem atenção especial, pois são um dos tipos de dados mais usados. É um tipo de dado que corresponde a uma estrutura de tabela conhecida em bancos de dados, onde cada coluna pode ser de um tipo diferente, e o desempenho de processamento desse tipo de dado em particular é o fator crucial para muitas aplicações.
Neste artigo, apresentaremos como obter a transformação de dados tabulares de maneira eficiente. Muitas pessoas que já usam R para aprendizado de máquina não estão cientes de que a manipulação de dados pode ser feita mais rapidamente em R e que não precisam usar outra ferramenta para isso.
Solução de alto desempenho em R
A Base R introduziu a classe data.frame
no ano de 1997, que era baseada no S-PLUS antes dela. Ao contrário dos bancos de dados comumente usados que armazenam dados linha por linha, o R data.frame
armazena os dados na memória como uma estrutura orientada a colunas, tornando-o mais eficiente em cache para operações de coluna que são comuns em análises. Além disso, embora R seja uma linguagem de programação funcional, ela não impõe isso ao desenvolvedor. Ambas as oportunidades foram bem abordadas pelo pacote R data.table
, que está disponível no repositório CRAN. Ele tem um desempenho bastante rápido ao agrupar operações e é particularmente eficiente em termos de memória por ter cuidado ao materializar subconjuntos de dados intermediários, como materializar apenas as colunas necessárias para uma determinada tarefa. Também evita cópias desnecessárias por meio de sua semântica de referência ao adicionar ou atualizar colunas. A primeira versão do pacote foi publicada em abril de 2006, melhorando significativamente o desempenho do data.frame
na época. A descrição inicial do pacote era:
Este pacote faz muito pouco. A única razão para sua existência é que o livro branco especifica que data.frame deve ter nomes de linha. Este pacote define uma nova classe data.table que funciona como um data.frame, mas usa até 10 vezes menos memória e pode ser até 10 vezes mais rápido para criar (e copiar). Ele também aproveita a oportunidade para permitir expressões como subset() e with() dentro do []. A maior parte do código é copiada das funções base com o código de manipulação de row.names removido.
Desde então, as implementações de data.frame
e data.table
foram aprimoradas, mas data.table
continua sendo incrivelmente mais rápido que a base R. Na verdade, data.table
não é apenas mais rápido que a base R, mas parece ser um da ferramenta de processamento de dados de código aberto mais rápida disponível, competindo com ferramentas como Python Pandas e bancos de dados de armazenamento colunar ou aplicativos de big data como Spark. Seu desempenho em infraestrutura compartilhada distribuída ainda não foi comparado, mas poder ter até dois bilhões de linhas em uma única instância oferece perspectivas promissoras. O desempenho excepcional anda de mãos dadas com as funcionalidades. Além disso, com os esforços recentes para paralelizar partes demoradas para ganhos incrementais de desempenho, uma direção para aumentar o limite de desempenho parece bastante clara.
Exemplos de transformação de dados
Aprender R fica um pouco mais fácil porque funciona de forma interativa, então podemos seguir exemplos passo a passo e ver os resultados de cada etapa a qualquer momento. Antes de começarmos, vamos instalar o pacote data.table
do repositório CRAN.
install.packages("data.table")
Dica útil : Podemos abrir o manual de qualquer função apenas digitando seu nome com um ponto de interrogação inicial, ou seja, ?install.packages
.
Carregando dados em R
Existem vários pacotes para extrair dados de uma ampla variedade de formatos e bancos de dados, que geralmente incluem drivers nativos. Carregaremos dados do arquivo CSV , o formato mais comum para dados tabulares brutos. O arquivo usado nos exemplos a seguir pode ser encontrado aqui. Não precisamos nos preocupar com o desempenho da leitura de CSV
, pois a função fread
é altamente otimizada para isso.
Para usar qualquer função de um pacote, precisamos carregá-la com a chamada da library
.
library(data.table) DT <- fread("flights14.csv") print(DT)
## year month day dep_delay arr_delay carrier origin dest air_time ## 1: 2014 1 1 14 13 AA JFK LAX 359 ## 2: 2014 1 1 -3 13 AA JFK LAX 363 ## 3: 2014 1 1 2 9 AA JFK LAX 351 ## 4: 2014 1 1 -8 -26 AA LGA PBI 157 ## 5: 2014 1 1 2 1 AA JFK LAX 350 ## --- ## 253312: 2014 10 31 1 -30 UA LGA IAH 201 ## 253313: 2014 10 31 -5 -14 UA EWR IAH 189 ## 253314: 2014 10 31 -8 16 MQ LGA RDU 83 ## 253315: 2014 10 31 -4 15 MQ LGA DTW 75 ## 253316: 2014 10 31 -5 1 MQ LGA SDF 110 ## distance hour ## 1: 2475 9 ## 2: 2475 11 ## 3: 2475 19 ## 4: 1035 7 ## 5: 2475 13 ## --- ## 253312: 1416 14 ## 253313: 1400 8 ## 253314: 431 11 ## 253315: 502 11 ## 253316: 659 8
Se nossos dados não estiverem bem modelados para processamento adicional, pois eles precisam ser reformulados do formato longo para largo ou largo para longo (também conhecido como pivô e unpivot ), podemos olhar para ?dcast
e ?melt
, conhecido do pacote reshape2. No entanto, data.table
implementa métodos mais rápidos e eficientes de memória para a classe data.table/data.frame.
Consultando com sintaxe data.table
Se você estiver familiarizado com data.frame
A consulta data.table
é muito semelhante à consulta data.frame
. Ao filtrar no argumento i
, podemos usar nomes de colunas diretamente sem a necessidade de acessá-los com o sinal $
, como df[df$col > 1, ]
. Ao fornecer o próximo argumento j
, fornecemos uma expressão a ser avaliada no escopo de nosso data.table
. Para passar um argumento j
sem expressão, use with=FALSE
. O terceiro argumento, não presente no método data.frame
, define os grupos, fazendo com que a expressão em j
seja avaliada por grupos.
# data.frame DF[DF$col1 > 1L, c("col2", "col3")] # data.table DT[col1 > 1L, .(col2, col3), ...] # by group using: `by = col4`
Se você estiver familiarizado com bancos de dados
A consulta data.table
em muitos aspectos corresponde a consultas SQL com as quais mais pessoas podem estar familiarizadas. O DT
abaixo representa o objeto data.table
e corresponde à cláusula SQLs FROM
.
DT[ i = where, j = select | update, by = group by] [ having, ... ] [ order by, ... ] [ ... ] ... [ ... ]
Classificando Linhas e Reordenando Colunas
A classificação de dados é uma transformação crucial para séries temporais e também é importada para extração e apresentação de dados. A classificação pode ser obtida fornecendo o vetor inteiro da ordem das linhas para o argumento i
, da mesma forma que data.frame
. O primeiro argumento na order(carrier, -dep_delay)
selecionará os dados em ordem crescente no campo da carrier
e em ordem decrescente na medida dep_delay
. O segundo argumento j
, conforme descrito na seção anterior, define as colunas (ou expressões) a serem retornadas e sua ordem.
ans <- DT[order(carrier, -dep_delay), .(carrier, origin, dest, dep_delay)] head(ans)
## carrier origin dest dep_delay ## 1: AA EWR DFW 1498 ## 2: AA JFK BOS 1241 ## 3: AA EWR DFW 1071 ## 4: AA EWR DFW 1056 ## 5: AA EWR DFW 1022 ## 6: AA EWR DFW 989
Para reordenar os dados por referência, em vez de consultar os dados em uma ordem específica, usamos as funções set*
.
setorder(DT, carrier, -dep_delay) leading.cols <- c("carrier","dep_delay") setcolorder(DT, c(leading.cols, setdiff(names(DT), leading.cols))) print(DT)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## --- ## 253312: WN -12 2014 3 9 -21 LGA BNA 115 ## 253313: WN -13 2014 3 10 -18 EWR MDW 112 ## 253314: WN -13 2014 5 17 -30 LGA HOU 202 ## 253315: WN -13 2014 6 15 10 LGA MKE 101 ## 253316: WN -13 2014 8 19 -30 LGA CAK 63 ## distance hour ## 1: 1372 7 ## 2: 187 13 ## 3: 1372 10 ## 4: 1372 6 ## 5: 1372 7 ## --- ## 253312: 764 16 ## 253313: 711 20 ## 253314: 1428 17 ## 253315: 738 20 ## 253316: 397 16
Na maioria das vezes, não precisamos do conjunto de dados original e do conjunto de dados ordenado/classificado. Por padrão, a linguagem R, semelhante a outras linguagens de programação funcionais, retornará dados classificados como um novo objeto e, portanto, exigirá o dobro de memória do que a classificação por referência.
Consultas de subconjunto
Vamos criar um conjunto de dados de subconjunto para a origem do voo “JFK” e o mês de 6 a 9. No segundo argumento, agrupamos os resultados nas colunas listadas, adicionando uma variável calculada sum_delay
.
ans <- DT[origin == "JFK" & month %in% 6:9, .(origin, month, arr_delay, dep_delay, sum_delay = arr_delay + dep_delay)] head(ans)
## origin month arr_delay dep_delay sum_delay ## 1: JFK 7 925 926 1851 ## 2: JFK 8 727 772 1499 ## 3: JFK 6 466 451 917 ## 4: JFK 7 414 450 864 ## 5: JFK 6 411 442 853 ## 6: JFK 6 333 343 676
Por padrão, ao subconfigurar o conjunto de dados em uma única coluna, data.table
criará automaticamente um índice para essa coluna. Isso resulta em respostas em tempo real em quaisquer outras chamadas de filtragem nessa coluna.
Atualizar conjunto de dados
A adição de uma nova coluna por referência é realizada usando o operador :=
, ele atribui uma variável ao conjunto de dados no local. Isso evita a cópia na memória do conjunto de dados, portanto, não precisamos atribuir resultados a cada nova variável.
DT[, sum_delay := arr_delay + dep_delay] head(DT)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: 187 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980
Para adicionar mais variáveis de uma vez, podemos usar a sintaxe DT[,
:= (sum_delay = arr_delay + dep_delay)]
, semelhante a .(sum_delay = arr_delay + dep_delay)
ao consultar do conjunto de dados.
É possível sub-atribuir por referência, atualizando apenas linhas específicas no local, apenas combinando com o argumento i
.
DT[origin=="JFK", distance := NA] head(DT)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: NA 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980
Dados agregados
Para agregar dados, fornecemos o terceiro argumento by
o colchete. Então, em j
precisamos fornecer chamadas de funções agregadas, para que os dados possam ser realmente agregados. O símbolo .N
usado no argumento j
corresponde ao número de todas as observações em cada grupo. Como mencionado anteriormente, os agregados podem ser combinados com subconjuntos em linhas e colunas de seleção.
ans <- DT[, .(m_arr_delay = mean(arr_delay), m_dep_delay = mean(dep_delay), count = .N), .(carrier, month)] head(ans)
## carrier month m_arr_delay m_dep_delay count ## 1: AA 10 5.541959 7.591497 2705 ## 2: AA 4 1.903324 3.987008 2617 ## 3: AA 6 8.690067 11.476475 2678 ## 4: AA 9 -1.235160 3.307078 2628 ## 5: AA 8 4.027474 8.914054 2839 ## 6: AA 7 9.159886 11.665953 2802
Muitas vezes, podemos precisar comparar um valor de uma linha com sua agregação em um grupo. No SQL, aplicamos agregados sobre a partição por : AVG(arr_delay) OVER (PARTITION BY carrier, month)
.

ans <- DT[, .(arr_delay, carrierm_mean_arr = mean(arr_delay), dep_delay, carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(ans)
## carrier month arr_delay carrierm_mean_arr dep_delay carrierm_mean_dep ## 1: AA 10 1494 5.541959 1498 7.591497 ## 2: AA 10 840 5.541959 848 7.591497 ## 3: AA 10 317 5.541959 338 7.591497 ## 4: AA 10 292 5.541959 331 7.591497 ## 5: AA 10 322 5.541959 304 7.591497 ## 6: AA 10 306 5.541959 299 7.591497
Se não quisermos consultar dados com esses agregados e, em vez disso, apenas colocá-los na atualização real da tabela por referência, podemos fazer isso com o operador :=
. Isso evita a cópia na memória do conjunto de dados, portanto, não precisamos atribuir resultados à nova variável.
DT[, `:=`(carrierm_mean_arr = mean(arr_delay), carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(DT)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep ## 1: 1372 7 2992 5.541959 7.591497 ## 2: NA 13 2464 1.903324 3.987008 ## 3: 1372 10 2135 8.690067 11.476475 ## 4: 1372 6 2171 -1.235160 3.307078 ## 5: 1372 7 2095 8.690067 11.476475 ## 6: 1372 11 1980 8.690067 11.476475
Unir conjuntos de dados
A junção e mesclagem de base R de conjuntos de dados é considerada um tipo especial de operação de subconjunto . Fornecemos um conjunto de dados ao qual queremos unir no primeiro argumento de colchetes i
. Para cada linha no conjunto de dados fornecido para i
, combinamos as linhas do conjunto de dados em que usamos [
. Se quisermos manter apenas as linhas correspondentes ( inner join ), passamos um argumento extra nomatch = 0L
. Usamos o argumento on
para especificar as colunas nas quais queremos unir os dois conjuntos de dados.
# create reference subset carrierdest <- DT[, .(count=.N), .(carrier, dest) # count by carrier and dest ][1:10 # just 10 first groups ] # chaining `[...][...]` as subqueries print(carrierdest)
## carrier dest count ## 1: AA DFW 5877 ## 2: AA BOS 1173 ## 3: AA ORD 4798 ## 4: AA SEA 298 ## 5: AA EGE 85 ## 6: AA LAX 3449 ## 7: AA MIA 6058 ## 8: AA SFO 1312 ## 9: AA AUS 297 ## 10: AA DCA 172
# outer join ans <- carrierdest[DT, on = c("carrier","dest")] print(ans)
## carrier dest count dep_delay year month day arr_delay origin ## 1: AA DFW 5877 1498 2014 10 4 1494 EWR ## 2: AA BOS 1173 1241 2014 4 15 1223 JFK ## 3: AA DFW 5877 1071 2014 6 13 1064 EWR ## 4: AA DFW 5877 1056 2014 9 12 1115 EWR ## 5: AA DFW 5877 1022 2014 6 16 1073 EWR ## --- ## 253312: WN BNA NA -12 2014 3 9 -21 LGA ## 253313: WN MDW NA -13 2014 3 10 -18 EWR ## 253314: WN HOU NA -13 2014 5 17 -30 LGA ## 253315: WN MKE NA -13 2014 6 15 10 LGA ## 253316: WN CAK NA -13 2014 8 19 -30 LGA ## air_time distance hour sum_delay carrierm_mean_arr ## 1: 200 1372 7 2992 5.541959 ## 2: 39 NA 13 2464 1.903324 ## 3: 175 1372 10 2135 8.690067 ## 4: 198 1372 6 2171 -1.235160 ## 5: 178 1372 7 2095 8.690067 ## --- ## 253312: 115 764 16 -33 6.921642 ## 253313: 112 711 20 -31 6.921642 ## 253314: 202 1428 17 -43 22.875845 ## 253315: 101 738 20 -3 14.888889 ## 253316: 63 397 16 -43 7.219670 ## carrierm_mean_dep ## 1: 7.591497 ## 2: 3.987008 ## 3: 11.476475 ## 4: 3.307078 ## 5: 11.476475 ## --- ## 253312: 11.295709 ## 253313: 11.295709 ## 253314: 30.546453 ## 253315: 24.217560 ## 253316: 17.038047
# inner join ans <- DT[carrierdest, # for each row in carrierdest nomatch = 0L, # return only matching rows from both tables on = c("carrier","dest")] # joining on columns carrier and dest print(ans)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1071 2014 6 13 1064 EWR DFW 175 ## 3: AA 1056 2014 9 12 1115 EWR DFW 198 ## 4: AA 1022 2014 6 16 1073 EWR DFW 178 ## 5: AA 989 2014 6 11 991 EWR DFW 194 ## --- ## 23515: AA -8 2014 10 11 -13 JFK DCA 53 ## 23516: AA -9 2014 5 21 -12 JFK DCA 52 ## 23517: AA -9 2014 6 5 -6 JFK DCA 53 ## 23518: AA -9 2014 10 2 -21 JFK DCA 51 ## 23519: AA -11 2014 5 27 10 JFK DCA 55 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: 1372 10 2135 8.690067 11.476475 5877 ## 3: 1372 6 2171 -1.235160 3.307078 5877 ## 4: 1372 7 2095 8.690067 11.476475 5877 ## 5: 1372 11 1980 8.690067 11.476475 5877 ## --- ## 23515: NA 15 -21 5.541959 7.591497 172 ## 23516: NA 15 -21 4.150172 8.733665 172 ## 23517: NA 15 -15 8.690067 11.476475 172 ## 23518: NA 15 -30 5.541959 7.591497 172 ## 23519: NA 15 -1 4.150172 8.733665 172
Esteja ciente de que, devido à consistência da subconjunto R base, a junção externa é, por padrão, RIGHT OUTER
. Se estivermos procurando LEFT OUTER
, precisamos trocar as tabelas, como no exemplo acima. O comportamento exato também pode ser facilmente controlado no método merge
data.table
, usando a mesma API que a base R merge
data.frame
.
Se quisermos simplesmente pesquisar a(s) coluna(s) em nosso conjunto de dados, podemos fazê-lo com eficiência com o operador :=
no argumento j
durante a junção. Da mesma forma que subatribuimos por referência, conforme descrito na seção Atualizar conjunto de dados, agora adicionamos uma coluna por referência do conjunto de dados ao qual associamos. Isso evita a cópia de dados na memória, portanto, não precisamos atribuir resultados a novas variáveis.
DT[carrierdest, # data.table to join with lkp.count := count, # lookup `count` column from `carrierdest` on = c("carrier","dest")] # join by columns head(DT)
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: NA 13 2464 1.903324 3.987008 1173 ## 3: 1372 10 2135 8.690067 11.476475 5877 ## 4: 1372 6 2171 -1.235160 3.307078 5877 ## 5: 1372 7 2095 8.690067 11.476475 5877 ## 6: 1372 11 1980 8.690067 11.476475 5877
Para agregar durante a junção , use by = .EACHI
. Ele executa a junção que não materializará os resultados intermediários da junção e aplicará agregações em tempo real, tornando a memória eficiente.
Rolling join é um recurso incomum, projetado para lidar com dados ordenados. Adapta-se perfeitamente ao processamento de dados temporais e séries temporais em geral. Ele basicamente rola correspondências em condição de junção para o próximo valor correspondente. Use-o fornecendo o argumento roll
ao ingressar.
A junção de sobreposição rápida une conjuntos de dados com base em períodos e sua manipulação de sobreposição usando vários operadores de sobreposição: any
, within
, start
, end
.
Um recurso de junção não equi para unir conjuntos de dados usando condições não iguais está sendo desenvolvido no momento.
Dados de perfil
Ao explorar nosso conjunto de dados, às vezes podemos querer coletar informações técnicas sobre o assunto, para entender melhor a qualidade dos dados.
Estatísticas descritivas
summary(DT)
## carrier dep_delay year month ## Length:253316 Min. :-112.00 Min. :2014 Min. : 1.000 ## Class :character 1st Qu.: -5.00 1st Qu.:2014 1st Qu.: 3.000 ## Mode :character Median : -1.00 Median :2014 Median : 6.000 ## Mean : 12.47 Mean :2014 Mean : 5.639 ## 3rd Qu.: 11.00 3rd Qu.:2014 3rd Qu.: 8.000 ## Max. :1498.00 Max. :2014 Max. :10.000 ## ## day arr_delay origin dest ## Min. : 1.00 Min. :-112.000 Length:253316 Length:253316 ## 1st Qu.: 8.00 1st Qu.: -15.000 Class :character Class :character ## Median :16.00 Median : -4.000 Mode :character Mode :character ## Mean :15.89 Mean : 8.147 ## 3rd Qu.:23.00 3rd Qu.: 15.000 ## Max. :31.00 Max. :1494.000 ## ## air_time distance hour sum_delay ## Min. : 20.0 Min. : 80.0 Min. : 0.00 Min. :-224.00 ## 1st Qu.: 86.0 1st Qu.: 529.0 1st Qu.: 9.00 1st Qu.: -19.00 ## Median :134.0 Median : 762.0 Median :13.00 Median : -5.00 ## Mean :156.7 Mean : 950.4 Mean :13.06 Mean : 20.61 ## 3rd Qu.:199.0 3rd Qu.:1096.0 3rd Qu.:17.00 3rd Qu.: 23.00 ## Max. :706.0 Max. :4963.0 Max. :24.00 Max. :2992.00 ## NA's :81483 ## carrierm_mean_arr carrierm_mean_dep lkp.count ## Min. :-22.403 Min. :-4.500 Min. : 85 ## 1st Qu.: 2.676 1st Qu.: 7.815 1st Qu.:3449 ## Median : 6.404 Median :11.354 Median :5877 ## Mean : 8.147 Mean :12.465 Mean :4654 ## 3rd Qu.: 11.554 3rd Qu.:17.564 3rd Qu.:6058 ## Max. : 86.182 Max. :52.864 Max. :6058 ## NA's :229797
Cardinalidade
Podemos verificar a exclusividade dos dados usando a função uniqueN
e aplicá-la em todas as colunas. O objeto .SD
na consulta abaixo corresponde ao subconjunto do Data.table :
DT[, lapply(.SD, uniqueN)]
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 14 570 1 10 31 616 3 109 509 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 152 25 1021 134 134 11
Razão NA
Para calcular a proporção de valores desconhecidos ( NA
em R e NULL
em SQL) para cada coluna, fornecemos a função desejada para aplicar em cada coluna.
DT[, lapply(.SD, function(x) sum(is.na(x))/.N)]
## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 0 0 0 0 0 0 0 0 0 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 0.3216654 0 0 0 0 0.9071555
Exportando dados
A exportação rápida de dados tabulares para o formato CSV
também é fornecida pelo pacote data.table
.
tmp.csv <- tempfile(fileext=".csv") fwrite(DT, tmp.csv) # preview exported data cat(system(paste("head -3",tmp.csv), intern=TRUE), sep="\n")
## carrier,dep_delay,year,month,day,arr_delay,origin,dest,air_time,distance,hour,sum_delay,carrierm_mean_arr,carrierm_mean_dep,lkp.count ## AA,1498,2014,10,4,1494,EWR,DFW,200,1372,7,2992,5.54195933456561,7.59149722735674,5877 ## AA,1241,2014,4,15,1223,JFK,BOS,39,,13,2464,1.90332441727168,3.98700802445548,1173
No momento em que escrevo isso, a função fwrite
ainda não foi publicada no repositório CRAN. Para usá-lo precisamos instalar a versão de desenvolvimento data.table
, caso contrário podemos usar a função base R write.csv
, mas não espere que seja rápido.
Recursos
Há muitos recursos disponíveis. Além dos manuais disponíveis para cada função, existem também vinhetas de pacotes, que são tutoriais focados em um determinado assunto. Esses podem ser encontrados na página de introdução. Além disso, a página Apresentações lista mais de 30 materiais (slides, vídeos, etc.) de apresentações data.table
em todo o mundo. Além disso, o suporte da comunidade cresceu ao longo dos anos, atingindo recentemente a 4000ª pergunta na tag data.table
do Stack Overflow, ainda com uma alta proporção (91,9%) de perguntas respondidas. O gráfico abaixo apresenta o número de perguntas marcadas data.table
no Stack Overflow ao longo do tempo.
Resumo
Este artigo fornece exemplos escolhidos para transformação eficiente de dados tabulares em R usando o pacote data.table
. Os números reais de desempenho podem ser examinados procurando benchmarks reproduzíveis. Publiquei um post resumido no blog sobre soluções data.table
para as 50 perguntas mais bem avaliadas do StackOverflow para a linguagem R chamada Solve common R problems eficientemente com data.table, onde você pode encontrar muitas figuras e código reproduzível. O pacote data.table
usa implementação nativa de ordenação rápida de raiz para suas operações de agrupamento e busca binária para subconjuntos/junções rápidas. Esta ordenação de base foi incorporada à base R a partir da versão 3.3.0. Além disso, o algoritmo foi implementado recentemente na plataforma de aprendizado de máquina H2O e paralelizado no cluster H2O, permitindo grandes junções eficientes em linhas de 10B x 10B.