data.table vs dplyr: คนหนึ่งทำสิ่งที่ดีไม่ได้หรือไม่ดี?


758

ภาพรวม

ฉันค่อนข้างคุ้นเคยกับไม่มากด้วยdata.table dplyrฉันได้อ่านdplyrบทความสั้น ๆและตัวอย่างที่ผุดขึ้นมาบน SO และจนถึงตอนนี้ข้อสรุปของฉันก็คือ:

  1. data.tableและdplyrเทียบเคียงได้กับความเร็วยกเว้นเมื่อมีหลายกลุ่ม (เช่น> 10-100K) และในบางกรณี (ดูมาตรฐานด้านล่าง)
  2. dplyr มีไวยากรณ์ที่เข้าถึงได้มากขึ้น
  3. dplyr บทคัดย่อ (หรือจะ) ปฏิสัมพันธ์ DB ที่มีศักยภาพ
  4. มีความแตกต่างการทำงานเล็กน้อย (ดู "ตัวอย่าง / การใช้งาน" ด้านล่าง)

ในใจของฉัน 2. ไม่ได้รับน้ำหนักมากเพราะฉันค่อนข้างคุ้นเคยกับมันdata.tableแต่ฉันเข้าใจว่าสำหรับผู้ใช้ที่เพิ่งเริ่มใช้ทั้งสองจะเป็นปัจจัยสำคัญ data.tableฉันต้องการที่จะหลีกเลี่ยงการโต้แย้งเกี่ยวกับการที่สามารถใช้งานง่ายมากขึ้นเป็นที่ไม่เกี่ยวข้องสำหรับคำถามที่เฉพาะเจาะจงของฉันถามจากมุมมองของคนที่คุ้นเคยกับ ฉันยังต้องการหลีกเลี่ยงการอภิปรายเกี่ยวกับวิธีการที่ "ใช้งานง่ายมากขึ้น" นำไปสู่การวิเคราะห์ที่รวดเร็วขึ้น (แน่นอนจริง แต่อีกครั้งไม่ใช่สิ่งที่ฉันสนใจมากที่สุดที่นี่)

คำถาม

สิ่งที่ฉันอยากรู้คือ:

  1. มีงานการวิเคราะห์ที่ง่ายกว่าในการเขียนโค้ดด้วยแพ็คเกจหนึ่งหรืออีกแพคเกจสำหรับผู้ที่คุ้นเคยกับแพ็คเกจ (เช่นการกดแป้นบางอย่างร่วมกับการใช้ระดับ esotericism ที่ต้องการ
  2. มีงานวิเคราะห์ที่ดำเนินการอย่างมีนัยสำคัญ (เช่นมากกว่า 2x) มีประสิทธิภาพมากกว่าในแพ็คเกจหนึ่งเทียบกับแพ็คเกจอื่น

หนึ่งคำถามดังนั้นเมื่อเร็ว ๆ นี้มีฉันคิดเกี่ยวกับเรื่องนี้มากขึ้นอีกนิดเพราะจนถึงจุดที่ผมไม่คิดว่าจะมีมากเกินกว่าสิ่งที่ฉันสามารถทำได้ในdplyr data.tableนี่คือdplyrวิธีแก้ปัญหา (ข้อมูลเมื่อสิ้นสุด Q):

dat %.%
  group_by(name, job) %.%
  filter(job != "Boss" | year == min(year)) %.%
  mutate(cumu_job2 = cumsum(job2))

ซึ่งดีกว่าความพยายามแฮ็คของฉันในการdata.tableแก้ปัญหา ที่กล่าวว่าdata.tableวิธีแก้ปัญหาที่ดีก็ค่อนข้างดี (ขอบคุณ Jean-Robert, อรุณและทราบที่นี่ฉันชอบคำสั่งเดียวมากกว่าทางออกที่ดีที่สุดอย่างเคร่งครัด):

setDT(dat)[,
  .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], 
  by=list(id, job)
]

ไวยากรณ์สำหรับหลังอาจดูลึกลับมาก แต่จริง ๆ แล้วมันค่อนข้างตรงไปตรงมาถ้าคุณคุ้นเคยdata.table(กล่าวคือไม่ได้ใช้กลอุบายลึกลับเพิ่มเติม)

นึกคิดสิ่งที่ฉันต้องการจะดูเป็นตัวอย่างที่ดีบางอย่างที่เป็นdplyrหรือdata.tableวิธีที่มีความรัดกุมมากขึ้นหรือทำงานได้ดีขึ้นอย่างมีนัยสำคัญ

ตัวอย่าง

การใช้
  • dplyrไม่อนุญาตการดำเนินการจัดกลุ่มที่ส่งคืนจำนวนแถวโดยพลการ (จากคำถามของ eddiหมายเหตุ: สิ่งนี้ดูเหมือนว่าจะถูกนำมาใช้ในdplyr 0.5เช่นกัน @beginneR แสดงการทำงานที่อาจเกิดขึ้นโดยใช้doคำตอบของคำถาม @ eddi)
  • data.tableรองรับการเข้าร่วมกลิ้ง (ขอบคุณ @dholstius) เช่นเดียวกับการรวมที่ทับซ้อนกัน
  • data.tableเพิ่มประสิทธิภาพการแสดงออกของแบบฟอร์มภายในDT[col == value]หรือDT[col %in% values]เพื่อความเร็วผ่านการจัดทำดัชนีอัตโนมัติซึ่งใช้การค้นหาแบบไบนารีในขณะที่ใช้ไวยากรณ์ R พื้นฐานเดียวกัน ดูที่นี่สำหรับรายละเอียดเพิ่มเติมและมาตรฐานเล็ก ๆ
  • dplyrข้อเสนอมาตรฐานรุ่นการประเมินผลของฟังก์ชั่น (เช่นregroup, summarize_each_) ที่สามารถลดความซับซ้อนของการใช้งานที่ใช้โปรแกรมdplyr(ใช้การเขียนโปรแกรมจดบันทึกdata.tableเป็นไปได้แน่นอนเพียงแค่ต้องมีความคิดอย่างรอบคอบเปลี่ยนตัว / quoting ฯลฯ อย่างน้อยความรู้ของฉัน)
มาตรฐาน

ข้อมูล

นี่เป็นตัวอย่างแรกที่ฉันแสดงในส่วนคำถาม

dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane", 
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob", 
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L, 
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L, 
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager", 
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager", 
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L, 
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id", 
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA, 
-16L))

9
วิธีแก้ปัญหาที่คล้ายกันในการอ่านหนังสือเล่มdplyrหนึ่งคือ:as.data.table(dat)[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by = list(name, job)]
eddi

7
สำหรับ # 1 ทั้งสองdplyrและdata.tableทีมกำลังทำงานกับมาตรฐานดังนั้นคำตอบจะอยู่ที่จุดหนึ่ง # 2 (ไวยากรณ์) imO เป็นเท็จอย่างเคร่งครัด แต่นั่นแสดงให้เห็นอย่างชัดเจนในอาณาเขตความคิดเห็นดังนั้นฉันจึงลงคะแนนให้ปิดเช่นกัน
eddi

13
กันอีกครั้ง IMO ชุดของปัญหาที่จะแสดงมากขึ้นหมดจดใน(d)plyrมีมาตรการ 0
Eddi

