Data.table içinde yapabileceğim dtplyr ile ne yapamam?


10

Öğrenme çabamı, özellikle ve arasında dplyr, R'de veri tarama için yatırmalı mıyım ?dtplyrdata.table

  • Kullandığım dplyrçoğunlukla, ancak veri ı kullanacağı için çok büyük olduğunda data.table, nadir bir olay olduğu. Şimdi dtplyrv1.0 bir arayüz olarak data.tableçıktı, yüzeyde bir daha asla data.tablearayüzü kullanma konusunda endişelenmem gerekiyor gibi görünüyor .

  • Yani en yararlı özellikleri veya yönleri ne olan data.tablebu olamaz kullanılarak yapılabilir dtplyranda ve muhtemelen yapılabilir asla dtplyr?

  • Yüzünde, dplyrfaydaları ile data.tablesesi dtplyrsollayacak gibi yapar dplyr. Tamamen olgunlaştıktan dplyrsonra kullanmak için herhangi bir neden olacak dtplyrmı?

Not: dplyrvs hakkında sormuyorum data.table( data.table vs dplyr'de: biri diğerinin iyi yapamayacağı veya kötü yapamayacağı bir şey yapabilir mi? ) t dtplyrKullanılacak araç ol.


1
Orada şey mi sen kuyuda yapabilirsiniz dplyrsen iyi yapamaz data.table? Değilse, geçiş yapmak data.tabledaha iyi olacaktır dtplyr.
sindri_baldur

2
Gönderen dtplyrBenioku'da, 'Bazı data.tableifadeler doğrudan sahip dplyreşdeğer. Örneğin, çapraz veya yuvarlanma birleşimlerini ifade etmenin bir yolu yok dplyr. ' ve ' dplyrAnlambilimle eşleştirmek için , mutate() varsayılan olarak yerinde değişiklik yapmaz. Bu, ilgili ifadelerin mutate()çoğunun, data.tabledoğrudan kullanıyorsanız gerekli olmayacak bir kopyasını oluşturması gerektiği anlamına gelir . ' Bu ikinci bölümün etrafında bir yol var ama ne sıklıkta mutatekullanıldığını düşünürsek , bu gözlerimde oldukça büyük bir dezavantaj.
ClancyStats

Yanıtlar:


16

En iyi rehberlerimi vermeye çalışacağım ama bu kolay değil çünkü bir kişinin {data.table}, {dplyr}, {dtplyr} ve ayrıca {base R} hakkında bilgi sahibi olması gerekiyor. {Data.table} ve birçok {düzenli dünya} paketi kullanıyorum ({dplyr} hariç). Her ikisini de seviyorum, ancak veri sözdizimini tercih ediyorum. Umarım tüm düzenli dünya paketleri gerektiğinde arka uç olarak {dtplyr} veya {data.table} kullanır.

Diğer tüm çevirilerde olduğu gibi (dplyr-to-sparkly / SQL'i düşünün), en azından şimdilik tercüme edilebilecek veya çevrilemeyecek şeyler vardır. Yani, belki bir gün {dtplyr}% 100 çeviri yapabilir, kim bilir. Aşağıdaki liste kapsamlı değildir ve ilgili konular / paketler / konular / vb. Hakkındaki bilgilerime dayanarak cevaplamak için elimden geleni yapacağım için% 100 doğru değildir.

Önemli olarak, tamamen doğru olmayan cevaplar için, umarım {data.table} 'ın hangi yönlerine dikkat etmeniz gerektiğine dair bazı kılavuzlar verir ve {dtplyr} ile karşılaştırır ve cevapları kendiniz öğrenirsiniz. Bu cevapları kesin kabul etmeyin.

Ve umarım bu yazı tüm {dplyr}, {data.table} veya {dtplyr} kullanıcıları / içerik oluşturucularının tartışmalar ve işbirlikleri için kaynaklardan biri olarak kullanılabilir ve #RStatları daha da iyi hale getirir.

{data.table} yalnızca hızlı ve bellek verimli işlemler için kullanılmaz. Ben de dahil olmak üzere birçok kişi {data.table} ifadesinin zarif sözdizimini tercih ediyor. Ayrıca frollapplyC'de yazılan haddeleme ailesi (yani ) gibi zaman serisi işlevleri gibi diğer hızlı işlemleri de içerir . Düzenli dahil olmak üzere herhangi bir işlevle kullanılabilir. Çok {data.table} + {purrr} kullanıyorum!

Operasyonların karmaşıklığı

Bu kolayca tercüme edilebilir

library(data.table)
library(dplyr)
library(flights)
data <- data.table(diamonds)

# dplyr 
diamonds %>%
  filter(cut != "Fair") %>% 
  group_by(cut) %>% 
  summarize(
    avg_price    = mean(price),
    median_price = as.numeric(median(price)),
    count        = n()
  ) %>%
  arrange(desc(count))

# data.table
data [
  ][cut != 'Fair', by = cut, .(
      avg_price    = mean(price),
      median_price = as.numeric(median(price)),
      count        = .N
    )
  ][order( - count)]

{data.table} çok hızlı ve bellek açısından verimli çünkü (neredeyse?) her şey sıfırdan referansa göre güncelleme , anahtar (SQL düşünün) ve paketin her yerinde acımasız optimizasyon kavramları ile baştan inşa edildi (yanififelse , fread/freadR tabanı tarafından benimsenen sayı tabanı sıralaması), sözdiziminin kısa ve tutarlı olmasını sağlarken, bu yüzden zarif olduğunu düşünüyorum.

Gönderen Giriş data.table için , örneğin ana veri manipülasyon işlemleri vb alt kümesi, grup güncelleme katılmak için bir araya tutulur

  • özlü ve tutarlı sözdizimi ...

  • her bir operasyonu haritalamak zorunda kalmanın bilişsel yükü olmadan akıcı bir şekilde analiz yapmak ...

  • Her işlem için gereken verileri tam olarak bilerek işlemleri dahili ve çok etkili bir şekilde otomatik olarak optimize ederek çok hızlı ve bellekte verimli koda yol açar

Son nokta, örnek olarak,

# Calculate the average arrival and departure delay for all flights with “JFK” as the origin airport in the month of June.
flights[origin == 'JFK' & month == 6L,
        .(m_arr = mean(arr_delay), m_dep = mean(dep_delay))]
  • İlk olarak i içinde başlangıç ​​havaalanının "JFK" ve ayın 6L'ye eşit olduğu satır dizinlerini bulmak için alt kümeyi ayarladık. Henüz bu satırlara karşılık gelen verilerin tamamını alt kümeleştirmiyoruz.

  • Şimdi j'ye bakıyoruz ve sadece iki sütun kullandığını görüyoruz. Ve yapmamız gereken ortalamalarını () hesaplamak. Bu nedenle, yalnızca eşleşen satırlara karşılık gelen sütunları alt gruplara ayırıyoruz ve ortalamalarını () hesaplıyoruz.

Çünkü terimi üç ana bileşeni (i, j ve ile) içerisinde birlikte [...] , data.table üç görebilir ve ayrı ayrı, değerlendirmeden önce tamamen eşleşen olmayan optimize . Bu nedenle, hem hız hem de bellek verimliliği için tüm alt kümeden (yani arr_delay ve dep_delay dışındaki sütunları alt kümeden) kaçınabiliyoruz.

{Data.table} 'ın faydalarından yararlanmak için, {dtplr} çevirisinin bu açıdan doğru olması gerekir. İşlemler ne kadar karmaşıksa, çeviriler de o kadar zor olur. Yukarıdaki gibi basit işlemler için kesinlikle kolayca çevrilebilir. Karmaşık olanlar veya {dtplyr} tarafından desteklenmeyenler için, yukarıda belirtildiği gibi kendinizi bulmanız gerekir, biri çevrilmiş sözdizimini ve karşılaştırmalı değerlendirmeyi karşılaştırmalı ve tanıdık ilgili paketler olmalıdır.

Karmaşık işlemler veya desteklenmeyen işlemler için, aşağıda bazı örnekler verebilirim. Yine, sadece elimden geleni yapıyorum. Bana karşı nazik ol.

Güncelleştirme referans-ile-

Giriş / ayrıntılara girmeyeceğim ama işte bazı bağlantılar

Ana kaynak: Referans anlambilimi

Daha fazla detay: Bir data.table dosyasının tam olarak başka bir veriye (yani bir kopyasına) referans olduğunu anlamak.

Güncelleme-referans olarak bence, {data.table} en önemli özelliği ve o kadar hızlı ve bellek verimli hale nasıl en. dplyr::mutatevarsayılan olarak desteklemez. {Dtplyr} 'a aşina olmadığım için, {dtplyr} tarafından ne kadar ve hangi işlemlerin desteklenebileceğinden veya desteklenemediğinden emin değilim. Yukarıda belirtildiği gibi, aynı zamanda, çevirileri etkileyen işlemlerin karmaşıklığına da bağlıdır.

{Data.table} içinde referansla güncelleme kullanmanın iki yolu vardır

  • {data.table} için atama operatörü :=

  • set-ailesi: set, setnames, setcolorder, setkey, setDT, fsetdiff, ve daha birçok

:=ile karşılaştırıldığında daha yaygın olarak kullanılmaktadır set. Karmaşık ve büyük veri kümesi için, en yüksek hız ve bellek verimliliğini elde etmenin anahtarı referans olarak güncelleme işlemidir . Düşünmenin kolay yolu (% 100 doğru değil, çünkü ayrıntılar bundan daha karmaşıktır, çünkü sert / sığ kopya ve diğer birçok faktörü içerir), her biri 10 sütun ve 1 GB ile 10 GB'lık büyük veri kümesiyle uğraştığınızı varsayalım . Bir sütunu değiştirmek için yalnızca 1 GB ile uğraşmanız gerekir.

Önemli nokta, referansa göre güncelleme ile sadece gerekli verilerle ilgilenmeniz gerektiğidir. Bu nedenle {data.table} 'ı kullanırken, özellikle büyük veri kümesiyle uğraşırken, mümkün olan her zaman referansa göre güncelleme kullanırız . Örneğin, büyük modelleme veri kümesini değiştirmek

# Manipulating list columns

df <- purrr::map_dfr(1:1e5, ~ iris)
dt <- data.table(df)

# data.table
dt [,
    by = Species, .(data   = .( .SD )) ][,  # `.(` shorthand for `list`
    model   := map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = . )) ][,
    summary := map(model, summary) ][,
    plot    := map(data, ~ ggplot( . , aes(Sepal.Length, Sepal.Width)) +
                           geom_point())]

