ผสานหลาย data.frames พร้อมกันในรายการ


258

ฉันมีรายการของ data.frames มากมายที่ฉันต้องการผสาน ปัญหาที่นี่คือแต่ละ data.frame แตกต่างกันในแง่ของจำนวนแถวและคอลัมน์ แต่พวกเขาทั้งหมดร่วมตัวแปรสำคัญ (ที่ฉันได้เรียก"var1"และ"var2"ในรหัสด้านล่าง) หาก data.frames เหมือนกันในแง่ของคอลัมน์ฉันสามารถทำได้rbindซึ่ง rbind.fill ของplyrจะทำงานได้ แต่นั่นไม่ใช่กรณีของข้อมูลเหล่านี้

เนื่องจากmergeคำสั่งใช้งานได้กับ data.frames เพียง 2 ตัวเท่านั้นฉันจึงหันไปหาแนวคิดทางอินเทอร์เน็ต ฉันได้หนึ่งจากที่นี่ซึ่งทำงานอย่างสมบูรณ์ใน R 2.7.2 ซึ่งเป็นสิ่งที่ฉันมีในเวลา:

merge.rec <- function(.list, ...){
    if(length(.list)==1) return(.list[[1]])
    Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}

และฉันจะเรียกฟังก์ชั่นเช่นนี้:

df <- merge.rec(my.list, by.x = c("var1", "var2"), 
                by.y = c("var1", "var2"), all = T, suffixes=c("", ""))

แต่ในรุ่น R หลังจาก 2.7.2 รวมถึง 2.11 และ 2.12 รหัสนี้ล้มเหลวพร้อมกับข้อผิดพลาดต่อไปนี้:

Error in match.names(clabs, names(xi)) : 
  names do not match previous names

(บังเอิญฉันเห็นการอ้างอิงอื่น ๆ ของข้อผิดพลาดนี้ที่อื่นโดยไม่มีการแก้ไข)

มีวิธีแก้ปัญหานี้ไหม?

คำตอบ:


182

อีกคำถามหนึ่งถามเฉพาะวิธีการดำเนินการหลายด้านซ้ายร่วมใช้ dplyr ใน R คำถามถูกทำเครื่องหมายว่าซ้ำกับคำถามนี้ดังนั้นฉันจึงตอบที่นี่โดยใช้กรอบข้อมูลตัวอย่าง 3 ตัวด้านล่าง:

x <- data.frame(i = c("a","b","c"), j = 1:3, stringsAsFactors=FALSE)
y <- data.frame(i = c("b","c","d"), k = 4:6, stringsAsFactors=FALSE)
z <- data.frame(i = c("c","d","a"), l = 7:9, stringsAsFactors=FALSE)

อัปเดตมิถุนายน 2561 : ฉันแบ่งคำตอบออกเป็นสามส่วนซึ่งเป็นวิธีการผสานรวมที่แตกต่างกันสามวิธี คุณอาจต้องการใช้purrrวิธีนี้หากคุณใช้แพ็คเกจtidyverseอยู่แล้ว เพื่อวัตถุประสงค์ในการเปรียบเทียบด้านล่างคุณจะพบเวอร์ชันฐาน R โดยใช้ชุดข้อมูลตัวอย่างเดียวกัน


1) เข้าร่วมกับพวกเขาreduceจากpurrrแพคเกจ:

purrrแพคเกจยังมีreduceฟังก์ชั่นที่มีไวยากรณ์กระชับ:

library(tidyverse)
list(x, y, z) %>% reduce(left_join, by = "i")
#  A tibble: 3 x 4
#  i       j     k     l
#  <chr> <int> <int> <int>
# 1 a      1    NA     9
# 2 b      2     4    NA
# 3 c      3     5     7

คุณยังสามารถทำการเชื่อมต่ออื่น ๆ เช่นfull_joinหรือinner_join:

list(x, y, z) %>% reduce(full_join, by = "i")
# A tibble: 4 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 1 a     1     NA     9
# 2 b     2     4      NA
# 3 c     3     5      7
# 4 d     NA    6      8

