เหตุใด X [Y] การรวม data.tables จึงไม่อนุญาตให้รวมภายนอกแบบเต็มหรือการรวมด้านซ้าย


123

นี่เป็นคำถามเชิงปรัชญาเกี่ยวกับ data.table join syntax ฉันพบการใช้งาน data.tables มากขึ้นเรื่อย ๆ แต่ยังคงเรียนรู้ ...

รูปแบบการเข้าร่วมX[Y]สำหรับ data.tables นั้นกระชับสะดวกและมีประสิทธิภาพ แต่เท่าที่ฉันบอกได้มันรองรับเฉพาะการรวมภายในและการรวมภายนอกที่ถูกต้องเท่านั้น ในการรับการรวมด้านซ้ายหรือแบบเต็มฉันต้องใช้merge:

  • X[Y, nomatch = NA] - แถวทั้งหมดใน Y - การรวมภายนอกด้านขวา (ค่าเริ่มต้น)
  • X[Y, nomatch = 0] - เฉพาะแถวที่มีการจับคู่ทั้ง X และ Y - การเข้าร่วมภายใน
  • merge(X, Y, all = TRUE) - ทุกแถวจากทั้ง X และ Y - การรวมภายนอกแบบเต็ม
  • merge(X, Y, all.x = TRUE) - แถวทั้งหมดใน X - การรวมภายนอกด้านซ้าย

สำหรับฉันแล้วดูเหมือนว่าจะมีประโยชน์ถ้าX[Y]รูปแบบการเข้าร่วมรองรับการรวมทั้ง 4 ประเภท มีเหตุผลที่รองรับการรวมสองประเภทเท่านั้นหรือไม่?

สำหรับฉันแล้วค่าพารามิเตอร์nomatch = 0และnomatch = NAไม่ได้ใช้งานง่ายสำหรับการดำเนินการ มันง่ายกว่าสำหรับฉันที่จะเข้าใจและจำmergeไวยากรณ์: all = TRUE, all.x = TRUEและall.y = TRUE. เนื่องจากการX[Y]ดำเนินการมีลักษณะคล้ายmergeกันมากกว่าmatchทำไมไม่ใช้mergeไวยากรณ์สำหรับการรวมแทนที่จะใช้พารามิเตอร์matchของฟังก์ชันnomatch

นี่คือตัวอย่างโค้ดของการเข้าร่วม 4 ประเภท:

# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
#    t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16

Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

