6

1 つの大きなデータ フレームを 2 番目の同様のデータ フレームの内容で更新するための一般的な解決策を探しています。それぞれ数千行、10,000 列以上のデータセットが多数あります。「更新」データセットは、対応する「ベース」データセットと、行方向に数パーセントからおそらく 50 パーセントまでオーバーラップします。データセットには「キー」列があり、特定のデータセットの一意のキー値ごとに 1 つの行しかありません。

基本的なルールは、特定のセルの更新データセットに NA 以外の値が存在する場合、ベース データセットの同じセルをその値に置き換えることです。(「同じセル」とは、「キー」列と列名の値が同じであることを意味します。)

更新データセットには、rbind で処理できる新しい行 (「挿入」) が含まれる可能性が高いことに注意してください。

したがって、基本データ フレーム「df1」が与えられた場合、列「K」は一意のキー列であり、「P1」..「P3」は 10,000 列を表し、その名前はデータセットのペアごとに異なります。

  K P1 P2 P3
1 A  1  1  1
2 B  1  1  1
3 C  1  1  1

...そして更新データフレーム「df2」:

  K P1 P2 P3
1 B  2 NA  2
2 C NA  2  2
3 D  2  2  2

必要な結果は次のとおりです。「B」と「C」の 1 は 2 で上書きされましたが、NA では上書きされませんでした。

  K P1 P2 P3
1 A  1  1  1
2 B  2  1  2
3 C  1  2  2
4 D  2  2  2

これはマージ候補ではないようです。マージにより、重複する行 (「キー」列に関して) または重複する列 (P1.x、P1.y など) が得られるため、何らかの形で折りたたむために反復する必要があります。 .

最終的な行/列の次元で行列を事前に割り当て、df1 の内容を入力してから、df2 の重複する行を反復処理しましたが、1 秒あたり 20 セル以上のパフォーマンスを得ることができず、何時間もかかりました(SAS の同等の DATA ステップの UPDATE 機能の数分と比較して)。

何かが欠けていると確信していますが、同等の例が見つかりません。

よく似た ddply の使用法が見られますが、一般的な解決策ではありません。これが結合の問題であることは明らかではないため、data.tableパッケージは役に立たなかったようです。少なくとも一般的には、非常に多くの列にまたがっています。

また、交差する行のみに焦点を当てたソリューションは、他の行を識別して rbind できるため、適切です。

上記のデータ フレームを作成するためのコードを次に示します。

cat("K,P1,P2,P3", "A,1,1,1", "B,1,1,1", "C,1,1,1", file="f1.dat", sep="\n");
cat("K,P1,P2,P3", "B,2,,2", "C,,2,2", "D,2,2,2", file="f2.dat", sep="\n");
df1 <- read.table("f1.dat", sep=",", header=TRUE, stringsAsFactors=FALSE);
df2 <- read.table("f2.dat", sep=",", header=TRUE, stringsAsFactors=FALSE);

ありがとう

4

4 に答える 4

2

これは列ごとにループし、dt1参照ごとに設定され、(うまくいけば)速くなるはずです。

dt1 = as.data.table(df1)
dt2 = as.data.table(df2)
if (!identical(names(dt1),names(dt2)))
    stop("Assumed for now. Can relax later if needed.")
w = chmatch(dt2$K, dt1$K)
for (i in 2:ncol(dt2)) {
    nna = !is.na(dt2[[i]])
    set(dt1,w[nna],i,dt2[[i]][nna])
}
dt1 = rbind(dt1,dt2[is.na(w)])
dt1
     K P1 P2 P3
[1,] A  1  1  1
[2,] B  2  1  2
[3,] C  1  2  2
[4,] D  2  2  2
于 2012-04-19T00:52:23.820 に答える
2

これはおそらく最速のソリューションではありませんが、完全にベースで行われます。

(トミーのコメントごとに回答を更新)

