我正在尝试基于dplyr的工作流程(而不是主要使用我习惯的data.table),而且我遇到了一个问题,我无法找到一个等效的dplyr解决方案 . 我经常遇到需要根据单个条件有条件地更新/替换多个列的场景 . 这是一些示例代码,我的data.table解决方案:
library(data.table)
# Create some sample data
set.seed(1)
dt <- data.table(site = sample(1:6, 50, replace=T),
space = sample(1:4, 50, replace=T),
measure = sample(c('cfl', 'led', 'linear', 'exit'), 50,
replace=T),
qty = round(runif(50) * 30),
qty.exit = 0,
delta.watts = sample(10.5:100.5, 50, replace=T),
cf = runif(50))
# Replace the values of several columns for rows where measure is "exit"
dt <- dt[measure == 'exit',
`:=`(qty.exit = qty,
cf = 0,
delta.watts = 13)]
是否有一个简单的dplyr解决方案来解决同样的问题?我想避免使用ifelse,因为我不想多次输入条件 - 这是一个简化的例子,但有时很多基于单个条件的赋值 .
在此先感谢您的帮助!
8 回答
以破坏通常的dplyr语法为代价,您可以使用
within
from base:它似乎与管道很好地集成,你可以在里面做任何你想做的事情 .
这些解决方案(1)维护管道,(2)不覆盖输入,(3)只需要指定条件一次:
1a) mutate_cond 为可以合并到管道中的数据框或数据表创建一个简单的函数 . 此函数类似于
mutate
但仅作用于满足条件的行:1b) mutate_last 这是数据帧或数据表的替代函数,它又类似于
mutate
,但仅在group_by
中使用(如下例所示),并且仅对最后一个组而不是每个组进行操作 . 请注意,TRUE> FALSE,因此如果group_by
指定了条件,则mutate_last
将仅对满足该条件的行进行操作 .2) factor out condition 将条件设为一个额外的列,然后将其删除 . 然后使用
ifelse
,replace
或算术与逻辑,如图所示 . 这也适用于数据表 .3) sqldf 我们可以通过管道中的sqldf包使用SQL
update
来获取数据帧(但不是数据表,除非我们转换它们 - 这可能代表dplyr中的一个错误 . 请参阅dplyr issue 1579) . 由于update
的存在,我们似乎不希望地修改此代码中的输入,但实际上update
正在临时生成的数据库中而不是实际输入上作用于输入的副本 .Note 1: 我们用它作为
DF
Note 2: 在dplyr问题134,631,1518和1573中讨论了如何轻松指定更新行子集的问题,其中631是主线程,1573是对此答案的回顾 .
您可以使用
magrittr
的双向管道%<>%
执行此操作:这减少了打字量,但仍然比
data.table
慢得多 .这是我喜欢的解决方案:
它可以让你写出像
这是非常易读的 - 尽管它可能没有那么高效 .
正如上面的eipi10所示,没有一种简单的方法可以在dplyr中进行子集替换,因为DT使用pass-by-reference语法与使用pass-by-value的dplyr . dplyr需要在整个向量上使用
ifelse()
,而DT将执行子集并通过引用进行更新(返回整个DT) . 因此,对于本练习,DT将大大加快 .您可以选择先进行子集,然后进行更新,最后重新组合:
但DT会快得多:(编辑使用eipi10的新答案)
我偶然发现了这个,非常喜欢@G的
mutate_cond()
. 格洛腾迪克,但认为处理新变量可能会派上用场 . 所以,下面有两个补充:不相关:通过使用
filter()
,倒数第二行更多dplyr
开头的三个新行获取变量名称以便在
mutate()
中使用,并在mutate()
发生之前初始化数据框中的任何新变量 . 使用new_init
为data.frame
的剩余部分初始化新变量,默认情况下将其设置为缺失(NA
) .以下是使用虹膜数据的一些示例:
将
Petal.Length
更改为88,其中Species == "setosa"
. 这将适用于原始功能以及此新版本 .与上面相同,但也创建一个新变量
x
(NA
在行中不包括在条件中) . 以前不可能 .与上面相同,但
x
条件中未包含的行设置为FALSE .此示例显示如何将
new_init
设置为list
以初始化具有不同值的多个新变量 . 这里创建了两个新变量,其中排除的行使用不同的值进行初始化(x
初始化为FALSE
,y
为NA
)mutate_cond是一个很好的函数,但如果用于创建条件的列中有NA,则会出错 . 我觉得条件变异应该只留下这些行 . 这匹配filter()的行为,该条件在条件为TRUE时返回行,但是省略了具有FALSE和NA的两行 .
通过这个小小的改变,这个功能就像一个魅力:
随着
rlang
的创建,Grothendieck 1a示例的略微修改版本成为可能,消除了对envir
参数的需要,因为enquo()
捕获了自动创建.p
的环境 .