list(x, y, z) %>% reduce(inner_join, by = "i")
# A tibble: 1 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 1 c     3     5     7

2) dplyr::left_join()ด้วยฐาน R Reduce():

list(x,y,z) %>%
    Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .)

#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7

3) ฐาน R merge()พร้อมฐาน R Reduce():

และเพื่อวัตถุประสงค์ในการเปรียบเทียบนี่คือเวอร์ชั่น R พื้นฐานของการเข้าร่วมทางซ้าย

 Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all.x = TRUE),
        list(x,y,z))
#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7

1
ตัวแปร full_join ทำงานได้อย่างสมบูรณ์และดูน่ากลัวน้อยกว่าคำตอบที่ยอมรับ แม้ว่าจะมีความแตกต่างด้านความเร็วไม่มากนัก
bshor

1
@Axeman ถูกต้อง แต่คุณอาจจะสามารถหลีกเลี่ยง (มองเห็นได้) กลับรายการของเฟรมข้อมูลทั้งหมดโดยใช้map_dfr()หรือmap_dfc()
DaveRGP

ฉันแม้ว่าฉันสามารถเข้าร่วม DF จำนวนหนึ่งโดยยึดตามรูปแบบโดยใช้´ls (pattern = "DF_name_contain_this") ´แต่ไม่ใช่ ใช้´noquote (paste (()) ´แต่ฉันยังคงผลิตตัวอักษรแทนรายการของ DF ฉันลงเอยด้วยการพิมพ์ชื่อซึ่งน่าสะพรึงกลัว
ปากกาของ George William Russel

คำถามก็ยังมีการดำเนินการหลาม : รายการของหมีแพนด้าเฟรมข้อมูลแล้วdfs = [df1, df2, df3] reduce(pandas.merge, dfs)
Paul Rougieux

222

ลดทำให้เรื่องนี้ค่อนข้างง่าย:

merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)

นี่เป็นตัวอย่างที่สมบูรณ์โดยใช้ข้อมูลจำลอง:

set.seed(1)
list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10)))
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
tail(merged.data.frame)
#    x  a  b         y
#12 12 NA 18        NA
#13 13 NA 19        NA
#14 14 NA 20 0.4976992
#15 15 NA NA 0.7176185
#16 16 NA NA 0.3841037
#17 19 NA NA 0.3800352

และนี่คือตัวอย่างการใช้ข้อมูลเหล่านี้เพื่อทำซ้ำmy.list:

merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list)
merged.data.frame[, 1:12]

#  matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y
#1   ALGIERE   200 RI      026       S         NA   <NA>   NA   NA   NA         NA   <NA>
#2     ALVES   100 RI      019       S         NA   <NA>   NA   NA   NA         NA   <NA>
#3    BADEAU   100 RI      032       S         NA   <NA>   NA   NA   NA         NA   <NA>

หมายเหตุ: มีลักษณะเช่นนี้เป็น arguably mergeข้อผิดพลาดใน ปัญหาคือไม่มีการตรวจสอบว่าการเพิ่มคำต่อท้าย (เพื่อจัดการชื่อที่ไม่ตรงกันที่ทับซ้อนกัน) ทำให้เกิดความซ้ำกัน ณ จุดหนึ่งมันใช้[.data.frameซึ่งทำ make.uniqueชื่อทำให้rbindล้มเหลว

# first merge will end up with 'name.x' & 'name.y'
merge(my.list[[1]], my.list[[2]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y
#<0 rows> (or 0-length row.names)
# as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again
merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y senate1995   name         votes.year  
#<0 rows> (or 0-length row.names)
# the next merge will fail as 'name' will get renamed to a pre-existing field.

วิธีที่ง่ายที่สุดในการแก้ไขปัญหาคือการไม่ปล่อยให้เปลี่ยนชื่อฟิลด์สำหรับเขตข้อมูลที่ซ้ำกัน (ซึ่งมีจำนวนมากที่นี่) mergeขึ้นไป เช่น:

my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by,
      names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list))

merge/ Reduceจากนั้นจะทำงานได้ดี


ขอบคุณ! ฉันเห็นโซลูชันนี้ในลิงก์จาก Ramnath ดูง่ายพอ แต่ฉันได้รับข้อผิดพลาดต่อไปนี้: "ข้อผิดพลาดในการจับคู่ชื่อ (clabs, ชื่อ (xi)): ชื่อไม่ตรงกับชื่อก่อนหน้า" ตัวแปรที่ฉันจับคู่นั้นมีอยู่ใน dataframes ทั้งหมดในรายการดังนั้นฉันจึงไม่เข้าใจว่าข้อผิดพลาดนี้บอกอะไรฉัน
bshor

1
ฉันทดสอบโซลูชันนี้ใน R2.7.2 และฉันได้รับข้อผิดพลาดการจับคู่ชื่อเดียวกัน ดังนั้นจึงมีปัญหาพื้นฐานเพิ่มเติมเกี่ยวกับโซลูชันนี้และข้อมูลของฉัน ฉันใช้รหัส: ลด (ฟังก์ชั่น (x, y) ผสาน (x, y, ทั้งหมด = T, by.x = match.by, by.y = match.by), my.list, สะสม = F)
bshor

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

ฉันสงสัยว่ามีบางสิ่งเกิดขึ้นกับเฟรมข้อมูลเปล่า ฉันลองตัวอย่างเช่นนี้empty <- data.frame(x=numeric(0),a=numeric(0); L3 <- c(empty,empty,list.of.data.frames,empty,empty,empty)และได้รับสิ่งแปลก ๆ เกิดขึ้นที่ฉันยังไม่ได้คิด
Ben Bolker

@Charles คุณเข้าสู่บางสิ่ง รหัสของคุณทำงานได้ดีสำหรับฉัน และเมื่อฉันปรับให้เข้ากับของฉันมันก็ทำงานได้ดีเช่นกัน - ยกเว้นว่าเป็นการรวมโดยไม่สนใจตัวแปรหลักที่ฉันต้องการ เมื่อฉันพยายามเพิ่มตัวแปรหลักแทนที่จะปล่อยพวกเขาออกไปฉันได้รับข้อผิดพลาดใหม่ "ข้อผิดพลาดใน is.null (x): 'x' หายไป" บรรทัดโค้ดคือ "test.reduce <- ผสาน (ฟังก์ชั่น (... )) ผสาน (โดย = match.by, all = T), my.list)" โดยที่ match.by เป็นเวกเตอร์ของชื่อตัวแปรหลักที่ฉันต้องการผสาน โดย
bshor

52

คุณสามารถทำได้โดยใช้merge_allในreshapeแพ็คเกจ คุณสามารถส่งพารามิเตอร์เพื่อmergeใช้...อาร์กิวเมนต์

reshape::merge_all(list_of_dataframes, ...)

นี่เป็นทรัพยากรที่ดีเกี่ยวกับวิธีการที่แตกต่างกันในการผสานเฟรมข้อมูล


ดูเหมือนว่าฉันเพิ่งจำลอง merge_recurse =) ดีที่รู้ว่ามีฟังก์ชันนี้อยู่แล้ว
SFun28

16
ใช่. เมื่อใดก็ตามที่ฉันมีความคิดที่ฉันมักจะตรวจสอบว่า @hadley ได้ทำมาแล้วมันและมากที่สุดเท่าที่เขามี :-)
Ramnath

1
ฉันสับสนเล็กน้อย ฉันควรทำ merge_all หรือ merge_recurse หรือไม่ ในกรณีใด ๆ เมื่อฉันพยายามที่จะเพิ่มในข้อโต้แย้งเพิ่มเติมของฉันทั้งฉันจะได้รับข้อผิดพลาด "อาร์กิวเมนต์อย่างเป็นทางการ" ทั้งหมด "จับคู่กับข้อโต้แย้งที่เกิดขึ้นจริงหลาย"
bshor

2
ฉันคิดว่าฉันทำสิ่งนี้จาก reshape2 ลด + รวมเป็นเรื่องง่ายเพียง
hadley

