基于第二个数据框中的值过滤数据框

9

I have 2 data frames:

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循环更简单的方法(例如使用“apply”)?
3个回答

7
这里有一个 data.table 的解决方案和另一个简单的解决方案。
需要注意的是,我已经将 ID 作为 data.frame 中的一个特定列而不是 row.names,这是基于意识形态和实际原因。
  • 一个 data.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

第二种简单方法

如果你知道在bt1at1(你的数据集)中,行的顺序是相同的,那么你可以简单地将数据框的适当组件相乘(*按元素逐个运算)。

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函数将其与at1bt1中的ID连接起来,或者如果将ID保留为row.names,则行名称将保留不变。


谢谢!我肯定会在某个时候使用你的第一个解决方案。但是你的简单方法,即将两个数据框相乘,非常有效! - Dalmuti71

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

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 是一个逻辑向量,如果 bt1$Sample1 等于 0,则为 TRUE,然后我们将其用作索引进入 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,则将值设为0,否则将其设置为 at1 中的值,
  • FUN.VALUE=at1$Sample1 是因为 vapply 需要一个函数输出的示例(在我们的情况下是数据框的一列)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接