Farklı sütun kümeleri olduğunda iki veri çerçevesini satırlarla (geri) birleştir


232

Aynı sütun kümesine sahip olmayan iki veri çerçevesini satır bağlamak mümkün müdür? Bağlandıktan sonra eşleşmeyen sütunları tutmayı umuyorum.

Yanıtlar:



124

Daha yeni bir çözüm kullanmaktır dplyr'ın bind_rowsı daha etkili olduğunu varsayalım fonksiyonu smartbind.

df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
dplyr::bind_rows(df1, df2)
    a  b    c
1   1  6 <NA>
2   2  7 <NA>
3   3  8 <NA>
4   4  9 <NA>
5   5 10 <NA>
6  11 16    A
7  12 17    B
8  13 18    C
9  14 19    D
10 15 20    E

Çok sayıda dataframes (16) farklı sütun adları ile birleştirmek çalışıyorum Bunu denediğimde bir hata alıyorum Hata: Sütun ABCkarakterden sayısal dönüştürülemez. İlk önce sütunları dönüştürmenin bir yolu var mı?
sar

46

Sen kullanabilirsiniz smartbindgelen gtoolspaketin.

Misal:

library(gtools)
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
smartbind(df1, df2)
# result
     a  b    c
1.1  1  6 <NA>
1.2  2  7 <NA>
1.3  3  8 <NA>
1.4  4  9 <NA>
1.5  5 10 <NA>
2.1 11 16    A
2.2 12 17    B
2.3 13 18    C
2.4 14 19    D
2.5 15 20    E

3
smartbindİki büyük veri çerçevesi ile denedim (toplamda yaklaşık 3 * 10 ^ 6 satır) ve 10 dakika sonra iptal ettim.
Joe

2
9 yılda çok şey oldu :) Bugün smartbind kullanmam. Orijinal sorunun büyük veri çerçeveleri belirtmediğini de unutmayın.
neilfws

42

Sütun halinde df1 içinde bir alt kümesi olup df2 (sütun adlarıyla):

df3 <- rbind(df1, df2[, names(df1)])

37

Alternatifi data.table:

library(data.table)
df1 = data.frame(a = c(1:5), b = c(6:10))
df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
rbindlist(list(df1, df2), fill = TRUE)

rbindAyrıca çalışacak data.tablenesneler dönüştürülür sürece data.tablenesneler, böylece

rbind(setDT(df1), setDT(df2), fill=TRUE)

bu durumda da çalışacaktır. Birkaç data.tabare varsa ve bir liste oluşturmak istemiyorsanız bu tercih edilebilir.


Bu, hepsini ayrı liste öğelerinde saklayabildiğiniz için, herhangi bir sayıda veri çerçevesine kolayca genelleştiren en basit, hazır çözümdür. intersectYaklaşım gibi diğer cevaplar sadece 2 veri çerçevesi için çalışır ve kolayca genelleştirilmez.
Rich Pauloo

35

Temel R cevaplarının çoğu, yalnızca bir data.frame'in ek sütunlara sahip olduğu veya elde edilen data.frame'in sütunların kesişimine sahip olacağı durumu ele alır. OP , bağlamadan sonra eşleşmeyen sütunları tutmayı umduğumu yazdığından , bu sorunu gidermek için temel R yöntemlerini kullanan bir yanıt muhtemelen göndermeye değer.

Aşağıda, iki temel R yöntemi sunuyorum: Biri orijinal verileri değiştiriyor. Çerçeveler ve diğeri değişmiyor. Ayrıca, tahribatsız yöntemi ikiden fazla data.frames için genelleştiren bir yöntem sunuyoruz.

İlk olarak, bazı örnek veriler elde edelim.

# sample data, variable c is in df1, variable d is in df2
df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5])
df2 = data.frame(a=6:10, b=16:20, c = letters[8:12])

İki data.frames, orijinali değiştirme
Her iki data.frames öğesindeki tüm sütunları bir rbind(ve işlevin hata vermeden çalışmasına izin vermek) için, her data.frame öğesine NA sütunları ekleyerek uygun eksik adlar doldurulur kullanarak setdiff.

# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA

Şimdi, rbind-em

rbind(df1, df2)
    a  b        d    c
1   1  6  January <NA>
2   2  7 February <NA>
3   3  8    March <NA>
4   4  9    April <NA>
5   5 10      May <NA>
6   6 16     <NA>    h
7   7 17     <NA>    i
8   8 18     <NA>    j
9   9 19     <NA>    k
10 10 20     <NA>    l

İlk iki satırın orijinal verileri değiştirdiğini unutmayın. Çerçeveler, df1 ve df2, her ikisine de tüm sütun kümesini ekler.