28
@BrodieG สิ่งหนึ่งที่จริง ๆ แล้วบั๊กฉันเกี่ยวกับทั้งสองdplyrและplyrเกี่ยวกับไวยากรณ์และเป็นเหตุผลหลักที่ทำไมฉันไม่ชอบไวยากรณ์ของพวกเขาคือฉันต้องเรียนรู้วิธีมากเกินไป (อ่านมากกว่า 1) ฟังก์ชั่นพิเศษ (ชื่อที่ยังคงไม่สมเหตุสมผลสำหรับฉัน) จำสิ่งที่พวกเขาทำข้อโต้แย้งที่พวกเขารับ ฯลฯ ซึ่งเป็นจุดเปลี่ยนครั้งใหญ่สำหรับฉันจากปรัชญาชั้นลึก
eddi

43
@eddi [tongue-in-cheek] สิ่งหนึ่งที่ทำให้ฉันเกี่ยวกับไวยากรณ์ data.table จริงๆคือฉันต้องเรียนรู้ว่าการโต้เถียงฟังก์ชั่นของฟังก์ชันมีมากเกินไปมาก.SDน้อยเพียงใด [จริงจัง] ฉันคิดว่าสิ่งเหล่านี้เป็นความแตกต่างในการออกแบบที่ถูกกฎหมายที่จะดึงดูดผู้คนที่แตกต่างกัน
hadley

คำตอบ:


532

เราจำเป็นต้องปกอย่างน้อยประเด็นเหล่านี้จะให้คำตอบที่ครอบคลุม / การเปรียบเทียบ (ในลำดับใดไม่มีความสำคัญ): Speed, Memory usage, และSyntaxFeatures

ความตั้งใจของฉันคือการครอบคลุมสิ่งเหล่านี้อย่างชัดเจนที่สุดเท่าที่จะทำได้จาก data.table

หมายเหตุ: นอกจากจะกล่าวถึงอย่างชัดเจนโดยอ้างอิงถึง dplyr เราหมายถึงส่วนต่อประสาน data.frame ของ dplyr ซึ่งมี internals อยู่ใน C ++ โดยใช้ Rcpp


ไวยากรณ์ data.table มีความสอดคล้องในรูปแบบของ DT[i, j, by]- เพื่อให้i, jและbyร่วมกันจากการออกแบบ ด้วยการทำให้การดำเนินงานที่เกี่ยวข้องเข้าด้วยกันจะช่วยให้การดำเนินการปรับความเร็วและการใช้หน่วยความจำมีความสำคัญยิ่งขึ้นได้อย่างง่ายดายและยังมีฟีเจอร์ที่ทรงพลังทั้งหมดในขณะที่ยังคงความสอดคล้องในไวยากรณ์

1. ความเร็ว

ค่อนข้างไม่กี่มาตรฐาน ( แต่ส่วนใหญ่ในการจัดกลุ่มการดำเนินงาน) ได้รับการเพิ่มคำถามแล้วแสดง data.table ได้รับเร็วขึ้นกว่า dplyr เป็นจำนวนของกลุ่มและ / หรือแถวไปยังกลุ่มจากการเพิ่มขึ้นรวมทั้งมาตรฐานโดยแมตต์ในการจัดกลุ่มจาก10 ล้านไป 2 พันล้านแถว (100GB ใน RAM) ใน100-10000000 กลุ่มpandasและคอลัมน์ที่แตกต่างกันการจัดกลุ่มซึ่งยังเปรียบเทียบ ดูมาตรฐานที่อัปเดตซึ่งรวมถึงSparkและpydatatableเช่นกัน

ในการวัดประสิทธิภาพจะเป็นการดีที่จะครอบคลุมประเด็นที่เหลือเหล่านี้เช่นกัน:

  • การจัดกลุ่มการดำเนินการที่เกี่ยวข้องกับชุดย่อยของแถว - เช่นDT[x > val, sum(y), by = z]การดำเนินการพิมพ์

  • เกณฑ์มาตรฐานการดำเนินการอื่น ๆ เช่นการปรับปรุงและร่วม

  • เกณฑ์มาตรฐานหน่วยความจำเกณฑ์มาตรฐานสำหรับแต่ละการดำเนินการนอกเหนือจากการใช้งานจริง

