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 frollapply
C'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/fread
R 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::mutate
varsayı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_join
Ayrıca test edildi ve hem {data.table} daha fazla bellek kullanmak 'ın, 9.000 ms ~ ile en yavaş olduğunu update_by_reference
ve setkey_n_update
ancak {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 + update
ve update
vardır ~ 11 ve ~ 6.5 kat daha fazladır normal join
, sırasıyla
- İlk katılmak üzerine, performansı
setkey + update
benzer update
bir havai olarak setkey
, büyük ölçüde kendi performans kazancı uzaklıklar
- İkinci ve sonraki katılır ilgili olarak
setkey
gerekli değildir, çünkü setkey + update
daha hızlı olan update
~ 1.8 kat (veya daha hızlı normal join
~ 11 kat)
Örnekler
Performans ve bellek tasarruflu birleşimler için, update
veya setkey + update
daha 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, setjoin
yukarı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 setkey
argüman on
atlanabilir. 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.
setkey
. Bkz. Tuşlar ve hızlı ikili arama tabanlı altküme
- İkincil endeksler ve otomatik indeksleme
- Veri Analizi için .SD Kullanımı
- zaman serisi fonksiyonları: düşün
frollapply
. yuvarlanma fonksiyonları, yuvarlanan agregalar, sürgülü pencere, hareketli ortalama
- yuvarlanma birleşimi , eşit olmayan birleşim , (bazı) "çapraz" birleşim
- {data.table}, hız ve bellek verimliliğine temel oluşturmuştur, gelecekte birçok işlevi içerecek şekilde genişletilebilir (yukarıda belirtilen zaman serisi işlevlerini nasıl uyguladıkları gibi)
- Genel olarak, data.table yıllardan daha karmaşık işlemler
i
, j
ya by
operasyonlarda, ben ile birleştirmek, özellikle sert çeviriler düşünüyorum (orada hemen hemen her ifadeleri kullanabilirsiniz) güncelleme referans-ile- , setkey
ve diğer yerel data.table gibi işlevlerfrollapply
- Başka bir nokta, baz R veya tidyverse kullanımı ile ilgilidir. Her iki data.table + tidyverse kullanıyorum (dplyr / readr / tidyr hariç). Büyük operasyonlar için, genellikle, örneğin
stringr::str_*
aile ve baz R fonksiyonlarını karşılaştırırım ve baz R'nin bir dereceye kadar daha hızlı olduğunu ve bunları kullandığımı görüyorum. Mesele şu ki, kendinizi sadece düzenli veya verilere saklamayın. Tablo veya ..., işi yapmak için diğer seçenekleri araştırın.
Bu yönlerin birçoğu yukarıda belirtilen noktalar ile ilişkilidir
{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 ☺☺
dplyr
sen iyi yapamazdata.table
? Değilse, geçiş yapmakdata.table
daha iyi olacaktırdtplyr
.