4

これは、以前の質問に対するより複雑なフォローアップです。答えはマトリックスを使用することでしたが、それは異なるモードの値を持つデータ フレームでは機能しません。

サイズの異なるデータ フレームを文字列と整数列で結合し、複数の条件に応じてそれらの合計を計算したいと考えています。

条件

  1. 合計は、一致する「名前」値を持つ行に対してのみ計算されます
  2. 合計は、一致する列名についてのみ計算されます
  3. のセルdf4が 0 でも NA でもない場合、合計はdf3 + df4
  4. それ以外の場合、合計はdf1 + df2 + df3

> df1 <- data.frame(Name=c("Joe","Ann","Lee","Dan"), "1"=c(0,1,5,2), "2"=c(3,1,0,0), "3"=c(2,0,2,2), "4"=c(2,1,3,4))
> df1
  Name X1 X2 X3 X4
1  Joe  0  3  2  2
2  Ann  1  1  0  1
3  Lee  5  0  2  3
4  Dan  2  0  2  4

> df2 <- data.frame(Name=c("Joe","Ann","Ken"), "1"=c(3,4,1), "2"=c(2,3,0), "3"=c(2,4,3))
> df2
  Name X1 X2 X3
1  Joe  3  2  2
2  Ann  4  3  4
3  Ken  1  0  3

> df3 <- data.frame(Name=c("Lee","Ben"), "1"=c(1,3), "2"=c(3,4), "3"=c(4,3))
> df3
  Name X1 X2 X3
1  Lee  1  3  4
2  Ben  3  4  3

条件は、このフレームによって異なります。

> df4 <- data.frame(Name=c("Lee","Ann","Dan"), "1"=c(6,0,NA), "2"=c(0,0,4), "3"=c(0,NA,0))
> df4
   Name  X1  X2  X3
1   Lee   6   0   0
2   Ann   0   0  NA 
3   Dan  NA   4   0

上記の例では、これが期待される結果です (* 値は df4 に依存します)。

> dfsum
  Name  X1  X2  X3  X4
1  Joe   3   5   4   2
2  Ann   5   4   4   1
3  Lee   7*  3   6   3
4  Dan   2   4*  2   4
5  Ken   1   0   3  NA
6  Ben   3   4   3  NA

可能な手順は?

最初に df1、df2、df3、df4 を 5 列 6 行に展開し、欠損データを NA で埋めます。

次に、各データ フレームについて:

  1. 行を「名前」で並べ替える
  2. 「名前」列を「X1」...「X4」から分離
  3. "X1"..."X4" 列を行列に変換
  4. 私の他の質問への回答のように行列の合計を計算します、追加の条件1を使用します
  5. 結果行列をデータ フレームに変換する
  6. c「名前」列を結果データフレームにバインドします

これはRでどのように行うことができますか?


解決

@Ricardo Saportaのソリューションは、少し変更するだけで機能します。

, padValue=NA)4 つの addCols()を追加します。

ここで回答したように、sumD3D4 と dtsum の定義を次のように置き換えます。

plus <- function(x) {
  if(all(is.na(x))){
    c(x[0],NA)} else {
      sum(x,na.rm = TRUE)}
}

sumD3D4  <- setkey(rbind(dt3, dt4)[,lapply(.SD, plus), by = Name], "Name")
dtsum <- setkey(rbind(dt1, dt2, dt3)[, lapply(.SD, plus), by=Name], "Name")
4

1 に答える 1

3

data.frame の代わりに data.table を使用する場合、そのby=xxxx機能を使用して名前で追加できます。以下のコードは、期待どおりの結果を提供するはずです。

data.tables に余分な空の列を埋め込んでいることに注意してください。condTrueただし、それ以前に計算します。

library(data.table)
dt1 <- data.table(df1)
dt2 <- data.table(df2)
dt3 <- data.table(df3)
dt4 <- data.table(df4)

# make sure all dt's have the same columns 
#-----------------------------------------#

# identify which dt4 satisfy the condition 
condTrue <- as.data.table(which(!(is.na(dt4) | dt4==0), arr.ind=TRUE))

# ignore column "Name" from dt4
condTrue <- condTrue[col>1]

# convert from (row, col) index to ("Name", columnName) 
condTrue <- data.table(Name=dt4[condTrue$row, Name], colm=names(dt4)[condTrue$col], key="Name")


# First make a list of all the unique column names
allColumnNames <- unique(c(names(dt1), names(dt2), names(dt3), names(dt4)))

# add columns as necessary, using addCols (definted below)
addCols(dt1, allColumnNames)
addCols(dt2, allColumnNames)
addCols(dt3, allColumnNames)
addCols(dt4, allColumnNames)


sumD3D4  <- setkey(rbind(dt3, dt4)[, lapply(.SD, sum), by=Name], "Name")
dtsum    <- setkey(rbind(dt1, dt2, dt3)[, lapply(.SD, sum), by=Name], "Name")

for (Nam in condTrue$Name) {
  colsRepl <- condTrue[.(Nam)]$colm
  valsRepl <- unlist(sumD3D4[.(Nam), c(colsRepl), with=FALSE])
  dtsum[.(Nam), c(colsRepl) :=  as.list(valsRepl)]
}

dtsum
#    Name 1 2 3 4
# 1:  Ann 5 4 4 1
# 2:  Ben 3 4 3 0
# 3:  Dan 2 4 2 4
# 4:  Joe 3 5 4 2
# 5:  Ken 1 0 3 0
# 6:  Lee 7 3 6 3

addCols <- function(x, cols, padValue=0)  {
  # adds to x any columns that are in cols but not in x
  # Returns TRUE  if columns were added
  #         FALSE if no columns added 
  colsMissing <- setdiff(cols, names(x))

  # grab the actual DT name that was passed to function
  dtName <- as.character(match.call()[2])

  if (length(colsMissing)) {
    get(dtName, envir=parent.frame(1))[, c(colsMissing) := padValue]  
    return(TRUE)
  }

  return(FALSE)
}
于 2013-02-23T21:09:55.900 に答える