2. การใช้หน่วยความจำ

  1. การดำเนินการที่เกี่ยวข้องfilter()หรือslice()ใน dplyr อาจไม่มีประสิทธิภาพของหน่วยความจำ (ทั้ง data.frames และ data.tables) ดูโพสต์นี้

    โปรดทราบว่านายอำเภอแสดงความคิดเห็นพูดคุยเกี่ยวกับความเร็ว (dplyr ที่เป็นไปอย่างรวดเร็วอุดมสมบูรณ์สำหรับเขา) ในขณะที่ความกังวลที่สำคัญที่นี่เป็นหน่วยความจำ

  2. ขณะนี้อินเทอร์เฟซ data.table อนุญาตให้หนึ่งแก้ไข / อัปเดตคอลัมน์โดยอ้างอิง (โปรดทราบว่าเราไม่จำเป็นต้องกำหนดผลลัพธ์กลับไปที่ตัวแปรอีกครั้ง)

    # sub-assign by reference, updates 'y' in-place
    DT[x >= 1L, y := NA]

    แต่ dplyr จะไม่อัปเดตตามการอ้างอิง ค่าเทียบเท่า dplyr จะเป็น (โปรดทราบว่าผลลัพธ์จะต้องได้รับมอบหมายอีกครั้ง):

    # copies the entire 'y' column
    ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))

    ความกังวลสำหรับเรื่องนี้คือความโปร่งใสอ้างอิง การอัพเดตวัตถุ data.table โดยการอ้างอิงโดยเฉพาะอย่างยิ่งภายในฟังก์ชั่นอาจไม่เป็นที่ต้องการเสมอไป แต่นี่เป็นคุณสมบัติที่มีประโยชน์อย่างไม่น่าเชื่อ: ดูนี่และโพสต์นี้สำหรับกรณีที่น่าสนใจ และเราต้องการเก็บไว้

    ดังนั้นเรากำลังทำงานเพื่อส่งออกshallow()ฟังก์ชั่นใน data.table ที่จะช่วยให้ผู้ใช้มีความเป็นไปได้ทั้งสองอย่าง ตัวอย่างเช่นถ้าเป็นที่พึงประสงค์ที่จะไม่แก้ไข data input table ภายในฟังก์ชั่นหนึ่งสามารถทำได้:

    foo <- function(DT) {
        DT = shallow(DT)          ## shallow copy DT
        DT[, newcol := 1L]        ## does not affect the original DT 
        DT[x > 2L, newcol := 2L]  ## no need to copy (internally), as this column exists only in shallow copied DT
        DT[x > 2L, x := 3L]       ## have to copy (like base R / dplyr does always); otherwise original DT will 
                                  ## also get modified.
    }

    หากไม่ใช้shallow()งานฟังก์ชันการใช้งานเดิมจะยังคงอยู่:

    bar <- function(DT) {
        DT[, newcol := 1L]        ## old behaviour, original DT gets updated by reference
        DT[x > 2L, x := 3L]       ## old behaviour, update column x in original DT.
    }

    โดยการสร้างสำเนาตื้นโดยใช้shallow()เราเข้าใจว่าคุณไม่ต้องการแก้ไขวัตถุต้นฉบับ เราดูแลทุกอย่างภายในเพื่อให้มั่นใจว่าในขณะเดียวกันก็มั่นใจในการคัดลอกคอลัมน์ที่คุณแก้ไขเมื่อจำเป็นเท่านั้น เมื่อดำเนินการสิ่งนี้ควรแก้ไขปัญหาความโปร่งใสในการอ้างอิงโดยรวมในขณะเดียวกันก็ให้ความเป็นไปได้กับผู้ใช้ทั้งสอง

    นอกจากนี้เมื่อshallow()ส่งออกอินเตอร์เฟซ data.table ของ dplyr ควรหลีกเลี่ยงสำเนาเกือบทั้งหมด ดังนั้นผู้ที่ชอบไวยากรณ์ของ dplyr สามารถใช้กับ data.tables

    แต่มันจะยังขาดคุณสมบัติหลายอย่างที่ data.table จัดเตรียมรวมถึง (ย่อย) - การมอบหมายโดยการอ้างอิง

  3. รวมขณะเข้าร่วม:

    สมมติว่าคุณมี data.tables สองรายการดังนี้:

    DT1 = data.table(x=c(1,1,1,1,2,2,2,2), y=c("a", "a", "b", "b"), z=1:8, key=c("x", "y"))
    #    x y z
    # 1: 1 a 1
    # 2: 1 a 2
    # 3: 1 b 3
    # 4: 1 b 4
    # 5: 2 a 5
    # 6: 2 a 6
    # 7: 2 b 7
    # 8: 2 b 8
    DT2 = data.table(x=1:2, y=c("a", "b"), mul=4:3, key=c("x", "y"))
    #    x y mul
    # 1: 1 a   4
    # 2: 2 b   3

    และคุณต้องการที่จะได้รับsum(z) * mulในแต่ละแถวในขณะที่การเข้าร่วมโดยคอลัมน์DT2 x,yเราสามารถ:

    • 1) ผลรวมที่DT1จะได้รับsum(z)2) ทำการเข้าร่วมและ 3) คูณ (หรือ)

      # data.table way
      DT1[, .(z = sum(z)), keyby = .(x,y)][DT2][, z := z*mul][]
      
      # dplyr equivalent
      DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% 
          right_join(DF2) %>% mutate(z = z * mul)
    • 2) ทำได้ทุกอย่างในครั้งเดียว (โดยใช้by = .EACHIคุณสมบัติ):

      DT1[DT2, list(z=sum(z) * mul), by = .EACHI]

    ข้อดีคืออะไร

    • เราไม่จำเป็นต้องจัดสรรหน่วยความจำสำหรับผลลัพธ์ระดับกลาง

    • เราไม่จำเป็นต้องจัดกลุ่ม / แฮชสองครั้ง (อย่างใดอย่างหนึ่งสำหรับการรวมและอื่น ๆ สำหรับการเข้าร่วม)

    • และที่สำคัญการดำเนินการที่เราต้องการจะชัดเจนโดยดูjใน (2)

    ตรวจสอบการโพสต์นี้by = .EACHIสำหรับคำอธิบายรายละเอียดของ ไม่มีผลลัพธ์ระหว่างกลางที่เป็นรูปธรรมและรวมเข้าร่วม + จะดำเนินการทั้งหมดในครั้งเดียว

    มีลักษณะที่นี้ , นี้และนี้โพสต์สำหรับสถานการณ์การใช้งานจริง

    ในdplyrคุณจะต้องเข้าร่วมและรวมหรือรวมก่อนแล้วจึงเข้าร่วมซึ่งไม่มีประสิทธิภาพเท่าที่ควรในแง่ของหน่วยความจำ (ซึ่งแปลว่าเป็นความเร็ว)

  4. อัปเดตและเข้าร่วม:

    พิจารณารหัส data.table ที่แสดงด้านล่าง:

    DT1[DT2, col := i.mul]

    เพิ่ม / อัปเดตDT1's คอลัมน์colด้วยmulจากDT2แถวเหล่านั้นที่DT2' s DT1ตรงกับคอลัมน์คีย์ ฉันไม่คิดว่าจะมีการเทียบเท่าในการดำเนินการนี้แน่นอนdplyrเช่นโดยไม่ต้องหลีกเลี่ยงการ*_joinดำเนินการซึ่งจะต้องคัดลอกทั้งหมดDT1เพียงเพื่อเพิ่มคอลัมน์ใหม่ลงไปซึ่งไม่จำเป็น

    ตรวจสอบโพสต์นี้สำหรับสถานการณ์การใช้งานจริง

เพื่อสรุปเป็นสิ่งสำคัญที่จะต้องตระหนักว่าการเพิ่มประสิทธิภาพทุกบิตมีความสำคัญ ในฐานะที่เป็นGrace Hopperจะกล่าวว่าใจของคุณ nanoseconds !

3. ไวยากรณ์

ตอนนี้ขอดูไวยากรณ์ นายอำเภอแสดงความคิดเห็นที่นี่ :

ตารางข้อมูลมีความรวดเร็วมาก แต่ฉันคิดว่าความกระชับของมันทำให้เรียนรู้ได้ยากและรหัสที่ใช้อ่านได้ยากกว่าหลังจากที่คุณเขียน ...

ฉันพบว่าคำพูดนี้ไม่มีประโยชน์เพราะมันเป็นความคิดเห็นส่วนตัว สิ่งที่เราอาจจะสามารถพยายามคือเพื่อความคมชัดสม่ำเสมอในไวยากรณ์ เราจะเปรียบเทียบ data.table และ dplyr ไวยากรณ์เคียงข้างกัน

เราจะทำงานกับข้อมูลจำลองที่แสดงด้านล่าง:

DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5))
DF = as.data.frame(DT)
  1. การดำเนินการรวม / อัพเดทขั้นพื้นฐาน

    # case (a)
    DT[, sum(y), by = z]                       ## data.table syntax
    DF %>% group_by(z) %>% summarise(sum(y)) ## dplyr syntax
    DT[, y := cumsum(y), by = z]
    ans <- DF %>% group_by(z) %>% mutate(y = cumsum(y))
    
    # case (b)
    DT[x > 2, sum(y), by = z]
    DF %>% filter(x>2) %>% group_by(z) %>% summarise(sum(y))
    DT[x > 2, y := cumsum(y), by = z]
    ans <- DF %>% group_by(z) %>% mutate(y = replace(y, which(x > 2), cumsum(y)))
    
    # case (c)
    DT[, if(any(x > 5L)) y[1L]-y[2L] else y[2L], by = z]
    DF %>% group_by(z) %>% summarise(if (any(x > 5L)) y[1L] - y[2L] else y[2L])
    DT[, if(any(x > 5L)) y[1L] - y[2L], by = z]
    DF %>% group_by(z) %>% filter(any(x > 5L)) %>% summarise(y[1L] - y[2L])
    • ไวยากรณ์ data.table มีขนาดกะทัดรัดและค่อนข้างละเอียด สิ่งต่าง ๆ จะเทียบเท่ากันมากหรือน้อยในกรณี (ก)

    • ในกรณี (ข) เรามีการใช้filter()ใน dplyr ขณะที่สรุป แต่ในขณะที่การปรับปรุงmutate()เราต้องย้ายภายในตรรกะ ใน data.table เราแสดงการดำเนินการทั้งสองด้วยตรรกะเดียวกัน - ทำงานบนแถวที่x > 2แต่ในกรณีแรกรับsum(y)ในขณะที่ในกรณีที่สองอัพเดตแถวเหล่านั้นyด้วยผลรวมสะสม

      นี่คือสิ่งที่เราหมายถึงเมื่อเราบอกว่าDT[i, j, by]รูปแบบที่มีความสอดคล้อง

    • ในทำนองเดียวกันในกรณี (c) เมื่อเรามีif-elseเงื่อนไขเราสามารถแสดงตรรกะ"ตามที่เป็น"ในทั้ง data.table และ dplyr อย่างไรก็ตามหากเราต้องการส่งคืนเฉพาะแถวเหล่านั้นที่มีifเงื่อนไขตรงตามและข้ามเป็นอย่างอื่นเราจะไม่สามารถใช้งานsummarise()ได้โดยตรง (AFAICT) เราต้องfilter()ก่อนแล้วจึงสรุปเพราะsummarise()คาดว่าจะมีค่าเดียวเสมอ

      ในขณะที่มันส่งกลับผลลัพธ์เดียวกันการใช้filter()ที่นี่ทำให้การดำเนินการจริงชัดเจนน้อยลง

      มันอาจจะเป็นไปได้ที่จะใช้filter()ในกรณีแรกเช่นกัน (ดูไม่ชัดเจนสำหรับฉัน) แต่ประเด็นของฉันคือเราไม่ควรต้องทำ

  2. การรวม / อัพเดตในหลายคอลัมน์

    # case (a)
    DT[, lapply(.SD, sum), by = z]                     ## data.table syntax
    DF %>% group_by(z) %>% summarise_each(funs(sum)) ## dplyr syntax
    DT[, (cols) := lapply(.SD, sum), by = z]
    ans <- DF %>% group_by(z) %>% mutate_each(funs(sum))
    
    # case (b)
    DT[, c(lapply(.SD, sum), lapply(.SD, mean)), by = z]
    DF %>% group_by(z) %>% summarise_each(funs(sum, mean))
    
    # case (c)
    DT[, c(.N, lapply(.SD, sum)), by = z]     
    DF %>% group_by(z) %>% summarise_each(funs(n(), mean))
    • ในกรณี (a) รหัสมีความเทียบเท่ามากหรือน้อย data.table ใช้ฟังก์ชั่นฐานที่คุ้นเคยlapply()ในขณะที่dplyrแนะนำ*_each()พร้อมกับฟังก์ชันfuns()มากมาย

    • data.table :=ต้องการชื่อคอลัมน์ที่จะให้ในขณะที่ dplyr สร้างขึ้นโดยอัตโนมัติ

    • ในกรณี (b) ไวยากรณ์ของ dplyr นั้นค่อนข้างตรงไปตรงมา การปรับปรุงการรวม / อัปเดตสำหรับฟังก์ชั่นหลายอย่างอยู่ในรายการ data.table

    • ในกรณี (c) แม้ว่า dplyr จะกลับมาn()หลายครั้งหลาย ๆ คอลัมน์แทนที่จะเป็นเพียงครั้งเดียว ใน data.table jทุกความจำเป็นที่เราจะทำคือการกลับรายการใน องค์ประกอบของรายการแต่ละรายการจะกลายเป็นคอลัมน์ในผลลัพธ์ ดังนั้นเราจึงสามารถใช้อีกครั้งหนึ่งฟังก์ชั่นฐานคุ้นเคยc()เพื่อ concatenate .Nไปซึ่งผลตอบแทนlistlist

    หมายเหตุ: อีกครั้งใน data.table jทุกความจำเป็นที่เราจะทำคือการกลับรายการใน องค์ประกอบของรายการแต่ละรายการจะกลายเป็นคอลัมน์ผลลัพธ์ คุณสามารถใช้c(), as.list(), lapply(), list()ฟังก์ชั่น ฯลฯ ... ฐานที่จะบรรลุเป้าหมายนี้ได้โดยไม่ต้องเรียนรู้ฟังก์ชั่นใหม่ ๆ

    คุณจะต้องเรียนรู้เฉพาะตัวแปรพิเศษ - .Nและ.SDอย่างน้อย ค่าเทียบเท่าใน dplyr คือn()และ.

  3. ร่วม

    dplyr ให้ฟังก์ชั่นแยกต่างหากสำหรับแต่ละประเภทของการเข้าร่วมที่เป็น data.table อนุญาตให้ร่วมใช้ไวยากรณ์เดียวกันDT[i, j, by](และมีเหตุผล) นอกจากนี้ยังมีmerge.data.table()ฟังก์ชั่นที่เทียบเท่าเป็นทางเลือก

    setkey(DT1, x, y)
    
    # 1. normal join
    DT1[DT2]            ## data.table syntax
    left_join(DT2, DT1) ## dplyr syntax
    
    # 2. select columns while join    
    DT1[DT2, .(z, i.mul)]
    left_join(select(DT2, x, y, mul), select(DT1, x, y, z))
    
    # 3. aggregate while join
    DT1[DT2, .(sum(z) * i.mul), by = .EACHI]
    DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% 
        inner_join(DF2) %>% mutate(z = z*mul) %>% select(-mul)
    
    # 4. update while join
    DT1[DT2, z := cumsum(z) * i.mul, by = .EACHI]
    ??
    
    # 5. rolling join
    DT1[DT2, roll = -Inf]
    ??
    
    # 6. other arguments to control output
    DT1[DT2, mult = "first"]
    ??
    • บางคนอาจพบว่าฟังก์ชั่นที่แยกจากกันสำหรับแต่ละคนเข้าร่วม nicer มาก (ซ้าย, ขวา, ภายใน, ต่อต้าน, กึ่ง ฯลฯ ) ในขณะที่คนอื่นอาจชอบ data.table ของDT[i, j, by]หรือmerge()ที่คล้ายกับฐานอาร์

    • อย่างไรก็ตามการเข้าร่วม dplyr ทำเช่นนั้น ไม่มีอะไรเพิ่มเติม ไม่มีอะไรน้อย

    • data.tables สามารถเลือกคอลัมน์ในขณะที่เข้าร่วม (2) และใน dplyr คุณจะต้องselect()ก่อนทั้ง data.frames ก่อนที่จะเข้าร่วมตามที่แสดงข้างต้น มิฉะนั้นคุณจะต้องใช้การรวมเข้ากับคอลัมน์ที่ไม่จำเป็นเท่านั้นเพื่อลบออกในภายหลังและไม่มีประสิทธิภาพ

    • data.tables สามารถรวมในขณะที่เข้าร่วม (3) และยังปรับปรุงในขณะที่เข้าร่วม (4) โดยใช้by = .EACHIคุณสมบัติ เหตุใดผลการเข้าร่วมทั้งหมดจึงเพิ่ม / อัปเดตคอลัมน์เพียงไม่กี่รายการ

    • data.table มีความสามารถในการรีดร่วม (5) - ม้วนไปข้างหน้า LOCF , ม้วนย้อนหลัง NOCB , ที่อยู่ใกล้ที่สุด

    • data.table นอกจากนี้ยังมีmult =ข้อโต้แย้งที่เลือกแรก , สุดท้ายหรือทุกแมตช์ (6)

    • data.table มีallow.cartesian = TRUEอาร์กิวเมนต์เพื่อป้องกันการรวมที่ไม่ถูกต้องโดยไม่ตั้งใจ