2
@Ramnath ลิงก์ตายไปแล้วมีกระจกมั้ย
Eduardo

4

คุณสามารถใช้การสอบถามซ้ำเพื่อทำสิ่งนี้ ฉันยังไม่ได้ตรวจสอบสิ่งต่อไปนี้ แต่ควรให้แนวคิดที่ถูกต้องแก่คุณ:

MergeListOfDf = function( data , ... )
{
    if ( length( data ) == 2 ) 
    {
        return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
    }    
    return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}

2

ฉันจะใช้ตัวอย่างข้อมูลจาก @PaulRougieux อีกครั้ง

x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)

นี่เป็นวิธีแก้ปัญหาสั้น ๆ ที่น่าใช้purrrและtidyr

library(tidyverse)

 list(x, y, z) %>% 
  map_df(gather, key=key, value=value, -i) %>% 
  spread(key, value)

1

ฟังก์ชั่นeatของแพคเกจsafejoinของฉันมีคุณสมบัติเช่นนี้ถ้าคุณให้รายการของข้อมูลเป็นเฟรมที่สองมันจะเข้าร่วมพวกเขาซ้ำกับอินพุตแรก

การยืมและขยายข้อมูลคำตอบที่ยอมรับได้:

x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)
z2 <- data_frame(i = c("a","b","c"), l = rep(100L,3),l2 = rep(100L,3)) # for later

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
eat(x, list(y,z), .by = "i")
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

เราไม่จำเป็นต้องนำคอลัมน์ทั้งหมดมาใช้เราสามารถเลือกผู้ช่วยเหลือจากtidyselectและเลือก (เมื่อเราเริ่มจากคอลัมน์.xทั้งหมด.xจะถูกเก็บไว้):