X[Y, nomatch = NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

merge(X, Y, by = "t", all.y = TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE

# only rows in both X and Y - inner join
X[Y, nomatch = 0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t", all = FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

อัปเดต: data.table v1.9.6 แนะนำon=ไวยากรณ์ซึ่งอนุญาตให้รวมเฉพาะกิจในฟิลด์อื่นที่ไม่ใช่คีย์หลัก คำตอบของ jangorecki สำหรับคำถามจะเข้าร่วม (ผสาน) data frames (ด้านใน, ด้านนอก, ซ้าย, ขวา) ได้อย่างไร? ให้ตัวอย่างบางส่วนของประเภทการรวมเพิ่มเติมที่ data.table สามารถจัดการได้


4
คุณอ่านFAQ 1.12หรือยัง? คุณสามารถโทรY[X]ถ้าคุณต้องการเข้าร่วม outer ซ้ายของX[Y]และrbind(Y[X],X[Y])ถ้าคุณต้องการด้านนอกเต็มรูปแบบเข้าร่วม
mnel

ดูคำตอบของฉันสำหรับข้อมูลเพิ่มเติมแนวทางที่เป็นมิตรกับการรวมภายนอกแบบเต็ม
mnel

@mnel ฉันคิดว่าunique()แนวทางของคุณด้านล่างสำหรับการเข้าร่วมแบบเต็มนั้นดีกว่าrbind(Y[X],X[Y])เนื่องจาก rbind จะเกี่ยวข้องกับการคัดลอกตาราง นั่นถูกต้องใช่ไหม?
Douglas Clark

เท่าที่จะรู้ได้ใช่ ฉันยังไม่ได้ทดสอบว่าการโทรที่ไม่ซ้ำกันสามครั้งนั้นเร็วกว่าการโทรขนาดใหญ่หนึ่งครั้งหรือไม่ (เช่นunique(c(unique(X[,t]), unique(Y[,t]))- นี่ควรจะมีประสิทธิภาพหน่วยความจำมากกว่าเนื่องจากเป็นการรวมสองรายการที่จะน้อยกว่าหรือเท่ากับจำนวนแถวใน X และ Y .
ช่อง

2
คำถามของคุณเป็นคำอธิบายที่ดี ฉันพบคำตอบสำหรับคำถามของฉันในคำถามของคุณ ขอบคุณ
irriss

คำตอบ:


72

อ้างจากdata.table FAQ 1.11 อะไรคือความแตกต่างระหว่างX[Y]และmerge(X, Y)?

X[Y] คือการเข้าร่วมค้นหาแถวของ X โดยใช้ Y (หรือคีย์ของ Y หากมี) เป็นดัชนี

Y[X] คือการเข้าร่วมค้นหาแถวของ Y โดยใช้ X (หรือคีย์ของ X หากมี)

merge(X,Y)ทั้งสองวิธีในเวลาเดียวกัน จำนวนแถวX[Y]และY[X]มักจะแตกต่างกันในขณะที่จำนวนแถวที่ส่งกลับโดยmerge(X,Y)และmerge(Y,X)เหมือนกัน

แต่ที่พลาดประเด็นหลัก งานส่วนใหญ่ต้องการบางสิ่งที่ต้องทำกับข้อมูลหลังจากการรวมหรือรวม เหตุใดจึงต้องรวมคอลัมน์ทั้งหมดของข้อมูลเข้าด้วยกันเพื่อใช้ชุดย่อยเล็ก ๆ ในภายหลัง คุณอาจแนะนำ merge(X[,ColsNeeded1],Y[,ColsNeeded2])ได้ แต่ต้องใช้โปรแกรมเมอร์เพื่อหาคอลัมน์ที่จำเป็น X[Y,j] ใน data.table ทำทุกอย่างให้คุณในขั้นตอนเดียว เมื่อคุณเขียนX[Y,sum(foo*bar)]data.table จะตรวจสอบjนิพจน์โดยอัตโนมัติเพื่อดูว่าคอลัมน์ใดใช้ มันจะย่อยเฉพาะคอลัมน์เหล่านั้นเท่านั้น คนอื่น ๆ จะถูกเพิกเฉย หน่วยความจำถูกสร้างขึ้นสำหรับคอลัมน์ที่jใช้เท่านั้นและYคอลัมน์จะใช้กฎการรีไซเคิล R มาตรฐานภายในบริบทของแต่ละกลุ่ม สมมติว่าfooอยู่ในXและบาร์อยู่Y (พร้อมกับอีก 20 คอลัมน์ในY) ไม่ใช่X[Y,sum(foo*bar)] เร็วกว่าในการตั้งโปรแกรมและทำงานได้เร็วกว่าการรวมทุกอย่างอย่างสิ้นเปลืองตามด้วยชุดย่อย?


หากคุณต้องการการรวมภายนอกด้านซ้ายของ X[Y]

le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE

หากคุณต้องการการรวมภายนอกแบบเต็ม

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]

5
ขอบคุณ @mnel คำถามที่พบบ่อย 1.12 ไม่ได้กล่าวถึงการรวมภายนอกแบบเต็มหรือด้านซ้าย คำแนะนำการเข้าร่วมภายนอกแบบเต็มของคุณด้วย unique () เป็นตัวช่วยที่ดี ที่ควรจะอยู่ในคำถามที่พบบ่อย ฉันรู้ว่า Matthew Dowle "ออกแบบมาเพื่อการใช้งานของเขาเองและเขาต้องการแบบนั้น" (คำถามที่พบบ่อย 1.9) แต่ฉันคิดว่า X[Y,all=T]อาจเป็นวิธีที่ดีในการระบุการรวมภายนอกแบบเต็มภายในไวยากรณ์ data.table X [Y] หรือX[Y,all.x=T]สำหรับการเข้าร่วมด้านซ้าย ฉันสงสัยว่าทำไมมันไม่ได้ออกแบบมาแบบนั้น เพียงแค่ความคิด
Douglas Clark

1
@DouglasClark ได้เพิ่มคำตอบและยื่น2302: การผสานเพิ่ม mnel เข้าร่วมไวยากรณ์คำถามที่พบบ่อย (ที่มีการกำหนดเวลา) ข้อเสนอแนะที่ยอดเยี่ยม!
Matt Dowle

1
@mnel ขอบคุณสำหรับการแก้ปัญหา ... ทำให้วันของฉัน ... :)
Ankit

@mnel มีวิธีใดบ้างที่เราสามารถอนุมาน NA ด้วย 0 เมื่อแสดงX[Y[J(unique_keys)]]?
Ankit

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

24

คำตอบของ @ mnel เป็นจุดที่ต้องยอมรับคำตอบนั้น นี่เพิ่งติดตามนานเกินไปสำหรับความคิดเห็น

ดังที่ mnel กล่าวว่าการรวมภายนอกด้านซ้าย / ขวานั้นได้มาจากการแลกเปลี่ยนYและX: Y[X]-vs-X[Y] -vs-ดังนั้นการรวม 3 ใน 4 ประเภทจึงได้รับการสนับสนุนในไวยากรณ์นั้นไม่ใช่ 2, iiuc

การเพิ่มครั้งที่ 4 ดูเหมือนจะเป็นความคิดที่ดี สมมติว่าเราเพิ่มfull=TRUEหรือboth=TRUEหรือmerge=TRUE(ไม่แน่ใจว่าชื่ออาร์กิวเมนต์ที่ดีที่สุด?) มันไม่เคยเกิดขึ้นกับฉันมาก่อนซึ่งX[Y,j,merge=TRUE]จะมีประโยชน์สำหรับเหตุผลหลังจากในคำถามที่พบบ่อย 1.12 เพิ่มคำขอคุณสมบัติใหม่แล้วและเชื่อมโยงกลับมาที่นี่แล้วขอบคุณ:

FR # 2301: เพิ่ม merge = TRUE อาร์กิวเมนต์สำหรับทั้ง X [Y] และ Y [X] เข้าร่วมเช่น merge () ทำ

เวอร์ชันล่าสุดได้เร่งความเร็วขึ้นmerge.data.table(โดยการคัดลอกตื้น ๆ ภายในเพื่อตั้งค่าคีย์ให้มีประสิทธิภาพมากขึ้นเป็นต้น) ดังนั้นเราจึงพยายามนำmerge()และX[Y]ใกล้ชิดและให้ตัวเลือกทั้งหมดให้กับผู้ใช้เพื่อความคล่องตัวเต็มรูปแบบ มีข้อดีและข้อเสียของทั้งสองอย่าง คำขอคุณสมบัติที่โดดเด่นอีกประการหนึ่งคือ:

FR # 2033: เพิ่ม by.x และ by.y เพื่อ merge.data.table

หากมีคนอื่น ๆ โปรดให้มา

โดยส่วนนี้ในคำถาม:

ทำไมไม่ใช้ไวยากรณ์การผสานสำหรับการรวมแทนที่จะใช้พารามิเตอร์ nomatch ของฟังก์ชันการจับคู่

หากคุณต้องการmerge()ไวยากรณ์และ 3 อาร์กิวเมนต์all, all.xและจากนั้นเพียงแค่ใช้ที่แทนall.y X[Y]คิดว่าน่าจะครอบคลุมทุกกรณี หรือคุณหมายถึงว่าทำไมเป็นอาร์กิวเมนต์เดียวnomatchใน[.data.table? ถ้าเป็นเช่นนั้นก็เป็นเพียงวิธีที่ดูเป็นธรรมชาติเมื่อได้รับ FAQ 2.14: "คุณสามารถอธิบายเพิ่มเติมได้หรือไม่ว่าทำไม data.table จึงได้รับแรงบันดาลใจจากไวยากรณ์ A [B] ในฐาน" แต่ยังnomatchใช้เวลาเพียงสองค่าในปัจจุบันและ0 NAที่อาจจะขยายออกไปเพื่อให้ค่าลบหมายถึงบางสิ่งบางอย่างหรือ 12 จะหมายถึงการใช้ค่าแถวที่ 12 ของการกรอกข้อมูลใน NAS, เช่นหรือในอนาคตอาจจะเป็นเวกเตอร์หรือแม้กระทั่งตัวเองnomatchdata.table

ฮึ่ม วิธีจะโดยการได้โดยไม่ต้องละโต้ตอบด้วยการผสาน = จริงหรือไม่? บางทีเราควรนำสิ่งนี้ไปใช้กับความช่วยเหลือด้านข้อมูล


ขอบคุณ @Matthew คำตอบของ @ mnel นั้นยอดเยี่ยมมาก แต่คำถามของฉันไม่ใช่วิธีการเข้าร่วมแบบเต็มหรือแบบซ้าย แต่ "มีเหตุผลที่รองรับการรวมเพียงสองประเภทหรือไม่" ตอนนี้มันเป็นปรัชญามากขึ้นเล็กน้อย ;-) จริงๆแล้วฉันไม่ชอบการผสานไวยากรณ์ แต่ดูเหมือนว่าจะมีประเพณี R สำหรับการสร้างจากสิ่งที่มีอยู่ซึ่งคนคุ้นเคย ฉันขีดเขียนjoin="all", join="all.x", join="all.y" and join="x.and.y"ไว้ที่ขอบกระดาษโน้ต ไม่แน่ใจว่าจะดีกว่านี้หรือไม่
Douglas Clark

@DouglasClark อาจจะเป็นjoinเช่นนั้นความคิดที่ดี ฉันโพสต์ไปที่ datatable-help แล้วมาดูกัน บางทีให้data.tableเวลาในการปรับตัวด้วย คุณต้องไปโดยไม่ต้องใช้โดยและเข้าร่วมขอบเขตที่สืบทอดมาหรือยัง?
Matt Dowle

ตามที่ระบุไว้ในความคิดเห็นของฉันข้างต้นผมขอแนะนำให้เพิ่มjoinคำหลักเพื่อเมื่อฉันเป็น X[Y,j,join=string]DataTable: ค่าสตริงที่เป็นไปได้สำหรับการเข้าร่วมแนะนำให้เป็น 1) "all.y" และ "right" -
Douglas Clark

1
สวัสดี Matt ห้องสมุด data.table นั้นยอดเยี่ยมมาก ขอบคุณสำหรับสิ่งนั้น; แม้ว่าฉันคิดว่าพฤติกรรมการเข้าร่วม (เป็นการรวมภายนอกที่ถูกต้องตามค่าเริ่มต้น) ควรได้รับการอธิบายอย่างชัดเจนในเอกสารประกอบหลัก ฉันใช้เวลา 3 วันในการคิดออก
Timothée HENRY

1
@tucson เพียงเพื่อการเชื่อมโยงที่นี่ตอนนี้ยื่นเป็นรุ่น #
Matt Dowle

17

นี้ "คำตอบ" เป็นข้อเสนอสำหรับการอภิปราย: ตามที่ระบุไว้ในความคิดเห็นของผมผมขอแนะนำให้เพิ่มjoinพารามิเตอร์ [.data.table () X[Y,j,join=string]เพื่อเปิดใช้งานชนิดที่เพิ่มขึ้นของร่วมคือ: นอกจากการรวมแบบธรรมดา 4 ประเภทแล้วฉันยังแนะนำให้รองรับการรวมแบบพิเศษ 3 ประเภทและการเข้าร่วมแบบครอ

joinค่าสตริง (และนามแฝง) เพื่อเข้าร่วมประเภทต่างๆจะนำเสนอจะเป็น:

  1. "all.y"และ"right"- เข้าร่วมอย่างถูกต้องค่าเริ่มต้น data.table ปัจจุบัน (nomatch = NA) - แถว Y ทั้งหมดที่มี NAs ที่ไม่มี X ที่ตรงกัน
  2. "both"และ"inner" - การรวมภายใน (nomatch = 0) - เฉพาะแถวที่ X และ Y ตรงกัน

  3. "all.x"และ"left" - เข้าร่วมทางซ้าย - แถวทั้งหมดจาก X, NAs ที่ไม่มี Y ตรง:

  4. "outer"และ"full" - การรวมภายนอกแบบเต็ม - แถวทั้งหมดจาก X และ Y, NAs ที่ไม่ตรงกัน

  5. "only.x"และ"not.y"- การไม่เข้าร่วมหรือต่อต้านการเข้าร่วมที่ส่งคืนแถว X ที่ไม่มีการจับคู่ Y

  6. "only.y" และ"not.x"- ไม่เข้าร่วมหรือต่อต้านการเข้าร่วมที่ส่งคืนแถว Y ที่ไม่มีการจับคู่ X
  7. "not.both" - การเข้าร่วมพิเศษที่ส่งคืนแถว X และ Y โดยที่ไม่มีการจับคู่กับตารางอื่นเช่นเอกสิทธิ์หรือ (XOR)
  8. "cross"- การรวมไขว้หรือผลิตภัณฑ์คาร์ทีเซียนโดยแต่ละแถวของ X จับคู่กับแต่ละแถวของ Y

ค่าเริ่มต้นคือค่าjoin="all.y"ที่สอดคล้องกับค่าเริ่มต้นปัจจุบัน

ค่าสตริง "all", "all.x" และ "all.y" สอดคล้องกับmerge()พารามิเตอร์ สตริง "ขวา" "ซ้าย" "ด้านใน" และ "ด้านนอก" อาจตอบสนองต่อผู้ใช้ SQL ได้มากกว่า

สตริง "both" และ "not.both" เป็นคำแนะนำที่ดีที่สุดของฉันในขณะนี้ - แต่อาจมีใครบางคนมีคำแนะนำสตริงที่ดีกว่าสำหรับการเข้าร่วมภายในและการเข้าร่วมแบบเอกสิทธิ์ (ฉันไม่แน่ใจว่า "เอกสิทธิ์" เป็นคำศัพท์ที่ถูกต้องหรือไม่โปรดแก้ไขฉันว่ามีคำที่เหมาะสมสำหรับการเข้าร่วม "XOR" หรือไม่)

การใช้join="not.y"เป็นทางเลือกสำหรับ X[-Y,j]หรือX[!Y,j]ไม่เข้าร่วมไวยากรณ์และอาจชัดเจนกว่า (สำหรับฉัน) แม้ว่าฉันจะไม่แน่ใจว่ามันเหมือนกัน (คุณลักษณะใหม่ใน data.table เวอร์ชัน 1.8.3)

การรวมแบบไขว้อาจมีประโยชน์ในบางครั้ง แต่อาจไม่พอดีกับ data.table กระบวนทัศน์


1
โปรดส่งสิ่งนี้ไปยังdatatable-helpเพื่อการสนทนา
Matt Dowle

3
+1 แต่โปรดส่งไปที่datatable-helpหรือส่งไฟล์กคำขอคุณสมบัติ ฉันไม่คิดจะเพิ่มjoinแต่ถ้ามันเข้าสู่ตัวติดตามมันจะลืมไป
Matt Dowle

1
ฉันเห็นว่าคุณไม่ได้เข้าสู่ระบบ SO มาระยะหนึ่งแล้ว ฉันได้ยื่นเรื่องนี้แล้ว FR # 2301
Matt Dowle

@MattDowle +1 สำหรับคุณสมบัตินี้ (พยายามทำผ่านFR # 2301แต่ได้รับข้อความปฏิเสธสิทธิ์)
adilapapaya

@adilapapaya เราย้ายจาก RForge มาที่ GitHub กรุณา +1 ที่นี่: github.com/Rdatatable/data.table/issues/614 อรุณแจ้งปัญหาให้เสร็จสิ้นเพื่อไม่ให้หายไป
Matt Dowle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.