อีกครั้งไวยากรณ์สอดคล้องกับDT[i, j, by]ข้อโต้แย้งเพิ่มเติมที่ช่วยให้การควบคุมการส่งออกเพิ่มเติม

  1. do()...

    ข้อมูลสรุปของ dplyr ได้รับการออกแบบมาเป็นพิเศษสำหรับฟังก์ชั่นที่ส่งกลับค่าเดียว หากการทำงานของคุณจะส่งกลับค่าหลายค่า / do()ไม่เท่ากันคุณจะต้องรีสอร์ทเพื่อ คุณต้องทราบล่วงหน้าเกี่ยวกับฟังก์ชั่นทั้งหมดของคุณคืนค่า

    DT[, list(x[1], y[1]), by = z]                 ## data.table syntax
    DF %>% group_by(z) %>% summarise(x[1], y[1]) ## dplyr syntax
    DT[, list(x[1:2], y[1]), by = z]
    DF %>% group_by(z) %>% do(data.frame(.$x[1:2], .$y[1]))
    
    DT[, quantile(x, 0.25), by = z]
    DF %>% group_by(z) %>% summarise(quantile(x, 0.25))
    DT[, quantile(x, c(0.25, 0.75)), by = z]
    DF %>% group_by(z) %>% do(data.frame(quantile(.$x, c(0.25, 0.75))))
    
    DT[, as.list(summary(x)), by = z]
    DF %>% group_by(z) %>% do(data.frame(as.list(summary(.$x))))
    • .SDเทียบเท่าคือ .

    • ใน data.table คุณสามารถใส่อะไรเข้าไปมากj- สิ่งเดียวที่ต้องจำคือให้คืนค่ารายการเพื่อให้แต่ละองค์ประกอบของรายการถูกแปลงเป็นคอลัมน์

    • ใน dplyr ไม่สามารถทำเช่นนั้นได้ ต้องหันไปใช้do()โดยขึ้นอยู่กับว่าคุณแน่ใจหรือไม่ว่าฟังก์ชั่นของคุณจะคืนค่าเดียวหรือไม่ และมันค่อนข้างช้า

อีกครั้งหนึ่งที่ไวยากรณ์ data.table DT[i, j, by]คือสอดคล้องกับ เราสามารถแสดงความคิดเห็นjต่อไปได้โดยไม่ต้องกังวลเกี่ยวกับสิ่งเหล่านี้

มีลักษณะที่คำถาม SO นี้และหนึ่งในนี้ ฉันสงสัยว่ามันจะเป็นไปได้ที่จะแสดงคำตอบที่ตรงไปตรงมาโดยใช้ไวยากรณ์ของ dplyr ...

เพื่อสรุปฉันได้เน้นหลายกรณีที่ไวยากรณ์ของ dplyr นั้นไม่มีประสิทธิภาพ จำกัด หรือล้มเหลวในการดำเนินการตรงไปตรงมา โดยเฉพาะอย่างยิ่งเนื่องจาก data.table ได้รับแบคแลชค่อนข้างน้อยเกี่ยวกับไวยากรณ์ "ยากต่อการอ่าน / เรียนรู้" (เช่นเดียวกับที่วาง / ลิงก์ด้านบน) โพสต์ส่วนใหญ่ที่พูดคุยเกี่ยวกับการปฏิบัติงานที่ตรงไปตรงมามากที่สุด และนั่นก็ยอดเยี่ยม แต่สิ่งสำคัญคือต้องตระหนักถึงไวยากรณ์และข้อ จำกัด ของคุณสมบัติเช่นกันและฉันยังไม่เห็นโพสต์

data.table มีนิสัยแปลก ๆ เช่นกัน (ซึ่งบางคนก็ชี้ให้เห็นว่าเรากำลังพยายามแก้ไข) นอกจากนี้เรายังพยายามที่จะปรับปรุงของ data.table ร่วมในฐานะที่ผมได้เน้นที่นี่

แต่หนึ่งควรพิจารณาจำนวนของคุณสมบัติที่ขาดการเปรียบเทียบกับ data.table

4. คุณสมบัติ