#READING IN YOUR DATA FRAMES
df1 <- read.table(text="  K P1 P2 P3
1 A  1  1  1
2 B  1  1  1
3 C  1  1  1", header=TRUE)

df2 <- read.table(text="  K P1 P2 P3
1 B  2 NA  2
2 C NA  2  2
3 D  2  2  2", header=TRUE)

all <- c(levels(df1$K), levels(df2$K))                  #all cells of key column
dups <- all[duplicated(all)]                            #the overlapping key cells
ndups <- all[!all %in% dups]                            #unique key cells
df3 <- rbind(df1[df1$K%in%ndups, ], df2[df2$K%in%ndups, ]) #bind the unique rows

decider <- function(x, y) ifelse(is.na(x), y, x) #function replaces NAs if existing
df4 <- data.frame(mapply(df2[df2$K%in%dups, ], df1[df1$K%in%dups, ], 
    FUN = decider)) #repalce all NAs of df2 with df1 values if they exist

df5 <- rbind(df3, df4) #bind unique rows of df1 and df2 with NA replaced df4
df5 <- df5[order(df5$K), ]  #reorder based on key column
rownames(df5) <- 1:nrow(df5)  #give proper non duplicated rownames
df5

これにより、次の結果が得られます。

  K P1 P2 P3
1 A  1  1  1
2 B  2  1  2
3 C  1  2  2
4 D  2  2  2

よく読んでみると、すべての列に同じ名前が付いているわけではありませんが、同じ順序であると想定しています。これはより役立つアプローチかもしれません:

all <- c(levels(df1$K), levels(df2$K))
dups <- all[duplicated(all)]
ndups <- all[!all %in% dups]
LS <- list(df1, df2)
LS2 <- lapply(seq_along(LS), function(i) {
        colnames(LS[[i]]) <- colnames(LS[[2]])
        return(LS[[i]])
    }
)

LS3 <- lapply(seq_along(LS2), function(i) LS2[[i]][LS2[[i]]$K%in%ndups, ])
LS4 <- lapply(seq_along(LS2), function(i) LS2[[i]][LS2[[i]]$K%in%dups, ])

decider <- function(x, y) ifelse(is.na(x), y, x)
DF <- data.frame(mapply(LS4[[2]], LS4[[1]], FUN = decider))
DF$K <- LS4[[1]]$K
LS3[[3]] <- DF
df5 <- do.call("rbind", LS3)
df5 <- df5[order(df5$K), ]
rownames(df5) <- 1:nrow(df5)
df5
于 2012-04-04T16:50:24.357 に答える
1

編集:この回答は無視してください。行ごとにループするのは悪い考えです。動作しますが、非常に遅いです。後世に残せ!私の2回目の試みを別の回答として参照してください。

require(data.table)
dt1 = as.data.table(df1)
dt2 = as.data.table(df2)
K = dt2[[1]]
for (i in 1:nrow(dt2)) {
    k = K[i]
    p = unlist(dt2[i,-1,with=FALSE])
    p = p[!is.na(p)]
    dt1[J(k),names(p):=as.list(p),with=FALSE]
}

matrixまたは、代わりに使用できますdata.frameか?その場合、更新する行番号と列番号を含む 2 列の行列A[B]whereを使用する 1 行にすることができます。B

于 2012-04-10T12:15:11.090 に答える
0

以下は、小さなサンプル データの正しい答えを示し、テーブルの「コピー」の数を最小限に抑えようとし、新しい fread と (新しい?) rbindlist を使用します。より大きな実際のデータセットで機能しますか? フラット化/正規化/スタックしようとしたときに発生したメモリの問題に関する元の投稿のすべてのコメントを完全にフォローしていないため、このルートを既に試した場合はお詫び申し上げます。

library(data.table)
library(reshape2)

cat("K,P1,P2,P3", "A,1,1,1", "B,1,1,1", "C,1,1,1", file="f1.dat", sep="\n")
cat("K,P1,P2,P3", "B,2,,2", "C,,2,2", "D,2,2,2", file="f2.dat", sep="\n")

dt1s<-data.table(melt(fread("f1.dat"), id.vars="K"), key=c("K","variable")) # read f1.dat, melt to long/stacked format, and convert to data.table

dt2s<-data.table(melt(fread("f2.dat"), id.vars="K", na.rm=T), key=c("K","variable")) # read f2.dat, melt to long/stacked format (removing NAs), and convert to data.table
setnames(dt2s,"value","value.new")

dt1s[dt2s,value:=value.new] # Update new values

dtout<-reshape(rbindlist(list(dt1s,dt1s[dt2s][is.na(value),list(K,variable,value=value.new)])), direction="wide", idvar="K", timevar="variable") # Use rbindlist to insert new records, and then reshape
setkey(dtout,K)
setnames(dtout,colnames(dtout),sub("value.", "", colnames(dtout))) # Clean up the column names
于 2013-01-16T04:16:42.930 に答える