İki data.frames, orijinalleri değiştirmeyin Orijinal data.frames'i olduğu gibi
bırakmak için, önce farklı adlar arasında döngü yapın, data.frame kullanarak bir listeye birleştirilen adlandırılmış bir NA vektörü döndürün c. Ardından, data.framesonucu için uygun bir veri çerçevesine dönüştürür rbind.

rbind(
  data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))),
  data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA)))
)

Birçok data.frames, orijinalleri değiştirmeyin
İkiden fazla data.frames olması durumunda, aşağıdakileri yapabilirsiniz.

# put data.frames into list (dfs named df1, df2, df3, etc)
mydflist <- mget(ls(pattern="df\\d+"))
# get all variable names
allNms <- unique(unlist(lapply(mydflist, names)))

# put em all together
do.call(rbind,
        lapply(mydflist,
               function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                  function(y) NA)))))

Belki orijinal data.frames satır adlarını görmek için biraz daha hoş? O zaman bunu yap.

do.call(rbind,
        c(lapply(mydflist,
                 function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                    function(y) NA)))),
          make.row.names=FALSE))

Bazıları farklı sütunlar (her biri yaklaşık 70-90 toplam sütunlar) ile 16 veri çerçeveleri var. Bunu denediğimde, <- mget (ls (pattern = "df \\ d +")) ilk komutuyla takılı kalıyorum. Veri çerçevelerimin farklı adları var. Mydflist <- c (dr, kr, hyt, ed1, of) kullanarak bir liste yapmaya çalıştım ama bu bana çok büyük bir liste verdi.
sar

Sadece @GKi bağlantı
sar

1
@sar kullanımı mydflist <- list(as, dr, kr, hyt, ed1, of). Bu, ortamınızın boyutunu büyütmeyen, ancak yalnızca listenin her öğesini işaret eden bir liste nesnesi oluşturmalıdır (daha sonra içeriğin hiçbirini değiştirmediğiniz sürece). İşlemden sonra, sadece güvende olmak için liste nesnesini kaldırın.
lmo

20

Ayrıca, ortak sütun adlarını da çıkarabilirsiniz.

> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])

6

Bunu yapmak için bir işlev yazdım, çünkü kodumun bir şeylerin yanlış olup olmadığını anlatmasını seviyorum. Bu işlev, hangi sütun adlarının eşleşmediğini ve tür uyuşmazlığınız olup olmadığını açıkça söyleyecektir. Daha sonra verileri birleştirmek için elinden geleni yapacaktır. Yine de çerçeveler. Sınırlama, aynı anda yalnızca iki veri çerçevesini birleştirebilmenizdir.

### combines data frames (like rbind) but by matching column names
# columns without matches in the other data frame are still combined
# but with NA in the rows corresponding to the data frame without
# the variable
# A warning is issued if there is a type mismatch between columns of
# the same name and an attempt is made to combine the columns
combineByName <- function(A,B) {
    a.names <- names(A)
    b.names <- names(B)
    all.names <- union(a.names,b.names)
    print(paste("Number of columns:",length(all.names)))
    a.type <- NULL
    for (i in 1:ncol(A)) {
        a.type[i] <- typeof(A[,i])
    }
    b.type <- NULL
    for (i in 1:ncol(B)) {
        b.type[i] <- typeof(B[,i])
    }
    a_b.names <- names(A)[!names(A)%in%names(B)]
    b_a.names <- names(B)[!names(B)%in%names(A)]
    if (length(a_b.names)>0 | length(b_a.names)>0){
        print("Columns in data frame A but not in data frame B:")
        print(a_b.names)
        print("Columns in data frame B but not in data frame A:")
        print(b_a.names)
    } else if(a.names==b.names & a.type==b.type){
        C <- rbind(A,B)
        return(C)
    }
    C <- list()
    for(i in 1:length(all.names)) {
        l.a <- all.names[i]%in%a.names
        pos.a <- match(all.names[i],a.names)
        typ.a <- a.type[pos.a]
        l.b <- all.names[i]%in%b.names
        pos.b <- match(all.names[i],b.names)
        typ.b <- b.type[pos.b]
        if(l.a & l.b) {
            if(typ.a==typ.b) {
                vec <- c(A[,pos.a],B[,pos.b])
            } else {
                warning(c("Type mismatch in variable named: ",all.names[i],"\n"))
                vec <- try(c(A[,pos.a],B[,pos.b]))
            }
        } else if (l.a) {
            vec <- c(A[,pos.a],rep(NA,nrow(B)))
        } else {
            vec <- c(rep(NA,nrow(A)),B[,pos.b])
        }
        C[[i]] <- vec
    }
    names(C) <- all.names
    C <- as.data.frame(C)
    return(C)
}