ฉันได้ชี้ให้เห็นคุณสมบัติส่วนใหญ่ที่นี่และในโพสต์นี้ นอกจากนี้:

  • fread - โปรแกรมอ่านไฟล์อย่างรวดเร็วนั้นมีมานานแล้ว

  • fwrite - ตัวเขียนไฟล์ด่วนแบบขนานพร้อมใช้งานแล้วในขณะนี้ ดูโพสต์นี้สำหรับคำอธิบายโดยละเอียดเกี่ยวกับการใช้งานและ# 1664เพื่อติดตามการพัฒนาเพิ่มเติม

  • การจัดทำดัชนีอัตโนมัติ - อีกคุณสมบัติที่มีประโยชน์เพื่อปรับพื้นฐานไวยากรณ์ R ตามที่เป็นอยู่ภายใน

  • การจัดกลุ่มแบบเฉพาะกิจ : dplyrเรียงลำดับผลลัพธ์โดยอัตโนมัติด้วยการจัดกลุ่มตัวแปรระหว่างsummarise()ซึ่งอาจไม่เป็นที่ต้องการเสมอไป

  • ข้อได้เปรียบมากมายใน data.table joins (สำหรับความเร็ว / ประสิทธิภาพของหน่วยความจำและไวยากรณ์) ที่กล่าวถึงข้างต้น

  • Non-equi joins : อนุญาตให้ joins ใช้ตัวดำเนินการอื่น<=, <, >, >=พร้อมกับข้อดีอื่น ๆ ของ data.table joins

  • การรวมช่วงที่ทับซ้อนกันถูกนำมาใช้ใน data.table เมื่อเร็ว ๆ นี้ ตรวจสอบโพสต์นี้เพื่อดูภาพรวมพร้อมการวัดประสิทธิภาพ

  • setorder() ฟังก์ชั่นใน data.table ที่ช่วยให้เรียงลำดับใหม่ของ data.tables ได้อย่างรวดเร็วโดยการอ้างอิง

  • dplyr จัดเตรียมอินเตอร์เฟสไปยังฐานข้อมูลโดยใช้ไวยากรณ์เดียวกันซึ่ง data.table ไม่ได้อยู่ในขณะนี้

  • data.tableให้เทียบเท่าที่เร็วขึ้นของการดำเนินงานชุด (เขียนโดยแจ Gorecki) - fsetdiff, fintersect, funionและfsetequalมีเพิ่มเติมallอาร์กิวเมนต์ (เช่นใน SQL)

  • data.table โหลดได้อย่างหมดจดโดยไม่มีการปิดบังคำเตือนและมีกลไกอธิบายไว้ที่นี่เพื่อ[.data.frameความเข้ากันได้เมื่อส่งผ่านไปยังแพ็คเกจ R ใด ๆ dplyr เปลี่ยนแปลงฟังก์ชั่นฐานfilter, lagและ[ซึ่งอาจทำให้เกิดปัญหา เช่นที่นี่และที่นี่


สุดท้าย:

  • บนฐานข้อมูล - ไม่มีเหตุผลใดที่ data.table ไม่สามารถให้อินเทอร์เฟซที่คล้ายกันได้ แต่ตอนนี้ไม่ใช่ลำดับความสำคัญ มันอาจจะถูกกระแทกหากผู้ใช้ต้องการคุณลักษณะนั้นมาก .. ไม่แน่ใจ

  • ในการขนาน - ทุกอย่างเป็นเรื่องยากจนกระทั่งมีคนไปข้างหน้าและทำมัน แน่นอนว่ามันต้องใช้ความพยายาม (เป็นหัวข้อที่ปลอดภัย)

    • ความคืบหน้าจะถูกทำในปัจจุบัน (ใน v1.9.7 devel) ต่อ parallelising OpenMPเวลาที่รู้จักกันชิ้นส่วนสำหรับการเพิ่มประสิทธิภาพที่เพิ่มขึ้นการบริโภคการใช้

9
@bluefeet: ฉันไม่คิดว่าคุณจะให้บริการที่ยอดเยี่ยมกับเราโดยการย้ายการสนทนานั้นไปสู่การแชท ฉันรู้สึกว่าอรุณเป็นหนึ่งในนักพัฒนาและสิ่งนี้อาจส่งผลให้เกิดความเข้าใจที่เป็นประโยชน์
IRTFM

2
เมื่อฉันไปแชทโดยใช้ลิงก์ของคุณปรากฏว่าเนื้อหาทั้งหมดตามความคิดเห็นเริ่มต้น "คุณควรใช้ตัวกรอง" .. หายไป ฉันขาดอะไรบางอย่างเกี่ยวกับกลไกการแชท SO
IRTFM

6
ฉันคิดว่าทุก ๆ ที่ที่คุณใช้งานที่ได้รับมอบหมายโดยการอ้างอิง ( :=) คุณdplyrควรใช้สิ่งที่<-เทียบเท่าDF <- DF %>% mutate...แทนเช่นกันDF %>% mutate...
David Arenburg

4
เกี่ยวกับไวยากรณ์ ฉันเชื่อว่าdplyrสามารถทำได้ง่ายกว่าสำหรับผู้ใช้ที่เคยใช้plyrไวยากรณ์ แต่data.tableสามารถทำได้ง่ายขึ้นสำหรับผู้ใช้ที่ใช้แบบสอบถามไวยากรณ์ภาษาเช่นSQLและพีชคณิตเชิงสัมพันธ์ที่อยู่เบื้องหลังซึ่งเป็นเรื่องเกี่ยวกับการแปลงข้อมูลแบบตาราง @Arun คุณควรทราบว่าตัวดำเนินการตั้งค่านั้นง่ายมากที่สามารถทำได้โดยการห่อdata.tableฟังก์ชั่นและแน่นอนว่าการเร่งความเร็วขึ้นอย่างมาก
jangorecki

9
ฉันอ่านโพสต์นี้หลายครั้งแล้วและมันช่วยฉันในการทำความเข้าใจ data.table และสามารถใช้มันได้ดีขึ้น โดยส่วนใหญ่แล้วผมชอบ data.table มากกว่า dplyr หรือ pandas หรือ PL / pgSQL อย่างไรก็ตามฉันไม่สามารถหยุดคิดได้ว่าจะแสดงออกอย่างไร ไวยากรณ์ไม่ใช่เรื่องง่ายชัดเจนหรือ verbose ในความเป็นจริงแม้หลังจากที่ฉันใช้ data.table มากฉันมักจะยังคงพยายามที่จะเข้าใจรหัสของตัวเองฉันได้เขียนตัวอักษรสัปดาห์ที่ผ่านมา นี่คือตัวอย่างชีวิตของภาษาเขียนอย่างเดียว en.wikipedia.org/wiki/Write-only_language ดังนั้นหวังว่าสักวันเราจะสามารถใช้ dplyr บน data.table ได้
Ufos

385

นี่คือความพยายามของฉันสำหรับคำตอบที่ครอบคลุมจากมุมมอง dplyr ตามร่างคร่าว ๆ ของคำตอบของอรุณ (แต่ค่อนข้างจะจัดเรียงใหม่ตามลำดับความสำคัญที่แตกต่างกัน)

วากยสัมพันธ์

มีความรู้สึกส่วนตัวของไวยากรณ์ แต่ฉันยืนหยัดด้วยคำพูดของฉันว่าความกระชับของ data.table ทำให้การเรียนรู้และอ่านยากขึ้น ส่วนหนึ่งเป็นเพราะ dplyr แก้ปัญหาได้ง่ายกว่ามาก!

สิ่งหนึ่งที่สำคัญจริง ๆ ที่ dplyr ทำเพื่อคุณคือมัน จำกัดตัวเลือกของคุณ ฉันอ้างว่าปัญหาตารางเดี่ยวส่วนใหญ่สามารถแก้ไขได้ด้วยตัวกรองคำกริยาที่สำคัญเพียงห้าตัวเลือกกลายพันธุ์จัดเรียงและสรุปพร้อมกับคำวิเศษณ์ "ตามกลุ่ม" ข้อ จำกัด นั้นเป็นความช่วยเหลือที่ยิ่งใหญ่เมื่อคุณเรียนรู้การจัดการข้อมูลเพราะจะช่วยให้คุณสามารถคิดปัญหาได้ ใน dplyr คำกริยาเหล่านี้แต่ละคำจะถูกจับคู่กับฟังก์ชั่นเดียว แต่ละฟังก์ชั่นทำหน้าที่เดียวและเข้าใจง่ายในการแยก

%>%คุณสามารถสร้างความซับซ้อนโดยท่อการดำเนินงานที่เรียบง่ายเหล่านี้ร่วมกันกับ นี่คือตัวอย่างจากหนึ่งในโพสต์ที่อรุณเชื่อมโยงกับ :

diamonds %>%
  filter(cut != "Fair") %>%
  group_by(cut) %>%
  summarize(
    AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = n()
  ) %>%
  arrange(desc(Count))

แม้ว่าคุณจะไม่เคยเห็น dplyr มาก่อน (หรือแม้แต่ R!) คุณก็ยังสามารถรับรู้สิ่งที่เกิดขึ้นได้เพราะฟังก์ชั่นเป็นคำกริยาภาษาอังกฤษทั้งหมด ข้อเสียของคำกริยาภาษาอังกฤษคือพวกเขาต้องการการพิมพ์มากกว่า [แต่ฉันคิดว่ามันสามารถบรรเทาได้โดยการเติมข้อความอัตโนมัติที่ดีกว่า

นี่คือรหัส data.table ที่เทียบเท่า:

diamondsDT <- data.table(diamonds)
diamondsDT[
  cut != "Fair", 
  .(AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = .N
  ), 
  by = cut
][ 
  order(-Count) 
]

ยากที่จะทำตามรหัสนี้เว้นแต่คุณจะคุ้นเคยกับ data.table แล้ว (ฉันยังไม่สามารถหาวิธีเยื้องซ้ำได้[ ในลักษณะที่ดูดีต่อตาของฉัน) โดยส่วนตัวเมื่อฉันดูรหัสฉันเขียน 6 เดือนที่ผ่านมามันเหมือนกับการดูรหัสที่เขียนโดยคนแปลกหน้าดังนั้นฉันจึงชอบที่ตรงไปตรงมามากถ้า verbose รหัส

สองปัจจัยย่อยอื่น ๆ ที่ฉันคิดว่าลดลงเล็กน้อยอ่าน:

  • เนื่องจากการใช้งานตารางข้อมูลเกือบทุกครั้งทำให้[คุณต้องใช้บริบทเพิ่มเติมเพื่อพิจารณาว่าเกิดอะไรขึ้น ตัวอย่างเช่นคือx[y] เข้าร่วมสองตารางข้อมูลหรือแยกคอลัมน์จากกรอบข้อมูลหรือไม่ นี่เป็นเพียงปัญหาเล็ก ๆ เพราะในรหัสที่เขียนดีชื่อตัวแปรควรแนะนำสิ่งที่เกิดขึ้น

  • ฉันชอบที่group_by()เป็นการดำเนินการแยกต่างหากใน dplyr มันพื้นฐานการเปลี่ยนแปลงการคำนวณดังนั้นฉันคิดว่าควรจะเห็นได้ชัดเมื่อ skimming รหัสและมันง่ายที่จะจุดgroup_by()กว่าอาร์กิวเมนต์by[.data.table

ฉันก็ชอบที่ท่อ ไม่ได้ จำกัด อยู่เพียงแค่หนึ่งแพคเกจ คุณสามารถเริ่มต้นด้วยการจัดเก็บข้อมูลของคุณด้วย tidyrและจบลงด้วยการพล็อตในggvis และคุณไม่ จำกัด แพ็คเกจที่ฉันเขียน - ใคร ๆ ก็สามารถเขียนฟังก์ชั่นที่เป็นส่วนต่อเนื่องของ data manipulation pipe ที่จริงแล้วฉันชอบที่จะเขียนรหัส data.table ก่อนหน้าด้วย%>%:

diamonds %>% 
  data.table() %>% 
  .[cut != "Fair", 
    .(AvgPrice = mean(price),
      MedianPrice = as.numeric(median(price)),
      Count = .N
    ), 
    by = cut
  ] %>% 
  .[order(-Count)]

และความคิดของท่อด้วย%>%ไม่ได้ จำกัด อยู่เพียงแค่เฟรมข้อมูลและทั่วไปได้อย่างง่ายดายเพื่อบริบทอื่น ๆ : เว็บกราฟิกแบบโต้ตอบ , เว็บขูด , จิสต์ , สัญญาเวลาทำงาน , ... )

หน่วยความจำและประสิทธิภาพ

ฉันได้รวมสิ่งเหล่านี้เข้าด้วยกันเพราะสำหรับฉันแล้วมันไม่สำคัญเลย ผู้ใช้ R ส่วนใหญ่ทำงานกับข้อมูลน้อยกว่า 1 ล้านแถวและ dplyr นั้นเร็วพอสำหรับขนาดของข้อมูลที่คุณไม่ได้ตระหนักถึงเวลาในการประมวลผล เราเพิ่มประสิทธิภาพ dplyr สำหรับการแสดงออกบนข้อมูลขนาดกลาง; อย่าลังเลที่จะใช้ data.table เพื่อรับข้อมูลดิบที่เร็วขึ้น

ความยืดหยุ่นของ dplyr ยังหมายความว่าคุณสามารถปรับแต่งลักษณะการทำงานโดยใช้ไวยากรณ์เดียวกันได้อย่างง่ายดาย หากประสิทธิภาพของ dplyr พร้อมแบ็กเอนด์ data frame นั้นไม่ดีพอสำหรับคุณคุณสามารถใช้ data.table backend (แม้ว่าจะมีชุดของฟังก์ชันที่ จำกัด ) หากข้อมูลที่คุณใช้ไม่พอดีกับหน่วยความจำคุณสามารถใช้แบ็กเอนด์ฐานข้อมูลได้

ทั้งหมดที่กล่าวมาว่าการปฏิบัติงาน dplyr จะดีขึ้นในระยะยาว เราจะนำแนวคิดที่ยอดเยี่ยมของ data.table อย่าง Radix ไปใช้และใช้ดัชนีเดียวกันสำหรับการรวม & ตัวกรอง เรากำลังทำงานแบบขนานเพื่อให้เราสามารถใช้ประโยชน์จากหลายแกน

คุณสมบัติ

บางสิ่งที่เราวางแผนที่จะทำงานในปี 2558:

  • แพคเกจที่จะทำให้มันง่ายที่จะได้รับไฟล์จากดิสก์และในหน่วยความจำเพื่อคล้ายคลึงreadrfread()

  • ตัวเชื่อมที่ยืดหยุ่นมากขึ้นรวมถึงการรองรับตัวเชื่อมที่ไม่ใช่ equi

  • การจัดกลุ่มที่ยืดหยุ่นมากขึ้นเช่นตัวอย่างบู๊ทสแตรปการเปิดใช้งานและอื่น ๆ

ฉันยังลงทุนเวลาในการปรับปรุง R ของการเชื่อมต่อฐานข้อมูลความสามารถในการพูดคุยกับ เว็บ APIsและทำให้ง่ายต่อการ หน้าขูด HTML


27
เพียงแค่ทราบด้านผมไม่เห็นด้วยกับหลายข้อโต้แย้งของคุณ (แม้ว่าฉันชอบdata.tableไวยากรณ์ตัวเอง) แต่คุณสามารถใช้%>%เพื่อท่อdata.tableหากคุณไม่ชอบ[สไตล์ %>%ไม่ได้เฉพาะเจาะจงไปที่dplyrค่อนข้างมาจากแยกบรรจุภัณฑ์ (ซึ่งคุณจะเกิดขึ้นจะเป็นผู้เขียนร่วมของเกินไป) ดังนั้นผมไม่แน่ใจว่าผมเข้าใจสิ่งที่คุณพยายามที่จะพูดในส่วนของคุณไวยากรณ์วรรค
David Arenburg

11
@DavidArenburg จุดดี ฉันได้เขียนไวยากรณ์ใหม่เพื่อหวังว่าจะได้ความชัดเจนในประเด็นหลักของฉันและเพื่อเน้นว่าคุณสามารถใช้%>%กับ data.table
hadley

5
ขอบคุณ Hadley นี่เป็นมุมมองที่มีประโยชน์ เยื้องฉันมักจะทำDT[\n\texpression\n][\texpression\n]( ส่วนสำคัญ ) ซึ่งใช้งานได้ดีจริง ๆ ฉันคงคำตอบของอรุณไว้เป็นคำตอบในขณะที่เขาตอบคำถามเฉพาะของฉันโดยตรงซึ่งไม่มากเกี่ยวกับการเข้าถึงไวยากรณ์ แต่ฉันคิดว่านี่เป็นคำตอบที่ดีสำหรับผู้ที่พยายามเข้าใจความแตกต่าง / commonalities ระหว่างdplyrและdata.table.
BrodieG

33
ทำไมต้องทำงานกับ fastread เมื่อมีอยู่แล้วfread()? เวลาจะไม่ถูกใช้ไปกับการปรับปรุง fread () หรือทำงานกับสิ่งอื่น ๆ (ด้อยพัฒนา) หรือไม่?
EDi

10
API ของdata.tableก่อตั้งขึ้นเมื่อมีการใช้[]สัญลักษณ์ในทางที่ผิด นั่นคือความแข็งแกร่งที่ยิ่งใหญ่ที่สุดและจุดอ่อนที่ยิ่งใหญ่ที่สุดของมัน
พอล

65

ในการตอบคำถามหัวข้อโดยตรง ...

dplyr อย่างแน่นอนทำสิ่งที่ไม่data.tableได้

จุดของคุณ # 3

dplyr บทคัดย่อ (หรือจะ) ปฏิสัมพันธ์ DB ที่มีศักยภาพ

เป็นคำตอบสำหรับคำถามของคุณโดยตรง แต่ไม่ได้รับการยกระดับให้สูงพอ dplyrเป็นส่วนขยายด้านหน้าของกลไกการจัดเก็บข้อมูลที่หลากหลายซึ่งเป็นdata.tableส่วนขยายไปยังอันเดียว

ดูdplyrเป็นอินเทอร์เฟซผู้ไม่เชื่อเรื่องพระเจ้าที่มีเป้าหมายทั้งหมดที่ใช้ Grammer เดียวกันซึ่งคุณสามารถขยายเป้าหมายและตัวจัดการได้ตามต้องการ data.tableคือจากdplyrมุมมองหนึ่งในเป้าหมายเหล่านั้น

คุณจะไม่ (หวังว่า) จะเห็นวันที่data.tableพยายามแปลข้อความค้นหาของคุณเพื่อสร้างคำสั่ง SQL ที่ทำงานกับที่เก็บข้อมูลบนดิสก์หรือเครือข่าย

dplyrอาจทำสิ่งต่าง ๆdata.tableไม่ได้หรืออาจทำไม่ดี

ขึ้นอยู่กับการออกแบบการทำงานในหน่วยความจำ data.tableอาจมีช่วงเวลาที่ยากลำบากมากขึ้นในการประมวลผลแบบสอบถามแบบขนานมากกว่าdplyrอาจมีช่วงเวลาที่ยากมากขึ้นการขยายตัวเองลงในการประมวลผลแบบขนานของแบบสอบถามกว่า


เพื่อตอบคำถามในร่างกาย ...

การใช้

มีงานการวิเคราะห์ที่ง่ายกว่าในการเขียนโค้ดด้วยแพ็คเกจหนึ่งหรืออีกแพ็คเกจสำหรับผู้ที่คุ้นเคยกับแพ็คเกจ (เช่นการกดแป้นบางครั้งผสมกับระดับความลับที่จำเป็น

นี่อาจดูเหมือนถ่อ แต่คำตอบที่แท้จริงคือไม่ คนที่คุ้นเคยกับเครื่องมือต่าง ๆ ดูเหมือนจะใช้สิ่งที่พวกเขาคุ้นเคยกันมากที่สุดหรือเครื่องมือที่เหมาะสมกับงานที่ทำอยู่ เมื่อพูดถึงบางครั้งคุณต้องการนำเสนอความสามารถในการอ่านเฉพาะบางครั้งระดับของประสิทธิภาพและเมื่อคุณต้องการทั้งสองระดับที่สูงพอคุณอาจต้องใช้เครื่องมืออื่นเพื่อให้สอดคล้องกับสิ่งที่คุณมีอยู่แล้ว .

ประสิทธิภาพ

มีงานวิเคราะห์ที่ดำเนินการอย่างมีนัยสำคัญ (เช่นมากกว่า 2x) มีประสิทธิภาพมากกว่าในแพ็คเกจหนึ่งเทียบกับแพ็คเกจอื่น

ไม่มีอีกแล้ว data.tableเก่งที่มีประสิทธิภาพในทุกสิ่งที่มันทำที่ไหนdplyrรับภาระในการถูก จำกัด ในบางประการในการจัดเก็บข้อมูลพื้นฐานและการขนย้ายที่ลงทะเบียน

ซึ่งหมายความว่าเมื่อคุณพบปัญหาเกี่ยวกับประสิทธิภาพdata.tableคุณสามารถมั่นใจได้ว่ามันอยู่ในฟังก์ชันการสืบค้นของคุณและถ้ามันเป็นปัญหาคอขวดด้วยdata.tableคุณจะได้รับความสุขจากการยื่นรายงาน สิ่งนี้ก็เป็นจริงเช่นกันเมื่อdplyrใช้data.tableเป็นส่วนหลัง คุณอาจเห็นบางส่วนค่าใช้จ่ายจากdplyrแต่ก็เป็นไปได้ว่ามันเป็นคำค้นหาของคุณ

เมื่อdplyrมีปัญหาด้านประสิทธิภาพการทำงานกับแบ็คเอนด์คุณสามารถแก้ไขได้โดยการลงทะเบียนฟังก์ชั่นสำหรับการประเมินแบบไฮบริดหรือ (ในกรณีของฐานข้อมูล) ที่จัดการคิวรีที่สร้างขึ้นก่อนดำเนินการ

ดูคำตอบที่ยอมรับได้เมื่อใดที่ดีกว่า data.table?


3
ไม่สามารถตัดข้อมูล data.table ด้วย tbl_dt ได้หรือไม่ ทำไมไม่เพียงแค่ได้รับสิ่งที่ดีที่สุดของทั้งสองโลก
aaa90210

22
คุณลืมพูดถึงคำสั่ง reverse "data.table แน่นอนว่าสิ่งที่ dplyr ไม่สามารถทำได้"ซึ่งก็เป็นจริงเช่นกัน
jangorecki

25
คำตอบของอรุณอธิบายได้ดี สิ่งสำคัญที่สุด (ในแง่ของประสิทธิภาพ) จะเป็น fread, ปรับปรุงโดยการอ้างอิง, การเชื่อมต่อแบบกลิ้ง, การรวมที่ทับซ้อนกัน ฉันเชื่อว่าไม่มีแพ็คเกจใด ๆ (ไม่เพียง แต่ dplyr) เท่านั้นที่สามารถแข่งขันกับฟีเจอร์เหล่านั้นได้ เป็นตัวอย่างที่ดีที่สามารถสไลด์สุดท้ายจากนี้นำเสนอ
jangorecki

15
ทั้งหมด data.table เป็นเหตุผลที่ฉันยังคงใช้อาร์มิฉะนั้นฉันจะใช้แพนด้า มันดีกว่า / เร็วกว่าหมีแพนด้า
marbel

8
ฉันชอบ data.table เพราะความเรียบง่ายและคล้ายคลึงกับโครงสร้างไวยากรณ์ SQL งานของฉันเกี่ยวข้องกับการวิเคราะห์ข้อมูลและกราฟิกแบบเฉพาะกิจทุกวันเพื่อสร้างแบบจำลองทางสถิติและฉันต้องการเครื่องมือที่ง่ายพอที่จะทำสิ่งที่ซับซ้อน ตอนนี้ฉันสามารถลดชุดเครื่องมือของฉันเป็น data.table สำหรับข้อมูลและขัดแตะสำหรับกราฟในงานประจำวันของฉันเท่านั้น ยกตัวอย่างที่ฉันสามารถทำได้เช่นนี้: $ DT [กลุ่ม == 1, y_hat: = ทำนาย (พอดี 1, data = .SD),] $, ซึ่งเรียบร้อยจริงๆและฉันคิดว่ามันเป็นข้อได้เปรียบที่ยอดเยี่ยมจาก SQL ใน สภาพแวดล้อมคลาสสิกอาร์
xappppp
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.