Saya memiliki kebiasaan menggabung tugas-tugas serupa menjadi satu baris. Sebagai contoh, jika saya perlu menyaring a
, b
dan c
dalam tabel data, saya akan menempatkan mereka bersama-sama dalam satu []
dengan ANDs. Kemarin, saya perhatikan bahwa dalam kasus khusus saya ini adalah filter chaining yang sangat lambat dan diuji. Saya telah memasukkan contoh di bawah ini.
Pertama, saya seed generator nomor acak, memuat data.table , dan membuat kumpulan data dummy.
# Set RNG seed
set.seed(-1)
# Load libraries
library(data.table)
# Create data table
dt <- data.table(a = sample(1:1000, 1e7, replace = TRUE),
b = sample(1:1000, 1e7, replace = TRUE),
c = sample(1:1000, 1e7, replace = TRUE),
d = runif(1e7))
Selanjutnya, saya mendefinisikan metode saya. Rantai pendekatan pertama menyaring bersama. ANDs kedua menyaring bersama.
# Chaining method
chain_filter <- function(){
dt[a %between% c(1, 10)
][b %between% c(100, 110)
][c %between% c(750, 760)]
}
# Anding method
and_filter <- function(){
dt[a %between% c(1, 10) & b %between% c(100, 110) & c %between% c(750, 760)]
}
Di sini, saya periksa mereka memberikan hasil yang sama.
# Check both give same result
identical(chain_filter(), and_filter())
#> [1] TRUE
Akhirnya, saya membandingkannya.
# Benchmark
microbenchmark::microbenchmark(chain_filter(), and_filter())
#> Unit: milliseconds
#> expr min lq mean median uq max
#> chain_filter() 25.17734 31.24489 39.44092 37.53919 43.51588 78.12492
#> and_filter() 92.66411 112.06136 130.92834 127.64009 149.17320 206.61777
#> neval cld
#> 100 a
#> 100 b
Dibuat pada 2019-10-25 oleh paket reprex (v0.3.0)
Dalam hal ini, perangkaian mengurangi waktu lari sekitar 70%. Mengapa demikian? Maksudku, apa yang terjadi di bawah tenda data? Saya belum melihat adanya peringatan untuk tidak menggunakan &
, jadi saya terkejut bahwa perbedaannya sangat besar. Dalam kedua kasus mereka mengevaluasi kondisi yang sama, sehingga seharusnya tidak menjadi perbedaan. Dalam kasus AND, &
adalah operator cepat dan kemudian hanya perlu memfilter tabel data sekali (yaitu, menggunakan vektor logis yang dihasilkan dari ANDs), sebagai lawan untuk memfilter tiga kali dalam chaining case.
Pertanyaan bonus
Apakah prinsip ini berlaku untuk operasi tabel data secara umum? Apakah tugas modularising selalu merupakan strategi yang lebih baik?
sumber
base
pengamatan serupa dengan vektor dengan melakukan hal berikut:chain_vec <- function() { x <- which(a < .001); x[which(b[x] > .999)] }
danand_vec <- function() { which(a < .001 & b > .999) }
. (Di manaa
danb
merupakan vektor dengan panjang yang sama darirunif
- saya gunakann = 1e7
untuk cutoff ini).Jawaban:
Sebagian besar, jawabannya diberikan dalam komentar aleady: "metode chaining" untuk
data.table
lebih cepat dalam hal ini daripada "metode anding" karena chaining menjalankan kondisi satu demi satu. Karena setiap langkah mengurangi ukuran,data.table
ada lebih sedikit untuk mengevaluasi untuk langkah selanjutnya. "Anding" mengevaluasi kondisi untuk data ukuran penuh setiap kali.Kami dapat menunjukkan ini dengan sebuah contoh: ketika langkah-langkah individual TIDAK mengurangi ukuran
data.table
(yaitu kondisi untuk memeriksa adalah sama untuk kedua appraoches):Menggunakan data yang sama tetapi
bench
paket, yang secara otomatis memeriksa apakah hasilnya identik:Seperti yang Anda lihat di sini pendekatan anding adalah 2,43 kali lebih cepat dalam kasus ini . Itu berarti chaining sebenarnya menambah beberapa overhead , menunjukkan bahwa biasanya anding harus lebih cepat. KECUALI jika kondisinya mengurangi ukuran
data.table
langkah demi langkah. Secara teoritis, pendekatan rantai bahkan bisa lebih lambat (bahkan menyisakan overhead), yaitu jika suatu kondisi akan meningkatkan ukuran data. Tetapi praktis saya pikir itu tidak mungkin karena daur ulang dari vektor logis tidak diperbolehkan dalamdata.table
. Saya pikir ini menjawab pertanyaan bonus Anda.Sebagai perbandingan, fungsi asli pada mesin saya dengan
bench
:sumber