2

Belki sorunuzu tamamen yanlış okudum, ancak "Bağlandıktan sonra eşleşmeyen sütunları tutmayı umuyorum" bana bir SQL sorgusu left joinveya right joinbenzeri bir şey aradığınızı düşündürüyor . R, mergeSQL'deki tabloları birleştirmeye benzer şekilde sol, sağ veya iç birleşimleri belirtmenize izin veren işleve sahiptir .

Burada zaten bu konuda büyük bir soru ve cevap var: Veri çerçevelerine (iç, dış, sol, sağ) nasıl birleştirilir (birleştirilir)?


2

gtools / smartbind, muhtemelen tarih olduğu için Tarihlerle çalışmayı sevmiyordu. İşte benim çözümüm ...

sbind = function(x, y, fill=NA) {
    sbind.fill = function(d, cols){ 
        for(c in cols)
            d[[c]] = fill
        d
    }

    x = sbind.fill(x, setdiff(names(y),names(x)))
    y = sbind.fill(y, setdiff(names(x),names(y)))

    rbind(x, y)
}

rind (x, y) yerine dplyr :: bind_rows (x, y) kullanılması, ilk veri çerçevesine göre sütun sırasını korur.
RanonKahn

2

Sadece belgeler için. StackKitaplığı ve işlevini Stackaşağıdaki biçimde deneyebilirsiniz :

Stack(df_1, df_2)

Ayrıca büyük veri kümeleri için diğer yöntemlerden daha hızlı olduğu izlenim var.


1

Ayrıca kullanabilirsiniz sjmisc::add_rows()hangi kullanımları dplyr::bind_rows(), ancak aksine bind_rows(), add_rows()korur nitelikleri ve dolayısıyla için yararlıdır etiketli veriler .

Etiketli veri kümesiyle aşağıdaki örneğe bakın. frq()Taşımasının avantajlı, değeri etiketlerle frekans tabloları basar ise veri etiketlenir.

library(sjmisc)
library(dplyr)

data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)

str(x1)
#> 'data.frame':    10 obs. of  5 variables:
#>  $ c12hour : num  16 148 70 168 168 16 161 110 28 40
#>   ..- attr(*, "label")= chr "average number of hours of care per week"
#>  $ e15relat: num  2 2 1 1 2 2 1 4 2 2
#>   ..- attr(*, "label")= chr "relationship to elder"
#>   ..- attr(*, "labels")= Named num  1 2 3 4 5 6 7 8
#>   .. ..- attr(*, "names")= chr  "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#>  $ e16sex  : num  2 2 2 2 2 2 1 2 2 2
#>   ..- attr(*, "label")= chr "elder's gender"
#>   ..- attr(*, "labels")= Named num  1 2
#>   .. ..- attr(*, "names")= chr  "male" "female"
#>  $ e17age  : num  83 88 82 67 84 85 74 87 79 83
#>   ..- attr(*, "label")= chr "elder' age"
#>  $ e42dep  : num  3 3 3 4 4 4 4 4 4 4
#>   ..- attr(*, "label")= chr "elder's dependency"
#>   ..- attr(*, "labels")= Named num  1 2 3 4
#>   .. ..- attr(*, "names")= chr  "independent" "slightly dependent" "moderately dependent" "severely dependent"

bind_rows(x1, x1) %>% frq(e42dep)
#> 
#> # e42dep <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>   val frq raw.prc valid.prc cum.prc
#>     3   6      30        30      30
#>     4  14      70        70     100
#>  <NA>   0       0        NA      NA

add_rows(x1, x1) %>% frq(e42dep)
#> 
#> # elder's dependency (e42dep) <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>  val                label frq raw.prc valid.prc cum.prc
#>    1          independent   0       0         0       0
#>    2   slightly dependent   0       0         0       0
#>    3 moderately dependent   6      30        30      30
#>    4   severely dependent  14      70        70     100
#>   NA                   NA   0       0        NA      NA

-1
rbind.ordered=function(x,y){

  diffCol = setdiff(colnames(x),colnames(y))
  if (length(diffCol)>0){
    cols=colnames(y)
    for (i in 1:length(diffCol)) y=cbind(y,NA)
    colnames(y)=c(cols,diffCol)
  }

  diffCol = setdiff(colnames(y),colnames(x))
  if (length(diffCol)>0){
    cols=colnames(x)
    for (i in 1:length(diffCol)) x=cbind(x,NA)
    colnames(x)=c(cols,diffCol)
  }
  return(rbind(x, y[, colnames(x)]))
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.