Saya suka hal-hal data.table ini, merata untuk kecepatan eksekusi dan untuk cara scripting yang hemat.
Saya menggunakannya bahkan di meja kecil juga.
Saya secara teratur membuat subset tabel dengan cara ini: DT[, .(id1, id5)]
dan tidak seperti ini: DT[, c("id1", "id5")]
Hari ini saya mengukur kecepatan keduanya dan saya heran dengan perbedaan kecepatan di meja kecil. Metode pelit jauh lebih lambat.
Apakah perbedaan ini sesuatu yang disengaja?
Apakah ada aspirasi untuk membuat cara pelit untuk konvergen dalam hal kecepatan eksekusi yang lain?
(Ini penting ketika saya harus membuat subset beberapa tabel kecil dengan cara yang berulang.)
Ubuntu 18.04
R versi 3.5.3 (2019-03-11)
data.tabel 1.12.0
RAM 32GB
Intel® Core™ i7-8565U CPU @ 1.80GHz × 8
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT[, .(id1, id5)],
DT[, c("id1", "id5")]
)
Unit: seconds
expr min lq mean median uq max neval
DT[, .(id1, id5)] 1.588367 1.614645 1.929348 1.626847 1.659698 12.33872 100
DT[, c("id1", "id5")] 1.592154 1.613800 1.937548 1.628082 2.184456 11.74581 100
N <- 2e5
DT2 <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT2[, .(id1, id5)],
DT2[, c("id1", "id5")]
)
Unit: microseconds
expr min lq mean median uq max neval
DT2[, .(id1, id5)] 1405.042 1461.561 1525.5314 1491.7885 1527.8955 2220.860 100
DT2[, c("id1", "id5")] 614.624 640.617 666.2426 659.0175 676.9355 906.966 100
Anda dapat memperbaiki format posting Anda dengan menggunakan satu baris dari tiga backticks sebelum dan sesudah potongan kode:
```
code
```
Itu penting ketika saya harus membuat subset beberapa tabel kecil dengan cara yang berulang.
Saya kira berulang kali memilih kolom dari tabel kecil adalah sesuatu yang harus, dan dalam banyak kasus, dapat dihindari ...? Karena j
di DT[i, j, by]
mendukung dan mengoptimalkan berbagai macam input, saya pikir wajar jika ada beberapa overhead dalam menguraikannya.
Mengenai cara lain untuk mendekati masalah Anda (dan mungkin ini akan lebih cocok untuk Stack Overflow jika Anda ingin membicarakannya lebih lanjut) ... Tergantung pada apa lagi yang ingin Anda lakukan dengan tabel, Anda bisa menghapus col lainnya , DT[, setdiff(names(DT), cols) := NULL]
dan terus menggunakan DT secara langsung.
Jika Anda masih lebih suka mengambil subset, mengambil pointer kolom jauh lebih cepat daripada salah satu opsi yang Anda pertimbangkan di sini, meskipun cara mengedit hasil ini akan memengaruhi tabel asli:
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sprintf("id%03d", 1:K), # large groups (char)
id2 = sprintf("id%03d", 1:K), # large groups (char)
id3 = sprintf("id%010d", 1:(N/K)), # small groups (char)
id4 = sample(K), # large groups (int)
id5 = sample(K), # large groups (int)
id6 = sample(N/K), # small groups (int)
v1 = sample(5), # int in range [1,5]
v2 = sample(5), # int in range [1,5]
v3 = round(runif(100, max = 100), 4), # numeric e.g. 23.5749
row = seq_len(N)
)
cols = c("id1", "id5")
microbenchmark(times = 3,
expression = DT[, .(id1, id5)],
index = DT[, c("id1", "id5")],
dotdot = DT[, ..cols],
oddball = setDT(lapply(setNames(cols, cols), function(x) DT[[x]]))[],
oddball2 = setDT(unclass(DT)[cols])[]
)
Unit: microseconds
expr min lq mean median uq max neval
expression 1249753.580 1304355.3415 1417166.9297 1358957.103 1500873.6045 1642790.106 3
index 1184056.302 1191334.4835 1396372.3483 1198612.665 1502530.3715 1806448.078 3
dotdot 1084521.234 1240062.2370 1439680.6980 1395603.240 1617260.4300 1838917.620 3
oddball 92.659 171.8635 568.5317 251.068 806.4680 1361.868 3
oddball2 66.582 125.9505 150.7337 185.319 192.8095 200.300 3
(Saya mengambil pengacakan dari contoh Anda dan mengurangi # kali dalam benchmark karena saya tidak sabar.)
Saya tidak pernah menemukan cara untuk langsung memanggil subset daftar R (yang digunakan setelah unclass
di atas).
Mengenai "pengeditan hasil akan mengubah tabel asli", maksud saya:
myDT = data.table(a = 1:2, b = 3:4)
# standard way
res <- myDT[, "a"]
res[, a := 0]
myDT
# a b
# 1: 1 3
# 2: 2 4
# oddball, grabbing pointers
res2 <- setDT(unclass(myDT)["a"])
res2[, a := 0]
myDT
# a b
# 1: 0 3
# 2: 0 4
Oke, saya telah mempelajari sesuatu yang baru dan cepat (yang eksentrik) hari ini dan saya telah mencatat bahwa ada trade-off antara kecepatan dan pengkodean yang hemat. Jadi gelasnya setengah penuh! Terima kasih!
Saya kira #852 terkait
Komentar yang paling membantu
Anda dapat memperbaiki format posting Anda dengan menggunakan satu baris dari tiga backticks sebelum dan sesudah potongan kode:
Saya kira berulang kali memilih kolom dari tabel kecil adalah sesuatu yang harus, dan dalam banyak kasus, dapat dihindari ...? Karena
j
diDT[i, j, by]
mendukung dan mengoptimalkan berbagai macam input, saya pikir wajar jika ada beberapa overhead dalam menguraikannya.Mengenai cara lain untuk mendekati masalah Anda (dan mungkin ini akan lebih cocok untuk Stack Overflow jika Anda ingin membicarakannya lebih lanjut) ... Tergantung pada apa lagi yang ingin Anda lakukan dengan tabel, Anda bisa menghapus col lainnya ,
DT[, setdiff(names(DT), cols) := NULL]
dan terus menggunakan DT secara langsung.Jika Anda masih lebih suka mengambil subset, mengambil pointer kolom jauh lebih cepat daripada salah satu opsi yang Anda pertimbangkan di sini, meskipun cara mengedit hasil ini akan memengaruhi tabel asli:
(Saya mengambil pengacakan dari contoh Anda dan mengurangi # kali dalam benchmark karena saya tidak sabar.)
Saya tidak pernah menemukan cara untuk langsung memanggil subset daftar R (yang digunakan setelah
unclass
di atas).Mengenai "pengeditan hasil akan mengubah tabel asli", maksud saya: