इसके साथ ही एक सूची में कई data.frames विलय


258

मेरे पास कई डेटा की सूची है। मैं विलय करना चाहता हूं। यहां मुद्दा यह है कि प्रत्येक डेटा.फ्रेम पंक्तियों और स्तंभों की संख्या के संदर्भ में भिन्न होता है, लेकिन वे सभी कुंजी चर (जो मैंने कॉल किया है "var1"और "var2"नीचे दिए गए कोड में) साझा करते हैं। यदि डेटा.फ्रेम स्तंभों के संदर्भ में समान थे, तो मैं केवल यह कह सकता था rbind, जिसके लिए प्लाई का rbind.fill काम करेगा, लेकिन इन आंकड़ों के साथ ऐसा नहीं है।

क्योंकि mergeकमांड केवल 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("", ""))

२..2.२ के बाद किसी भी आर संस्करण में, २.११ और २.१२ सहित, यह कोड निम्न त्रुटि के साथ विफल हो जाता है:

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

(संयोग से, मुझे इस त्रुटि के अन्य संदर्भ दिखाई देते हैं कहीं और नहीं संकल्प के साथ दिखाई देते हैं)।

क्या इसका हल करने का कोई तरीका है?

जवाबों:


182

एक और प्रश्न विशेष रूप से पूछा गया कि R में dplyr का उपयोग करके कई बायें जॉइन कैसे करें । प्रश्न इस एक के डुप्लिकेट के रूप में चिह्नित किया गया था, इसलिए मैं यहां उत्तर देता हूं, नीचे 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)

अपडेट जून 2018 : मैंने मर्ज करने के लिए तीन अलग-अलग तरीकों का प्रतिनिधित्व करते हुए तीन खंडों में उत्तर को विभाजित किया। आप शायद इस purrrतरह का उपयोग करना चाहते हैं यदि आप पहले से ही साफ-सुथरे पैकेजों का उपयोग कर रहे हैं। नीचे दिए गए तुलनात्मक उद्देश्यों के लिए, आपको समान नमूना डेटासेट का उपयोग करके एक आधार आर संस्करण मिलेगा।


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()आधार के साथ आर 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) बेस आर के merge()साथ बेस आर Reduce():

और तुलनात्मक उद्देश्यों के लिए, यहाँ बाईं ओर का आधार आर संस्करण है

 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()
डेवआरजीपी

हालांकि मैं patternls (पैटर्न = "DF_name_contains_this") ´ का उपयोग करके एक पैटर्न के आधार पर कई DF में शामिल हो सकता हूं, लेकिन नहीं। इस्तेमाल किया ´noquote (पेस्ट ()) (, लेकिन मैं अभी भी DF की एक सूची के बजाय एक चरित्र वेक्टर का निर्माण कर रहा हूं। मैंने नाम लिखना बंद कर दिया है, जो अप्रिय है।
जॉर्ज विलियम रसेल का पेन

एक और सवाल एक प्रदान करता है अजगर कार्यान्वयन पांडा डेटा फ्रेम की सूची: dfs = [df1, df2, df3]तो reduce(pandas.merge, dfs)
पॉल रौजीक्स

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>

नोट: ऐसा लगता है कि यह यकीनन एक बग है 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तो काम ठीक होगा।


धन्यवाद! मैंने रामनाथ के लिंक पर भी इसका समाधान देखा। काफी आसान लग रहा है। लेकिन मुझे निम्नलिखित त्रुटि मिलती है: "माचिस में त्रुटि। नाम (क्लैब्स, नाम (xi)): नाम पिछले नामों से मेल नहीं खाते"। मैं जिन चरों पर मिलान कर रहा हूं वे सभी सूची में सभी डेटाफ्रेम में मौजूद हैं, इसलिए मैं यह नहीं पकड़ रहा हूं कि यह त्रुटि मुझे क्या बता रही है।
21

1
मैंने R2.7.2 पर इस समाधान का परीक्षण किया और मुझे वही मिलान मिला। नाम त्रुटि। तो इस समाधान और मेरे डेटा के साथ कुछ और मौलिक समस्या है। मैंने कोड का उपयोग किया: कम करें (फ़ंक्शन (x, y) मर्ज (x, y, all = 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)और कुछ अजीब चीजें हो रही हैं जो मुझे अभी तक नहीं मिली हैं।
बेन बोल्कर

@ घाट आप कुछ पर हैं। आपका कोड मेरे लिए ठीक ऊपर चलता है। और जब मैं इसे अपने अनुकूल करता हूं, तो यह ठीक चलता है - सिवाय इसके कि यह एक मर्ज की अनदेखी करता है जिसे मैं चाहता हूं। जब मैं उन्हें छोड़ने के बजाय मुख्य चर जोड़ने की कोशिश करता हूं, तो मुझे एक नई त्रुटि मिलती है "त्रुटि in.null (x): 'x' गायब है"। कोड लाइन "test.reduce <- Reduce (फ़ंक्शन (...) मर्ज (by = match.by, all = T), my.list)" है जहाँ match.by प्रमुख वैरिएबल नामों के वेक्टर हैं जिन्हें मैं विलय करना चाहता हूं। द्वारा।
bshor

52

आप इसे उपयोग कर सकते हैं merge_allमेंreshape पैकेज । आप तर्क mergeका उपयोग करने के लिए पैरामीटर पारित कर सकते हैं...

reshape::merge_all(list_of_dataframes, ...)

डेटा फ़्रेम को मर्ज करने के लिए विभिन्न तरीकों पर एक उत्कृष्ट संसाधन है


ऐसा लगता है कि मैंने अभी-अभी मर्ज किए गए मर्ज_रेस्कुर =) को यह जानने के लिए अच्छा है कि यह फ़ंक्शन पहले से मौजूद है।
SFun28

16
हाँ। जब भी मुझे एक विचार आता है, तो मैं हमेशा जांचता हूं कि क्या @ हडले पहले ही कर चुका है, और ज्यादातर बार वह :-)
रामनाथ

