Data.table: لماذا يعتبر data.table أسرع مع مجموعة فرعية متجهية من الأعمدة من مجموعة فرعية لأعمدة القائمة

تم إنشاؤها على ٢٧ مارس ٢٠١٩  ·  3تعليقات  ·  مصدر: Rdatatable/data.table

أنا أحب هذه العناصر data.table ، بالتساوي لسرعة تنفيذها وطريقتها البخل في البرمجة النصية.
أنا أستخدمه حتى على الطاولات الصغيرة أيضًا.
أقوم بانتظام بتقسيم الجداول بهذه الطريقة: DT [،. (id1، id5)]
وليس بهذه الطريقة: DT [، c ("id1"، "id5")]

لقد قمت اليوم بقياس سرعة الاثنين وقد اندهشت من اختلاف السرعة على الطاولات الصغيرة. الطريقة البخل بطيئة.

هل هذا الاختلاف شيء مقصود؟

هل هناك طموح في شق طريق التقارب من حيث سرعة التنفيذ إلى الآخر؟
(يتم احتسابها عندما يتعين عليّ تقسيم عدة جداول صغيرة بطريقة متكررة.)

أوبونتو 18.04.2018
R الإصدار 3.5.3 (2019-03-11)
جدول البيانات 1.12.0
ذاكرة عشوائية 32 جيجا بايت
Intel® Core ™ i7-8565U CPU @ 1.80 جيجاهرتز × 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

التعليق الأكثر فائدة

يمكنك إصلاح تنسيق منشورك باستخدام سطر واحد من ثلاث علامات خلفية قبل وبعد مقطع الرمز:

```
code
```

يتم احتسابها عندما يتعين عليّ تقسيم عدة جداول صغيرة بطريقة متكررة.

أعتقد أن اختيار الأعمدة بشكل متكرر من الجداول الصغيرة أمر يجب تجنبه ، وفي معظم الحالات يمكن تجنبه ...؟ نظرًا لأن j in DT[i, j, by] يدعم ويحسن مثل هذه المجموعة المتنوعة من المدخلات ، أعتقد أنه من الطبيعي أن يكون هناك بعض النفقات العامة في تحليلها.


فيما يتعلق بالطرق الأخرى للتعامل مع مشكلتك (وربما يكون هذا مناسبًا بشكل أفضل لـ Stack Overflow إذا كنت تريد التحدث عنها أكثر) ... اعتمادًا على ما تريد فعله بالجدول ، يمكنك فقط حذف العواميد الأخرى ، DT[, setdiff(names(DT), cols) := NULL] واستمر في استخدام DT مباشرة.

إذا كنت لا تزال تفضل أخذ المجموعة الفرعية ، فإن الحصول على مؤشرات الأعمدة يكون أسرع بكثير من أي من الخيارين اللذين فكرت فيهما هنا ، على الرغم من أن التعديلات على النتيجة بهذه الطريقة ستؤثر على الجدول الأصلي:

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

(لقد استبعدت العشوائية من مثالك وقللت # مرة في المعيار لأنني كنت غير صبور.)

لم أجد أبدًا طريقة للاتصال المباشر بالمجموعة الفرعية لقائمة R (والتي يتم استخدامها بعد unclass أعلاه).

بخصوص "التعديلات على النتيجة ستعدل الجدول الأصلي" ، أعني:

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

ال 3 كومينتر

يمكنك إصلاح تنسيق منشورك باستخدام سطر واحد من ثلاث علامات خلفية قبل وبعد مقطع الرمز:

```
code
```

يتم احتسابها عندما يتعين عليّ تقسيم عدة جداول صغيرة بطريقة متكررة.

أعتقد أن اختيار الأعمدة بشكل متكرر من الجداول الصغيرة أمر يجب تجنبه ، وفي معظم الحالات يمكن تجنبه ...؟ نظرًا لأن j in DT[i, j, by] يدعم ويحسن مثل هذه المجموعة المتنوعة من المدخلات ، أعتقد أنه من الطبيعي أن يكون هناك بعض النفقات العامة في تحليلها.


فيما يتعلق بالطرق الأخرى للتعامل مع مشكلتك (وربما يكون هذا مناسبًا بشكل أفضل لـ Stack Overflow إذا كنت تريد التحدث عنها أكثر) ... اعتمادًا على ما تريد فعله بالجدول ، يمكنك فقط حذف العواميد الأخرى ، DT[, setdiff(names(DT), cols) := NULL] واستمر في استخدام DT مباشرة.

إذا كنت لا تزال تفضل أخذ المجموعة الفرعية ، فإن الحصول على مؤشرات الأعمدة يكون أسرع بكثير من أي من الخيارين اللذين فكرت فيهما هنا ، على الرغم من أن التعديلات على النتيجة بهذه الطريقة ستؤثر على الجدول الأصلي:

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

(لقد استبعدت العشوائية من مثالك وقللت # مرة في المعيار لأنني كنت غير صبور.)

لم أجد أبدًا طريقة للاتصال المباشر بالمجموعة الفرعية لقائمة R (والتي يتم استخدامها بعد unclass أعلاه).

بخصوص "التعديلات على النتيجة ستعدل الجدول الأصلي" ، أعني:

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

حسنًا ، لقد تعلمت شيئًا جديدًا وسريعًا (الكرات الغريبة) اليوم وقد لاحظت أن هناك مفاضلة بين السرعة والتشفير البخل. لذا فإن الكوب نصف ممتلئ! شكرا!

أعتقد # 852 ذات الصلة

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات