9

我有2个数据框:

at1 = data.frame(ID = c("A", "B", "C", "D", "E"), Sample1 = rnorm(5, 50000, 2500),
      Sample2 = rnorm(5, 50000, 2500), Sample3 = rnorm(5, 50000, 2500),
      row.names = "ID")

  Sample1  Sample2  Sample3
A 52626.55 51924.51 50919.90
B 51430.51 49100.38 51005.92
C 50038.27 52254.73 50014.78
D 48644.46 53926.53 51590.05
E 46462.01 45097.48 50963.39

bt1 = data.frame(ID = c("A", "B", "C", "D", "E"), Sample1 = c(0,1,1,1,1),
      Sample2 = c(0,0,0,1,0), Sample3 = c(1,0,1,1,0), 
      row.names = "ID")

   Sample1 Sample2 Sample3
A       0       0       1
B       1       0       0
C       1       0       1
D       1       1       1
E       1       0       0

我想根据 bt1 中相应单元格中的值(0 或 1)过滤 at1 中的每个单元格,并将结果存储在新的数据帧 ct1 中。例如,如果 bt1[1, "Sample1"] = 1 则 ct1[1, "Sample1"] = at1[1, "Sample1"]。如果 bt1[1, "Sample1"] = 0 则 ct1[1, "Sample1"] = 0。我的原始数据框有 100 多列和 30,000 多行。

我想知道是否有比编写 if 循环更简单的方法(例如使用“应用”?)。

4

3 回答 3

7

这是一个data.table解决方案,以及第二个简单的解决方案

注意,我在不是 出于思想和实际原因做ID了一个特定的专栏data.framerow.names

  • adata.table没有行名
  • 我认为将它们视为数据的一部分更容易

library(data.table)
library(reshape2)

bt1 <- data.frame(ID = c("A", "B", "C", "D", "E"), Sample1 = c(0,1,1,1,1),
   Sample2 = c(0,0,0,1,0), Sample3 = c(1,0,1,1,0))

at1 <- data.frame(ID = c("A", "B", "C", "D", "E"), Sample1 = rnorm(5, 50000, 2500),
  Sample2 = rnorm(5, 50000, 2500), Sample3 = rnorm(5, 50000, 2500))

# place in long form
at_long <- data.table(melt(at1, id.var = 1))
bt_long <- data.table(melt(bt1, value.name = 'bt_value', id.var = 1))
# set keys for easy merging with data.tabl
setkeyv(at_long, c('ID','variable'))
setkeyv(bt_long, c('ID','variable'))
# merge
combined <- at_long[bt_long]
# set those where 'bt_value == 0' as 0
set(combined, which(combined[['bt_value']]==0), 'value',0)
# or (using the fact that the `bt` data is only 0 or 1
combined[value := value * bt_value]
# then reshape to wide format
dcast(combined, ID~variable, value.var = 'value')
##   ID  Sample1  Sample2  Sample3
## 1  A     0.00     0.00 50115.24
## 2  B 50173.16     0.00     0.00
## 3  C 48216.31     0.00 51952.30
## 4  D 52387.53 50889.95 44043.66
## 5  E 50982.56     0.00     0.00

第二种简单的方法

bt1如果您知道和(您的数据集)中的行顺序相同at1,您可以简单地将 data.frames 的适当组件相乘(按*元素工作)

sample_cols <- paste0('Sample',1:3)
at1[,sample_cols] * bt1[,sample_cols]

##    Sample1  Sample2  Sample3
## 1     0.00     0.00 50115.24
## 2 50173.16     0.00     0.00
## 3 48216.31     0.00 51952.30
## 4 52387.53 50889.95 44043.66
## 5 50982.56     0.00     0.00

您可以cbind使用IDfromat1bt1如果保留IDas row.names,则 row.names 将持续存在。

于 2012-08-16T23:54:55.183 回答
5

厚颜无耻的方法使用sqldf

library(sqldf)
variables <- "bt1.Sample1*at1.Sample1 Sample1,
    bt1.Sample2*at1.Sample2 Sample2,
    bt1.Sample3*at1.Sample3 Sample3"

fn$sqldf("SELECT $variables from at1,bt1 WHERE at1.ROWID=bt1.ROWID")


#   Sample1  Sample2  Sample3
#1     0.00     0.00 55778.34
#2 48819.24     0.00     0.00
#3 51896.14     0.00 52522.69
#4 47946.93 48604.23 47755.30
#5 49423.68     0.00     0.00
于 2012-08-17T01:40:47.433 回答
5

您可以使用矢量化(除其他外)。

例如:

ct1 <- at1                           # set ct1 equal to at1
ct1$Sample1[bt1$Sample1 == 0] <- 0   # if bt1$Sample1 = 0, set the value to 0

对于第二行:bt1$Sample1 == 0是一个逻辑向量,TRUE如果bt1$Sample1为 0,然后我们将其用作索引ct1,以便将这些值设置为 0。由于ct1初始化为at1,所有其他行(其中bt1$Sample1 == 1)都设置为该值在at1.

另一种方法是使用ifelse,它是 if 语句的向量化形式:

ct1$Sample1 <- ifelse(bt1$Sample1 == 0, 0, at1$Sample1)

这意味着“对于 中的每一行bt1$Sample1,如果bt1$Sample1[row] == 0替换为 0,否则替换为at1$Sample1[row].

您可以对您感兴趣的每一列重复此操作。

您可以遍历列,或者您可以使用类似这样vapply的话:

for each column `col` in bt1:
    ct1$col <- ifelse(bt1$col == 0, 0, at1$col)

这可以通过以下方式实现:

ct1 <- vapply(colnames(bt1), function (col) {
           ifelse(bt1[[col]] == 0, 0, at1[[col]])
        }, FUN.VALUE=at1$Sample1)

?vapply,但简而言之:

  • 意思是“对于colnames(bt1)”中的每一列bt
  • function (col) { ifelse(bt1[[col]] == 0, 0, at1[[col]]) }上面伪代码中的语句:如果 bt1 为 0,则将值 eqqual 设置为 0,at1否则将其设置为 in 的值,
  • FUN.VALUE=at1$Sample1是因为vapply需要一个函数将输出什么的示例(在我们的例子中,是数据框的一列)。
于 2012-08-16T23:43:10.160 回答