1
मैं थोड़ा उलझन में हूँ; क्या मुझे मर्ज करना चाहिए या मर्ज करना होगा? किसी भी स्थिति में, जब मैं अपने अतिरिक्त तर्कों को या तो जोड़ने की कोशिश करता हूं, तो मुझे "औपचारिक तर्क" "सभी वास्तविक तर्कों से मेल खाते" त्रुटि मिलती है।
21

2
मुझे लगता है कि मैंने इसे reshape2 से गिरा दिया। कम करना + मर्ज करना उतना ही सरल है।
हैडले

2
@ रामनाथ, लिंक मर चुका है, क्या कोई आईना है?
एडुआर्डो

4

ऐसा करने के लिए आप पुनरावर्तन का उपयोग कर सकते हैं। मैंने निम्नलिखित को सत्यापित नहीं किया है, लेकिन यह आपको सही विचार देना चाहिए:

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

2

मैं @PaRRougieux से डेटा उदाहरण का पुन: उपयोग करूंगा

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मेरे पैकेज का कार्य सुरक्षित है में ऐसी सुविधा है, यदि आप इसे दूसरे इनपुट के रूप में डेटा.फ्रेम की सूची देते हैं, तो यह उन्हें पहले इनपुट में पुन: सम्मिलित करेगा।

स्वीकृत उत्तर के डेटा को उधार लेना और बढ़ाना:

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

हमें सभी कॉलम लेने की आवश्यकता नहीं है, हम चुनिंदा सहायकों को ट्रिब्लेक्ट से चुन सकते हैं और चुन सकते हैं (जैसा कि हम .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

मेरे पास बिना किसी सामान्य आईडी कॉलम के डेटाफ्रेम की सूची थी।
मेरे पास कई डीएफएस पर डेटा गायब था। अशक्त मूल्य थे। तालिका फ़ंक्शन का उपयोग करके डेटाफ़्रेम का उत्पादन किया गया था। रिड्यूस, मर्जिंग, rbind, rbind.fill, और उनकी तरह मुझे अपने उद्देश्य में मदद नहीं कर सके। मेरा उद्देश्य एक समझे जाने योग्य मर्ज किए गए डेटाफ्रेम, लापता डेटा और सामान्य आईडी कॉलम के अप्रासंगिक उत्पादन करना था।

इसलिए, मैंने निम्नलिखित कार्य किया। शायद यह फ़ंक्शन किसी की मदद कर सकता है।

##########################################################
####             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 गायब हैं, तो आप अनुपस्थित पंक्ति Ids या लेबल के कई Dfs में शामिल होने के लिए Reduce / Merge के इस संस्करण का उपयोग कर सकते हैं:

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

0

यहां एक सामान्य आवरण है, जिसका उपयोग बाइनरी फ़ंक्शन को मल्टी-पैरामीटर फ़ंक्शन में परिवर्तित करने के लिए किया जा सकता है। इस समाधान का लाभ यह है कि यह बहुत सामान्य है और इसे किसी भी बाइनरी फ़ंक्शन पर लागू किया जा सकता है। आपको बस एक बार करने की आवश्यकता है और फिर आप इसे किसी भी स्थान पर लागू कर सकते हैं।

विचार को डेमो करने के लिए, मैं लागू करने के लिए सरल पुनरावर्तन का उपयोग करता हूं। यह निश्चित रूप से अधिक सुरुचिपूर्ण तरीके से लागू किया जा सकता है जो कार्यात्मक प्रतिमान के लिए आर के अच्छे समर्थन से लाभ उठाता है।

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)()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.