foo bar complete
0 0 0
1 0 1
0 1 2
嗨,这里我的数据框有两列foo和bar。我想基于foo和bar数据创建一个新列Complete。
例如。
foo==1
修改
如果bar==1
和NA
则<th>
。
答案 0 :(得分:3)
接下来,当两列都是1时使用NA
。从行总和开始。如果其中任何一个为2(列数),请将其替换为NA
。然后将其乘以max.col()
值。
rs <- rowSums(data)
cbind(data, complete = max.col(data) * replace(rs, rs == 2, NA))
# foo bar complete
# 1 0 1 2
# 2 1 0 1
# 3 0 0 0
# 4 0 0 0
# 5 1 1 NA
# 6 0 0 0
# 7 0 1 2
# 8 0 0 0
# 9 1 0 1
# 10 1 1 NA
# 11 1 0 1
如果您不想分配新对象,可以使用本地环境或将其包装到函数中:
local({
rs <- rowSums(data)
max.col(data) * replace(rs, rs == 2, NA)
})
# [1] 2 1 0 0 NA 0 2 0 1 NA 1
答案 1 :(得分:3)
如果寻求代数方法,我们可以尝试下面的一行:
with(data, 2L * bar + foo + 0L * NA^(bar & foo))
with(data, 2L * bar + foo + NA^(bar & foo) - 1L)
with(data, (2L * bar + foo) * NA^(bar & foo))
全部返回
[1] 2 1 0 0 NA 0 2 0 1 NA 1
表达式2L * bar + foo
将bar
和foo
视为二进制数字的数字。困难是在NA
的情况下返回foo == 1 & bar == 1
。为此,bar
和foo
被视为逻辑值。如果两者都是1
,即TRUE
,则NA^(bar & foo)
会返回NA
,否则会1
。
如果表达式的一个操作数是NA
,那么整个表达式也是如此。因此,将NA^(bar & foo)
与2L * bar + foo
结合起来有几种可能性。我想知道哪个是最快的。
到目前为止,
已发布了7种不同的方法 OP已将其样本数据提供为double
类型。正如我在其他场合看到integer
和double
值的显着不同时序,将针对每种类型重复基准运行,以研究数据类型对不同方法的影响。
基准数据将包含100万行:
n_row <- 1e6L
set.seed(1234L)
data_int <- data.frame(foo = sample(0:1, n_row, replace = TRUE),
bar = sample(0:1, n_row, replace = TRUE))
with(data_int, table(foo, bar))
bar foo 0 1 0 249978 250330 1 249892 249800
data_dbl <- data.frame(foo = as.double(data_int$foo),
bar = as.double(data_int$bar))
对于基准测试,使用microbenchmark
包。
# define check function to compare results
check <- function(values) {
all(sapply(values[-1], function(x) all.equal(values[[1]], x)))
}
library(dplyr)
data <- data_dbl
microbenchmark::microbenchmark(
d.b = {
vect = c("0 0" = 0, "1 0" = 1, "0 1" = 2)
unname(vect[match(with(data, paste(foo, bar)), names(vect))])
},
Balter = with(data,ifelse(foo == 0 & bar == 0, 0,
ifelse(foo == 1 & bar == 0, 1,
ifelse(foo == 0 & bar == 1, 2, NA)))),
PoGibas = with(data, case_when(foo == 0 & bar == 0 ~ 0,
foo == 1 & bar == 0 ~ 1,
foo == 0 & bar == 1 ~ 2)),
Rich = local({rs = rowSums(data); max.col(data) * replace(rs, rs == 2, NA)}),
Frank = with(data, ifelse(xor(foo, bar), max.col(data), 0*NA^foo)),
user20650 = with(data, c(0, 1, 2, NA)[c(2*bar + foo + 1)]),
uwe1i = with(data, 2L * bar + foo + 0L * NA^(bar & foo)),
uwe1d = with(data, 2 * bar + foo + 0 * NA^(bar & foo)),
uwe2i = with(data, 2L * bar + foo + NA^(bar & foo) - 1L),
uwe2d = with(data, 2 * bar + foo + NA^(bar & foo) - 1),
uwe3i = with(data, (2L * bar + foo) * NA^(bar & foo)),
uwe3d = with(data, (2 * bar + foo) * NA^(bar & foo)),
times = 11L,
check = check)
请注意,只有创建结果向量,而在data
中创建新列。相应地修改了PoGibas的方法。
如上所述,使用integer
或double
值可能存在速度差异。因此,我想测试使用整数常量(例如0L, 1L
)与双常数0, 1
的效果。
首先,对于double
类型的输入数据:
Unit: milliseconds expr min lq mean median uq max neval cld d.b 1687.05063 1700.52197 1707.72896 1706.48511 1715.46814 1730.62160 11 e Balter 287.89649 377.42284 412.59764 452.75668 458.21178 472.92971 11 d PoGibas 152.90900 154.82164 176.09522 158.23214 165.73524 333.48223 11 c Rich 67.43862 68.68331 76.42759 77.10620 82.42179 89.90016 11 b Frank 170.78293 174.66258 192.85203 179.69422 184.55237 333.74578 11 c user20650 20.11790 20.29744 22.32541 20.81453 21.11509 34.45654 11 a uwe1i 24.86296 25.13935 28.38634 25.60604 28.79395 45.53514 11 a uwe1d 24.90034 25.05439 28.62943 25.41460 29.47379 41.08459 11 a uwe2i 25.21222 25.59754 30.15579 26.29135 33.00361 47.13382 11 a uwe2d 24.38305 25.09385 29.46715 25.41951 29.11112 45.05486 11 a uwe3i 23.27334 23.95714 27.12474 24.28073 25.86336 44.40467 11 a uwe3d 23.23332 23.65073 27.60330 23.96620 29.53911 40.41175 11 a
现在,对于integer
类型的输入数据:
Unit: milliseconds expr min lq mean median uq max neval cld d.b 591.71859 596.31904 607.51452 601.24232 617.13886 636.51405 11 e Balter 284.08896 297.06170 374.42691 303.14888 465.27859 488.19606 11 d PoGibas 151.75851 155.28304 174.31369 159.18364 163.50864 329.00412 11 c Rich 67.79770 71.22311 78.38562 77.46642 84.56777 96.55540 11 b Frank 166.60802 170.34078 192.19833 180.09257 182.43584 350.86681 11 c user20650 19.79204 20.06220 21.95963 20.18624 20.42393 30.13135 11 a uwe1i 27.54680 27.83169 32.36917 28.08939 37.82286 45.21722 11 ab uwe1d 22.60162 22.89350 25.94329 23.10419 23.74173 47.39435 11 a uwe2i 27.05104 27.57607 27.80843 27.68122 28.02048 28.88193 11 a uwe2d 22.83384 22.93522 23.22148 23.12231 23.41210 24.18633 11 a uwe3i 25.17371 26.44427 29.34889 26.68290 27.08276 47.71379 11 a uwe3d 21.68712 21.83060 26.16276 22.37659 28.40750 43.33989 11 a
对于integer
和double
输入值, user20650 的方法最快。接下来是我的代数方法。第三个是Rich
的解决方案,但比第二个慢三倍。
输入数据的类型对d.b
的解决方案影响最大,对 Balter 的影响程度较小。其他解决方案似乎相当不变。
有趣的是,在我的代数解决方案中使用integer
或double
常数似乎没有显着差异。
答案 2 :(得分:1)
您可以使用vect
match
)并使用该向量查找值
vect = c("0 0" = 0, "1 0" = 1, "0 1" = 2)
unname(vect[match(with(data, paste(foo, bar)), names(vect))])
# [1] 2 1 0 0 NA 0 2 0 1 NA 1
答案 3 :(得分:0)
有很多方法可以做到这一点,根据你拥有多少条件,一些更有效。但一个基本的方法是:
data$New_Column <- with(data,ifelse(foo == 0 & bar == 0, 0,
ifelse(foo == 1 & bar == 0, 1,
ifelse(foo == 0 & bar == 1, 2, NA))))
# foo bar New_Column
#1 0 1 2
#2 1 0 1
#3 0 0 0
#4 0 0 0
#5 1 1 NA
#6 0 0 0
#7 0 1 2
#8 0 0 0
#9 1 0 1
#10 1 1 NA
#11 1 0 1