شرح - iframe html




دمج العديد من data.frames في قائمة في وقت واحد (5)

تقليل يجعل هذا سهل إلى حد ما:

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>

ملاحظة: يبدو أن هذا خطأ يمكن دمجه. المشكلة هي أنه لا يوجد أي تحقق من أن إضافة اللواحق (للتعامل مع تداخل الأسماء غير المطابقة) يجعلها فريدة بالفعل. عند نقطة معينة فإنه يستخدم [.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 بشكل جيد.

لدي قائمة بالعديد من data.frames التي أريد دمجها. تكمن المشكلة هنا في أن كل ملف data.frame يختلف من حيث عدد الصفوف والأعمدة ، لكنهم يشتركون جميعًا في المتغيرات الأساسية (التي دعوت بها "var1" و "var2" في الشفرة أدناه). إذا كانت البيانات.أطر متطابقة من حيث الأعمدة ، يمكن أن أقوم فقط rbind ، والتي من rbind.fill ستقوم rbind.fill بالعمل ، ولكن هذا ليس هو الحال مع هذه البيانات.

نظرًا لأن أمر merge لا يعمل إلا على البيانات 2.frames ، فتوجهت إلى الإنترنت للحصول على أفكار. حصلت على هذا واحد من here ، والتي عملت تماما في 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

(بالمناسبة ، أرى إشارات أخرى إلى هذا الخطأ في elsewhere بدون قرار).

هل هناك طريقة لحل هذا؟


تقوم الحلول الأخرى هنا بعمل جيد للبيانات الصغيرة ، ولكنها تقوم بشكل متكرر بإنشاء وتدمير الكثير من المتغيرات للقيام بذلك. لتجنب التعقيد N ^ 2 من القيام بشيء مثل

X = A
X = merge(X,B)
X = merge(X,C)
...
X = merge(X,Z)

يمكن استخدام rbind. سوف تحتاج فقط إلى إدارة أسماء الأعمدة بنفسك ، وهذا هو الألم. لا أعتقد أن هناك طريقة جيدة للقيام بذلك إلا عن طريق دفع بعض الكود مثل ما يلي.

allnames <- unique(unlist(sapply(myBigDataframeList,names)))
for(i in 1:length(myBigDataframeList)){
  columnmap <- match(allnames,names(myBigDataframeList[[i]]))
  columnmap <- ifelse(is.na(columnmap),1,columnmap+1)
  myBigDataframeList[[i]] <- cbind(data.frame(dummycolumn=NA),myBigDataframeList[[i]])[,columnmap]
  names(myBigDataframeList[[i]]) <- allnames
}
myBiggerDataframe <- do.call(rbind,myBigDataframeList)

الشيء الوحيد الذي يجب الانتباه إليه هو نوع بيانات الأعمدة النهائية. سيتم إرسال أي شيء مع NAs إلى النوع الصحيح ، ولكن قد تحتاج إلى إدارتين بنفس الاسم ولكن نوع مختلف.


سأل سؤال آخر على وجه التحديد كيفية تنفيذ العديد من الصلات اليسرى باستخدام dplyr في R. تم وضع علامة على السؤال باعتباره نسخة مكررة من هذا السؤال لذا أجب هنا:

library(dplyr)
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)
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

يمكنك أيضًا تنفيذ full_join () و inner_join ()

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

#Source: local data frame [4 x 4] 
#  i  j  k  l
#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(function(dtf1,dtf2) inner_join(dtf1,dtf2,by="i"), .)
#Source: local data frame [1 x 4]

#  i j k l
#1 c 3 5 7

من أجل اكتمالها ، إليك إصدار R الأساسي للانضمام الكامل

Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all = TRUE),
       list(x,y,z))

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

قد يتوفر حل جديد في حزمة purrr . بالنسبة لسؤالك المحدد ، يمكنك استخدام reduce() ملاحظة r الصغير مقارنة بـ base::Reduce ، ولكن قد تتمكن من تجنب المشكلة تمامًا باستخدام map_dfr() أو map_dfc مما قد يمنع المشكلة عن طريق عمل الخريطة والتقليل خطوة في واحد.






r-faq