# dplyr
df %>% 
  group_by(Species) %>% 
  nest() %>% 
  mutate(
    model   = map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = . )),
    summary = map(model, summary),
    plot    = map(data, ~ ggplot( . , aes(Sepal.Length, Sepal.Width)) +
                          geom_point())
  )

Yerleştirme işlemi list(.SD)düzenli kullanıcılar kullanıldıkça {dtlyr} tarafından desteklenmiyor olabilir tidyr::nest? Bu yüzden sonraki işlemlerin {data.table} yolu daha hızlı ve daha az bellek olarak çevrilip çevrilemeyeceğinden emin değilim.

NOT: data.table sonucu "milisaniye", dplyr "dakika"

df <- purrr::map_dfr(1:1e5, ~ iris)
dt <- copy(data.table(df))

bench::mark(
  check = FALSE,

  dt[, by = Species, .(data = list(.SD))],
  df %>% group_by(Species) %>% nest()
)
# # A tibble: 2 x 13
#   expression                                   min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
#   <bch:expr>                              <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
# 1 dt[, by = Species, .(data = list(.SD))] 361.94ms 402.04ms   2.49      705.8MB     1.24     2     1
# 2 df %>% group_by(Species) %>% nest()        6.85m    6.85m   0.00243     1.4GB     2.28     1   937
# # ... with 5 more variables: total_time <bch:tm>, result <list>, memory <list>, time <list>,
# #   gc <list>

