[英]Creating a new column based on two old columns in a data frame
data <- data.frame(foo = c(0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1),
bar = c(1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0))
嗨,這里我有一個包含兩列foo和bar的數據框。 我想基於foo和bar數據創建一個新列Complete。
例如。
foo bar complete
0 0 0
1 0 1
0 1 2
編輯:
如果foo==1
且bar==1
則NA
。
接下來,當兩列都是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
如果尋求代數方法,我們可以嘗試下面的一行:
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
視為二進制數的數字。 難度是在foo == 1 & bar == 1
情況下返回NA
。 為此, 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 db 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 db 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
的解決方案,但比第二個慢三倍。
輸入數據的類型對db
的解決方案影響最大,而對Balter的解決方案影響較小。 其他解決方案似乎相當不變。
有趣的是,在我的代數解決方案中使用integer
或double
常數似乎沒有顯着差異。
您可以創建命名向量(在此示例中為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
有很多方法可以做到這一點,一些更有效,取決於你有多少條件。 但一個基本的方法是:
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.