eat(x, list(y,z), starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     l
#   <chr> <int> <int>
# 1 a         1     9
# 2 b         2    NA
# 3 c         3     7

หรือลบรายการเฉพาะ:

eat(x, list(y,z), -starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     k
#   <chr> <int> <int>
# 1 a         1    NA
# 2 b         2     4
# 3 c         3     5

หากรายการมีชื่อชื่อจะถูกใช้เป็นคำนำหน้า:

eat(x, dplyr::lst(y,z), .by = "i")
# # A tibble: 3 x 4
#   i         j   y_k   z_l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

หากมีข้อขัดแย้งของคอลัมน์.conflictอาร์กิวเมนต์จะช่วยให้คุณสามารถแก้ไขได้ตัวอย่างเช่นการรับค่าแรก / วินาทีการเพิ่มการรวมการรวมหรือการซ้อน

เก็บไว้ก่อน:

eat(x, list(y, z, z2), .by = "i", .conflict = ~.x)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7

ให้ล่าสุด:

eat(x, list(y, z, z2), .by = "i", .conflict = ~.y)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   100
# 2 b         2     4   100
# 3 c         3     5   100

เพิ่ม:

eat(x, list(y, z, z2), .by = "i", .conflict = `+`)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   109
# 2 b         2     4    NA
# 3 c         3     5   107

รวมกัน:

eat(x, list(y, z, z2), .by = "i", .conflict = dplyr::coalesce)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA     9
# 2 b         2     4   100
# 3 c         3     5     7

รัง:

eat(x, list(y, z, z2), .by = "i", .conflict = ~tibble(first=.x, second=.y))
# # A tibble: 3 x 4
#   i         j     k l$first $second
#   <chr> <int> <int>   <int>   <int>
# 1 a         1    NA       9     100
# 2 b         2     4      NA     100
# 3 c         3     5       7     100

NAค่าสามารถถูกแทนที่โดยใช้.fillอาร์กิวเมนต์

eat(x, list(y, z), .by = "i", .fill = 0)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <dbl> <dbl>
# 1 a         1     0     9
# 2 b         2     4     0
# 3 c         3     5     7

โดยค่าเริ่มต้นจะเป็นการปรับปรุงleft_joinแต่การรวมdplyrทั้งหมดได้รับการสนับสนุนผ่านการ.modeโต้แย้งการเชื่อมแบบคลุมเครือยังได้รับการสนับสนุนผ่านการmatch_fun โต้แย้ง (ซึ่งล้อมรอบแพ็กเกจfuzzyjoin) หรือให้สูตรเช่น ~ X("var1") > Y("var2") & X("var3") < Y("var4")การ byโต้แย้ง


0

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

ดังนั้นฉันทำฟังก์ชั่นดังต่อไปนี้ ฟังก์ชั่นนี้อาจช่วยคนได้

##########################################################
####             Dependencies                        #####
##########################################################

# Depends on Base R only

##########################################################
####             Example DF                          #####
##########################################################

# Example df
ex_df           <- cbind(c( seq(1, 10, 1), rep("NA", 0), seq(1,10, 1) ), 
                         c( seq(1, 7, 1),  rep("NA", 3), seq(1, 12, 1) ), 
                         c( seq(1, 3, 1),  rep("NA", 7), seq(1, 5, 1), rep("NA", 5) ))

# Making colnames and rownames
colnames(ex_df) <- 1:dim(ex_df)[2]
rownames(ex_df) <- 1:dim(ex_df)[1]

# Making an unequal list of dfs, 
# without a common id column
list_of_df      <- apply(ex_df=="NA", 2, ( table) )

มันติดตามฟังก์ชั่น

##########################################################
####             The function                        #####
##########################################################


# The function to rbind it
rbind_null_df_lists <- function ( list_of_dfs ) {
  length_df     <- do.call(rbind, (lapply( list_of_dfs, function(x) length(x))))
  max_no        <- max(length_df[,1])
  max_df        <- length_df[max(length_df),]
  name_df       <- names(length_df[length_df== max_no,][1])
  names_list    <- names(list_of_dfs[ name_df][[1]])

  df_dfs <- list()
  for (i in 1:max_no ) {

    df_dfs[[i]]            <- do.call(rbind, lapply(1:length(list_of_dfs), function(x) list_of_dfs[[x]][i]))

  }

  df_cbind               <- do.call( cbind, df_dfs )
  rownames( df_cbind )   <- rownames (length_df)
  colnames( df_cbind )   <- names_list

  df_cbind

}

ใช้ตัวอย่าง

##########################################################
####             Running the example                 #####
##########################################################

rbind_null_df_lists ( list_of_df )

0

เมื่อคุณมีรายการ dfs และคอลัมน์มี "ID" แต่ในบางรายการ ID บางส่วนหายไปจากนั้นคุณสามารถใช้ลด / การรวมรุ่นนี้เพื่อเข้าร่วม Dfs แถวที่ขาดหายไปหรือป้ายชื่อหลายรายการ:

Reduce(function(x, y) merge(x=x, y=y, by="V1", all.x=T, all.y=T), list_of_dfs)

0

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

เพื่อสาธิตแนวคิดนี้ฉันใช้การเรียกซ้ำแบบง่ายเพื่อนำไปใช้ แน่นอนว่าสามารถนำไปใช้กับวิธีที่หรูหรากว่าซึ่งได้รับประโยชน์จากการสนับสนุนกระบวนทัศน์การทำงานที่ดี

fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}

จากนั้นคุณสามารถห่อฟังก์ชันไบนารีใด ๆ ด้วยและเรียกใช้พารามิเตอร์ตำแหน่ง (โดยปกติคือ data.frames) ในวงเล็บแรกและพารามิเตอร์ที่มีชื่อในวงเล็บที่สอง (เช่นby =หรือsuffix =) หากไม่มีพารามิเตอร์ที่ระบุชื่อปล่อยให้วงเล็บที่สองว่างเปล่า

merge_all <- fold_left(merge)
merge_all(df1, df2, df3, df4, df5)(by.x = c("var1", "var2"), by.y = c("var1", "var2"))

left_join_all <- fold_left(left_join)
left_join_all(df1, df2, df3, df4, df5)(c("var1", "var2"))
left_join_all(df1, df2, df3, df4, df5)()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.