Referansla güncellemenin birçok kullanım örneği vardır ve hatta {data.table} kullanıcıları daha fazla kod gerektirdiğinden, gelişmiş sürümünü her zaman kullanmaz. İster {dtplyr} kutudan çıktığı gibi destekliyor olun, kendinizi bulmanız gerekir.

Aynı işlevler için birden çok referansla güncelleme

Ana kaynak: lapply () ile data.table içinde zarif bir şekilde birden fazla sütun atama

Bu ya daha yaygın olarak kullanılan :=ya da set.

dt <- data.table( matrix(runif(10000), nrow = 100) )

# A few variants

for (col in paste0('V', 20:100))
  set(dt, j = col, value = sqrt(get(col)))

for (col in paste0('V', 20:100))
  dt[, (col) := sqrt(get(col))]

# I prefer `purrr::map` to `for`
library(purrr)
map(paste0('V', 20:100), ~ dt[, (.) := sqrt(get(.))])

{Data.table} Matt Dowle'ın yaratıcısına göre

(Çok sayıda satıra döngü setinin çok sayıda sütundan daha yaygın olabileceğini unutmayın.)

+ Setkey + referansa göre güncelle

Son zamanlarda nispeten büyük veri ve benzer birleştirme desenleri ile hızlı birleştirme ihtiyacım vardı, bu yüzden normal birleştirme yerine referansla güncelleme gücünü kullanıyorum . Daha fazla kod gerektirdiklerinden, bunları yeniden adlandırılabilirlik ve okunabilirlik için standart dışı değerlendirme ile özel pakete sarıyorum setjoin.

