根据列名和变量匹配替换数据框中的单元格值

时间:2016-04-30 02:29:03

标签: r dataframe

我想基于与行变量匹配的列名替换数据框单元格中的值,如示例代码中所示。我知道,嵌套循环不是首选的方法(并且更有效的解决方案可能非常简单),但是却无法找到干净的矢量化方法。我一直在搞乱嵌套的应用,即在apply中调用的函数内部应用,但是没有成功,也不是这似乎是最好的方法。

输入:

test.df
  Group         G1        G2         G3
1    G1 0.63910462 0.5738143 0.97428347
2    G2 0.62578294 0.4653417 0.92010090
3    G3 0.01136336 0.3163722 0.20266664
4    G1 0.09054996 0.1984567 0.17488220
5    G2 0.74865266 0.9862222 0.80725355
6    G3 0.82855980 0.2668529 0.06786335
7    G1 0.74310410 0.8861169 0.44801963
8    G2 0.97329786 0.6682355 0.69658779
9    G3 0.68696020 0.7362139 0.77452962

输出:

test.df
  Group         G1        G2        G3
1    G1         NA 0.5738143 0.9742835
2    G2 0.62578294        NA 0.9201009
3    G3 0.01136336 0.3163722        NA
4    G1         NA 0.1984567 0.1748822
5    G2 0.74865266        NA 0.8072535 
6    G3 0.82855980 0.2668529        NA
7    G1         NA 0.8861169 0.4480196
8    G2 0.97329786        NA 0.6965878
9    G3 0.68696020 0.7362139        NA

代码:

test.df <- data.frame("Group"=rep(c("G1", "G2", "G3"), 3), "G1"=runif(9, 0, 1), "G2"=runif(9, 0, 1), "G3" = runif(9,0,1))

for (j in 1:ncol(test.df)) {
  for(i in 1:nrow(test.df)) {
    if(colnames(test.df)[j] == test.df$Group[i]) {
      test.df[i,j] <- NA
    }
  }
}

我想我可以使用dplyr过滤器来获取需要替换的值,替换它们,然后重新组装数据框,但我有兴趣学习其他选项。

3 个答案:

答案 0 :(得分:3)

使用match()对其进行矢量化。

idx <- cbind(seq_len(nrow(test.df)), match(test.df$Group, names(test.df)[-1]))
test.df[-1][idx] <- NA

idx创建索引矩阵,用于收集我们想要转换为NA的值。第一部分只是行数长度的序列。第二部分将Group列与其他列的名称相匹配。然后我们替换。

这会导致更新test.df

Group         G1        G2        G3
1    G1         NA 0.5738143 0.9742835
2    G2 0.62578294        NA 0.9201009
3    G3 0.01136336 0.3163722        NA
4    G1         NA 0.1984567 0.1748822
5    G2 0.74865266        NA 0.8072535
6    G3 0.82855980 0.2668529        NA
7    G1         NA 0.8861169 0.4480196
8    G2 0.97329786        NA 0.6965878
9    G3 0.68696020 0.7362139        NA

注意:我在创建stringsAsFactors = FALSE时遇到了test.df,所以在您的test.df$Group <- as.character(test.df$Group)中,您必须首先使用{{1}},因为您的示例将其作为因素

答案 1 :(得分:2)

您可以拆分行然后再分配:

s = split(seq_len(nrow(test.df)), test.df$Group)
for (k in names(s)) test.df[s[[k]], k] <- NA_real_

或者使用data.table:

library(data.table)
for (k in names(s)) set(test.df, i = s[[k]], j = k, v = NA_real_)

请注意,您实际上不必在此处使用data.table;我们在data.frame上使用set。在这种情况下set的优点是通过引用进行修改。

答案 2 :(得分:1)

传递接力棒:

test.df %>% mutate(G1 = ifelse(Group == "G1", NA, G1))

#   Group         G1        G2        G3
# 1    G1         NA 0.3337749 0.3999944
# 2    G2 0.25801678 0.4763512 0.3253522
# 3    G3 0.47854525 0.8921983 0.7570871
# 4    G1         NA 0.8643395 0.2026923
# 5    G2 0.08424691 0.3899895 0.7111212
# 6    G3 0.87532133 0.7773207 0.1216919
# 7    G1         NA 0.9606180 0.2454885
# 8    G2 0.83944035 0.4346595 0.1433044
# 9    G3 0.34668349 0.7125147 0.2396294

现在,我如何让mutate扫描所有列?