数据框中的条件值替换

83

我正在尝试理解如何在不使用循环的情况下对数据框中的值进行条件替换。我的数据框结构如下:

df <- data.frame(
  a = c(11.77, 10.9, 10.32, 10.96, 9.906, 10.7, 11.43, 11.41, 10.48512, 11.19),
  b = c(2, 3, 2, 0, 0, 0, 1, 2, 4, 0),
  est = numeric(10)
)
df
          a b est
1  11.77000 2   0
2  10.90000 3   0
3  10.32000 2   0
4  10.96000 0   0
5   9.90600 0   0
6  10.70000 0   0
7  11.43000 1   0
8  11.41000 2   0
9  10.48512 4   0
10 11.19000 0   0

我想做的是检查b的值。如果b为0,我想将est设置为a中的一个值。我知道df$est[df$b == 0] <- 23会在b==0时将所有est的值设置为23。但我不知道如何在这个条件成立时将est设置为a的一个值。例如:
df$est[df$b == 0] <- (df$a - 5)/2.533 
                                

会给出以下警告:

Warning message:
In df$est[df$b == 0] <- (df$a - 5)/2.533 :
  number of items to replace is not a multiple of replacement length

有没有办法可以传递相关单元格,而不是向量?

5个回答

98

由于您正在对 df$est 进行有条件的索引,因此您还需要对替换向量 df$a 进行有条件的索引:

index <- df$b == 0
df$est[index] <- (df$a[index] - 5)/2.533 

当然,变量index只是临时的,我使用它使代码更易读。你可以用一步完成它:

df$est[df$b == 0] <- (df$a[df$b == 0] - 5)/2.533 

为了更好的可读性,你可以使用within

df <- within(df, est[b==0] <- (a[b==0]-5)/2.533)
无论您选择哪种方法,结果如下:
df
          a b      est
1  11.77000 2 0.000000
2  10.90000 3 0.000000
3  10.32000 2 0.000000
4  10.96000 0 2.352941
5   9.90600 0 1.936834
6  10.70000 0 2.250296
7  11.43000 1 0.000000
8  11.41000 2 0.000000
9  10.48512 4 0.000000
10 11.19000 0 2.443743

正如其他人所指出的那样,在你的例子中另一个解决方案是使用ifelse


28

很棒,感谢参考,所有内容+1。 - PKumar
非常有用的回复。如果您使用此功能,请注意DT不是data.table包中的函数,而是数据表对象的引用。 - Alison Bennett

22

以下是一种方法。 ifelse 是向量化的,并检查所有行中b是否有零值,如果是,则用(a-5)/2.53替换est

df <- transform(df, est = ifelse(b == 0, (a - 5)/2.53, est))

11

另一种选择是使用case_when

require(dplyr)

mutate(df, est = case_when(
    b == 0 ~ (a - 5)/2.53, 
    TRUE   ~ est 
))

如果需要区分超过2个情况,这种解决方案会变得更加方便,因为它可以避免嵌套的if_else结构。


7

《R地狱》, 或者基本的 R 文档,会解释为什么在这里使用 df$* 不是最好的方法。从 "[" 的帮助页面中可以得知:

"通过 [ 进行索引操作类似于原子向量,并选择指定元素的列表。[[ 和 $ 都选择列表中的单个元素。主要区别在于,$ 不允许计算索引,而 [[ 允许。x$name 等同于 x[["name", exact = FALSE]]。此外,[[ 的部分匹配行为可以使用 exact 参数进行控制。"

我建议使用 [row,col] 标记。例如:

Rgames: foo   
         x    y z  
   [1,] 1e+00 1 0  
   [2,] 2e+00 2 0  
   [3,] 3e+00 1 0  
   [4,] 4e+00 2 0  
   [5,] 5e+00 1 0  
   [6,] 6e+00 2 0  
   [7,] 7e+00 1 0  
   [8,] 8e+00 2 0  
   [9,] 9e+00 1 0  
   [10,] 1e+01 2 0  
Rgames: foo<-as.data.frame(foo)

Rgames: foo[foo$y==2,3]<-foo[foo$y==2,1]
Rgames: foo
       x y     z
1  1e+00 1 0e+00
2  2e+00 2 2e+00
3  3e+00 1 0e+00
4  4e+00 2 4e+00
5  5e+00 1 0e+00
6  6e+00 2 6e+00
7  7e+00 1 0e+00
8  8e+00 2 8e+00
9  9e+00 1 0e+00
10 1e+01 2 1e+01

如果您首先添加指向 R-Inferno 页面的链接或总结 $ 的问题(最好两者兼备),则此内容值得点赞。 - Andrie
虽然我认为在这种情况下$运算符是完全可以的。(另外,我注意到尽管你发出了警告,但你自己也使用了$...) - Andrie
@Andrie: 是的,我在它能用的地方使用了它(虽然这并没有多大帮助 :-))。OP试图使用它来定义正在操作哪些元素,这就是问题开始的地方。我只是用它来定义选择数据框元素的条件。但你已经知道了 :-)。 - Carl Witthoft

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