web-dev-qa-db-ja.com

元の行の順序を維持しながら2つのデータフレームをマージする

2つのデータフレームをマージし、そのうちの1つの元の行の順序を維持します(以下の例のdf.2)。

以下にサンプルデータを示します(class列のすべての値は両方のデータフレームで定義されています)。

df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))
df.2 <- data.frame(object = c('A', 'B', 'D', 'F', 'C'), class = c(2, 1, 2, 3, 1))

私が行った場合:

merge(df.2, df.1)

出力は次のとおりです。

  class object prob
1     1      B  0.5
2     1      C  0.5
3     2      A  0.7
4     2      D  0.7
5     3      F  0.3

sort = FALSEを追加した場合:

merge(df.2, df.1, sort = F)                                                        

結果は次のとおりです。

  class object prob
1     2      A  0.7
2     2      D  0.7
3     1      B  0.5
4     1      C  0.5
5     3      F  0.3

しかし、私が望むのは:

  class object prob
1     2      A  0.7
2     1      B  0.5
3     2      D  0.7
4     3      F  0.3    
5     1      C  0.5
51
DJack

Plyrパッケージのjoin関数を確認してください。マージに似ていますが、データセットの1つの行の順序を維持できます。全体として、マージよりも柔軟性があります。

サンプルデータを使用して、次のようにjoinを使用します。

> join(df.2,df.1)
Joining by: class
  object class prob
1      A     2  0.7
2      B     1  0.5
3      D     2  0.7
4      F     3  0.3
5      C     1  0.5

行の順序を維持するためのマージ関数の修正について説明するリンクがいくつかあります。

http://www.r-statistics.com/2012/01/merging-two-data-frame-objects-while-preserving-the-rows-order/

http://r.789695.n4.nabble.com/patching-merge-to-allow-the-user-to-keep-the-order-of-one-of-the-two-data- frame-objects-merged-td4296561.html

26
user2635373

Df.2の行番号を与える変数を作成するだけです。次に、データをマージしたら、この変数に従って新しいデータセットを並べ替えます。以下に例を示します。

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
df.2$id  <- 1:nrow(df.2)
out  <- merge(df.2,df.1, by = "class")
out[order(out$id), ]
45
PAC

data.table v1.9.5 + から、次のことができます。

require(data.table) # v1.9.5+
setDT(df.1)[df.2, on="class"]

df.1の各行に対してdf.2の一致する行を見つけ、対応する列を抽出することにより、列classで結合を実行します。

11
Arun

また、inner_join関数は、Hadleyのdplyrパッケージに含まれています(plyrの次の反復)。最初のデータセットの行順序を保持します。目的のソリューションとのわずかな違いは、最初のデータセットの元の列の順序も保持することです。したがって、必ずしもマージに使用した列を最初の位置に配置するとは限りません。

上記の例を使用すると、inner_join結果は次のようになります。

inner_join(df.2,df.1)
Joining by: "class"
  object class prob
1      A     2  0.7
2      B     1  0.5
3      D     2  0.7
4      F     3  0.3
5      C     1  0.5
8
alex23lemm

完全を期すために、joinを更新すると、元の行の順序も保持されます。追加する列が数個しかない場合、これは Arunのdata.table answer の代わりになる可能性があります。

library(data.table)
setDT(df.2)[df.1, on = "class", prob := i.prob][]
   object class prob
1:      A     2  0.7
2:      B     1  0.5
3:      D     2  0.7
4:      F     3  0.3
5:      C     1  0.5

ここで、df.2df.1に右結合され、df.1の一致する行からコピーされる新しい列probを取得します。

5
Uwe

受け入れられた回答 は、mergeを使用するときに順序を維持するための手動の方法を提案します。これは、ほとんどの場合に機能しますが、不必要な手動作業を必要とします。この解決策は ソートせずにddply()する方法? の裏にあります。これは、順序を保持する問題を処理しますが、分割適用結合のコンテキストで:

これはしばらく前にplyrメーリングリストに登場しました(@kohskeも同様です)。これは、Peter Meil​​strupが限られた場合に提供するソリューションです。

#Peter's version used a function gensym to
# create the col name, but I couldn't track down
# what package it was in.
keeping.order <- function(data, fn, ...) { 
  col <- ".sortColumn"
  data[,col] <- 1:nrow(data) 
  out <- fn(data, ...) 
  if (!col %in% colnames(out)) stop("Ordering column not preserved by function") 
  out <- out[order(out[,col]),] 
  out[,col] <- NULL 
  out 
} 

したがって、この汎用keeping.order関数を使用して、merge呼び出しの元の行の順序を維持できます。

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
keeping.order(df.2, merge, y=df.1, by = "class")

リクエストに応じて、どちらが得られますか:

> keeping.order(df.2, merge, y=df.1, by = "class")
  class object id prob
3     2      A  1  0.7
1     1      B  2  0.5
4     2      D  3  0.7
5     3      F  4  0.3
2     1      C  5  0.5

したがって、keeping.orderは、受け入れられた回答のアプローチを効果的に自動化します。

3
landroni

この特定のケースでは、コンパクトな基本ソリューションとしてfactorを使用できます。

df.2$prob = factor(df.2$class,labels=df.1$prob)

df.2
#   object class prob
# 1      A     2  0.7
# 2      B     1  0.5
# 3      D     2  0.7
# 4      F     3  0.3
# 5      C     1  0.5

一般的な解決策ではありませんが、次の場合に機能します:

  1. 一意の値を含むルックアップテーブルがあります
  2. 新しいテーブルを作成するのではなく、テーブルを更新したい
  3. ルックアップテーブルはマージ列でソートされます
  4. ルックアップテーブルに追加のレベルはありません
  5. left_join
  6. 要因に問題がない場合

1は交渉できません。残りは私たちにできることです。

df.3  <- df.2 # deal with 2.
df.1b <- df.1[order(df.1$class),] # deal with 3
df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4.
df.3$prob = factor(df.3$class,labels=df.1b$prob)
df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join`
df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6.
1

@PACのおかげで、次のようなものを思いつきました。

merge_sameord = function(x, y, ...) {
    UseMethod('merge_sameord')
}

merge_sameord.data.frame = function(x, y, ...) {
    rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='')
    x[, rstr] = 1:nrow(x)
    res = merge(x, y, all.x=TRUE, sort=FALSE, ...)
    res = res[order(res[, rstr]), ]
    res[, rstr] = NULL
    res
}

これは、最初のデータフレームの順序を保持し、マージされたデータフレームの行数が最初のデータフレームと同じになることを前提としています。余分な列のないきれいなデータフレームが得られます。

1
qed

単純なサブセットが行ういくつかのユースケースがあります。

_# Use the key variable as row.names
row.names(df.1) = df.1$key

# Sort df.1 so that it's rows match df.2
df.3 = df.1[df.2$key, ]

# Create a data.frame with cariables from df.1 and (the sorted) df.2
df.4 = cbind(df.1, df.3)
_

このコードはdf.2とその順序を保持し、df.1からの一致するデータのみを追加します

変数を1つだけ追加する場合、cbind()は不要です。

_row.names(df.1) = df.1$key
df.2$data = df.1[df.2$key, "data"]
_
0
BurninLeo