Burada bir kıyaslama yaptım: data.table join + referansla güncelleme + setkey

özet

# For brevity, only the codes for join-operation are shown here. Please refer to the link for details

# Normal_join
x <- y[x, on = 'a']

# update_by_reference
x_2[y_2, on = 'a', c := c]

# setkey_n_update
setkey(x_3, a) [ setkey(y_3, a), on = 'a', c := c ]

NOT: dplyr::left_joinAyrıca test edildi ve hem {data.table} daha fazla bellek kullanmak 'ın, 9.000 ms ~ ile en yavaş olduğunu update_by_referenceve setkey_n_updateancak {data.table} daha az bellek kullanmak' ın normal_join. Yaklaşık ~ 2.0GB bellek tüketti. Yalnızca {data.table} üzerine odaklanmak istediğim için eklemedim.

Önemli bulgular

  • setkey + updateve updatevardır ~ 11 ve ~ 6.5 kat daha fazladır normal join, sırasıyla
  • İlk katılmak üzerine, performansı setkey + updatebenzer updatebir havai olarak setkey, büyük ölçüde kendi performans kazancı uzaklıklar
  • İkinci ve sonraki katılır ilgili olarak setkeygerekli değildir, çünkü setkey + updatedaha hızlı olan update~ 1.8 kat (veya daha hızlı normal join~ 11 kat)

görüntü

Örnekler

Performans ve bellek tasarruflu birleşimler için, updateveya setkey + updatedaha fazla kod pahasına ikincisinin daha hızlı olduğu yerlerde kullanın .

Kısaca kısaca sözde kodlar görelim . Mantık aynı.

Bir veya birkaç sütun için

a <- data.table(x = ..., y = ..., z = ..., ...)
b <- data.table(x = ..., y = ..., z = ..., ...)

# `update`
a[b, on = .(x), y := y]
a[b, on = .(x),  `:=` (y = y, z = z, ...)]
# `setkey + update`
setkey(a, x) [ setkey(b, x), on = .(x), y := y ]
setkey(a, x) [ setkey(b, x), on = .(x),  `:=` (y = y, z = z, ...) ]

Birçok sütun için

cols <- c('x', 'y', ...)
# `update`
a[b, on = .(x), (cols) := mget( paste0('i.', cols) )]
# `setkey + update`
setkey(a, x) [ setkey(b, x), on = .(x), (cols) := mget( paste0('i.', cols) ) ]

Hızlı ve bellek verimli birleşimler için sarıcı ... birçoğu ... benzer birleştirme modeliyle, setjoinyukarıdaki gibi - ile update - birlikte veya olmadansetkey

setjoin(a, b, on = ...)  # join all columns
setjoin(a, b, on = ..., select = c('columns_to_be_included', ...))
setjoin(a, b, on = ..., drop   = c('columns_to_be_excluded', ...))
# With that, you can even use it with `magrittr` pipe
a %>%
  setjoin(...) %>%
  setjoin(...)

İle setkeyargüman onatlanabilir. Okunabilirlik için, özellikle başkalarıyla işbirliği için de dahil edilebilir.

Büyük sıra operasyonu

  • yukarıda belirtildiği gibi kullanın set
  • tablonuzu önceden doldurun, referansla güncelleme tekniklerini kullanın
  • tuşunu kullanarak alt küme (ie setkey)

İlgili kaynak: data.table nesnesinin sonuna başvuruyla bir satır ekleyin

Referansa göre güncellemenin özeti

Bunlar, referans ile güncelleme için bazı kullanım durumlarıdır . Daha çok var.

Gördüğünüz gibi, büyük verilerle başa çıkmanın gelişmiş kullanımı için, büyük veri kümesi için referansla güncellemeyi kullanan birçok kullanım durumu ve tekniği vardır . {Data.table} içinde kullanmak o kadar kolay değil ve {dtplyr} tarafından desteklenip desteklenmediğini kendiniz öğrenebilirsiniz.

Hızlı ve bellek verimli işlemler için {data.table} 'ın en güçlü özelliği olduğunu düşündüğüm için bu yazıdaki referansa göre güncellemeye odaklanıyorum . Bununla birlikte, onu çok verimli kılan birçok, daha birçok yönü var ve bence bu {dtplyr} tarafından yerel olarak desteklenmiyor.

Diğer önemli hususlar

Nelerin desteklenmediği / desteklenmediği, aynı zamanda işlemlerin karmaşıklığına ve data.table'ın referansa göre güncelleme veya setkey. Ve çevrilen kodun daha verimli olup olmadığı (data.table kullanıcılarının yazacağı kod) da başka bir faktördür (yani kod çevrilir, ancak verimli sürüm mü?). Birçok şey birbirine bağlıdır.

Bu yönlerin birçoğu yukarıda belirtilen noktalar ile ilişkilidir

  • operasyonların karmaşıklığı

  • Güncelleme referans-ile-

{Dtplyr} 'ın bu işlemleri özellikle birleştirildiklerinde destekleyip desteklemediğini öğrenebilirsiniz.

Etkileşimli oturum sırasında, küçük veya büyük veri kümesiyle uğraşırken bir başka yararlı hile, {data.table}, programlama ve hesaplama süresini büyük ölçüde azaltma vaadini yerine getirir .

Hem hız hem de 'süper şarjlı rownames' için tekrarlanan değişken için ayar anahtarı (değişken adı belirtmeden alt küme).

dt <- data.table(iris)
setkey(dt, Species) 

dt['setosa',    do_something(...), ...]
dt['virginica', do_another(...),   ...]
dt['setosa',    more(...),         ...]

# `by` argument can also be omitted, particularly useful during interactive session
# this ultimately becomes what I call 'naked' syntax, just type what you want to do, without any placeholders. 
# It's simply elegant
dt['setosa', do_something(...), Species, ...]

İşlemleriniz yalnızca ilk örnekteki gibi basit işlemleri içeriyorsa, {dtplyr} işi tamamlayabilir. Karmaşık / desteklenmeyenler için, bu kılavuzu {dtplyr} 'ın çevrilmiş olanlarını, deneyimli verilerle karşılaştırmak için kullanabilirsiniz. Çeviri, farklı büyük veri durumlarıyla ilgilenmek için farklı teknikler olabileceğinden, bunun en etkili yol olduğu anlamına gelmez. Daha da büyük veri kümesi için, en iyisini elde etmek için {data.table} ile {disk.frame} , {fst} ve {drake} ve diğer harika paketleri birleştirebilirsiniz. Ayrıca bir {big.data.table} var ama şu anda etkin değil.

Umarım herkese yardımcı olur. İyi günler ☺☺


3

Eşit olmayan birleşimler ve yuvarlanan birleşimler akla geliyor. Eşit işlevlerin dplyr'a dahil edilmesi için herhangi bir plan mevcut görünmemektedir, bu nedenle dtplyr'ın çevireceği hiçbir şey yoktur.

Ayrıca dplyr'de olmayan yeniden şekillendirme (reshape2'deki aynı işlevlere eşdeğer optimize dcast ve erime) var.

Tüm * _if ve * _at işlevleri şu anda dtplyr ile de çevrilemez ancak bunlar çalışmalardadır.


0

Birleştirme sırasında bir sütunu güncelleme Bazı .SD hileleri Birçok f işlevi Ve tanrı başka ne biliyor çünkü # rdatatable sadece basit bir kütüphaneden daha fazlası ve birkaç işlevle özetlenemiyor

Tek başına bütün bir ekosistem

R'yi başlattığım günden beri hiç dplyr'e ihtiyacım olmadı. Çünkü data.table çok iyi

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.