如果 case_when 找到某个结果则停止执行
Stopping execution if case_when finds a certain result
来自here:
x <- 1:50
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
如何使用 stop() 在 x 被 6 整除后立即停止执行?我不想要多条错误信息,只要一条。
如果您查看 case_when
的内部结构,您会发现它是按向量的顺序执行每个向量,而不是按数据的顺序。也就是说,在您的示例中,您的第一个表达式 x %% 35 == 0
在函数查看您的第二个表达式 x %% 5 == 0
之前计算 x
的整个长度。它评估所有表达式 (LHS),然后才开始检查匹配项。所以没有short-circuiting对你数据的评价
如果您不担心 over-calculation 并且只想截断数据,那么
x <- 1:50
ret <- case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!cumany(x %% 6 == 0)]
# [1] "1" "2" "3" "4" "fizz"
如果你want/need留在case_when
电话内,那么也许
x <- 1:50
ret <- case_when(
cumany(x %% 6 == 0) ~ NA_character_,
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret
# [1] "1" "2" "3" "4" "fizz" NA NA NA NA NA NA NA NA NA NA NA NA
# [18] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
# [35] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## filtered
na.omit(ret)
# [1] "1" "2" "3" "4" "fizz"
# attr(,"na.action")
# [1] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
# [40] 45 46 47 48 49 50
# attr(,"class")
# [1] "omit"
## or more succinctly
ret[!is.na(ret)]
# [1] "1" "2" "3" "4" "fizz"
然后您可以 na.omit
或类似的。但同样,这不是中断或 short-circuiting,因此绝对不会节省执行时间。 (并且假设当找到 %% 6
条件之一时它会自行中断,因此无法提供 infinitely-large 向量。)
为了 short-circuit 对 LHS 的评估,您需要打破 case_when
的矢量化评估并自行完成所有操作。不幸的是,存在一个“问题”:case_when
保持通用性的方式是通过在调用环境的上下文中评估每个 LHS 表达式(和 RHS,就此而言)。例如,它在父框架(即调用 case_when
的环境)中计算 x %% 35 == 0
。 case_when
函数事先并不知道返回的向量将有多长,也不知道如何正确索引(单步执行)所有存在的变量。例如,使用 x %% 35 == 0
似乎很直观,我们首先要检查 x[1] %% 35 == 0
,然后是 x[2] %% 35 == 0
,等等……但是 (x+y) %% 35 == 0
又如何呢 y
可能是相同长度的向量、可整除长度的循环向量或单个值,其中 (x[50]+y[50]) %% 35 == 0
会导致 NA
(或索引错误)。
这里有一种环绕 case_when
的方法,只计算直到找到约束,然后停止。
x <- 1:50
ret <- numeric(length(x))
for (i in seq_along(x)) {
# constraint
if (x[i] %% 6 == 0) break
# regular piece-wise execution
ret[i] <- case_when(
x[i] %% 35 == 0 ~ "fizz buzz",
x[i] %% 5 == 0 ~ "fizz",
x[i] %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x[i])
)
}
ret <- head(ret, i - 1)
ret
# [1] "1" "2" "3" "4" "fizz"
这个循环迭代了6次,其中前5次正常执行,第6次迭代发现x[i] %% 6 == 0
为真,break
s.
如果我不解决这些方法的相对(低)效率,那我就是失职了。
x <- 1:50
microbenchmark::microbenchmark(
one = {
ret <- case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!cumany(x %% 6 == 0)]
},
two = {
ret <- case_when(
cumany(x %% 6 == 0) ~ NA_character_,
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!is.na(ret)]
},
three = {
ret <- numeric(length(x))
for (i in seq_along(x)) {
# constraint
if (x[i] %% 6 == 0) break
# regular piece-wise execution
ret[i] <- case_when(
x[i] %% 35 == 0 ~ "fizz buzz",
x[i] %% 5 == 0 ~ "fizz",
x[i] %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x[i])
)
}
ret <- head(ret, i - 1)
}
)
# Unit: microseconds
# expr min lq mean median uq max neval
# one 136.6 143.55 168.975 152.60 167.55 478.3 100
# two 156.9 171.10 199.213 180.05 206.80 427.3 100
# three 4772.7 5336.75 5854.889 5605.25 6073.20 12001.8 100
在这里应该清楚的是,使用这组表达式(一些模运算),在 R 中计算比我们需要的更多但使用矢量化方法比尝试限制多少要有效得多x
我们处理。
如果你很好奇,当 x
是 500K 长时,这个 third
方法仍然没有效率......
x <- 1:500
# Unit: microseconds
# expr min lq mean median uq max neval
# one 216.9 245.5 287.715 261.55 289.20 601.4 100
# two 220.9 260.8 300.539 277.75 295.75 691.5 100
# three 5578.7 6164.9 6802.093 6531.20 6884.25 13667.9 100
x <- 1:5000
# Unit: microseconds
# expr min lq mean median uq max neval
# one 1468.2 1644.50 3809.862 1708.65 1879.90 196632.1 100
# two 780.9 852.25 986.799 889.90 952.45 6761.6 100
# three 8061.9 8785.15 9836.741 9249.85 9803.70 17088.5 100
x <- 1:50000
# Unit: milliseconds
# expr min lq mean median uq max neval
# one 15.9505 20.33195 26.18902 22.60755 26.75880 230.6372 100
# two 6.8114 8.33300 12.92443 8.95825 14.18375 236.4153 100
# three 34.6127 43.44130 48.28222 47.23290 53.26485 71.2169 100
x <- 1:500000
# Unit: milliseconds
# expr min lq mean median uq max neval
# one 201.1099 220.5286 278.7940 238.9214 280.8388 548.7299 100
# two 82.8113 104.9474 139.0557 118.3804 136.0794 380.3658 100
# three 295.7582 310.8903 335.8939 322.4250 349.4466 567.1915 100
但最终在 5M 处达到了某种奇偶校验:
x <- 1:5000000
# Unit: seconds
# expr min lq mean median uq max neval
# one 2.713632 2.794410 3.371636 3.175023 3.820303 4.682576 10
# two 1.105257 1.278336 1.535301 1.371372 1.854551 2.281774 10
# three 3.082974 3.116061 3.292641 3.314118 3.476838 3.513049 10
(随着 computation-cost 的变化,这会有很大的不同。)
来自here:
x <- 1:50
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
如何使用 stop() 在 x 被 6 整除后立即停止执行?我不想要多条错误信息,只要一条。
如果您查看 case_when
的内部结构,您会发现它是按向量的顺序执行每个向量,而不是按数据的顺序。也就是说,在您的示例中,您的第一个表达式 x %% 35 == 0
在函数查看您的第二个表达式 x %% 5 == 0
之前计算 x
的整个长度。它评估所有表达式 (LHS),然后才开始检查匹配项。所以没有short-circuiting对你数据的评价
如果您不担心 over-calculation 并且只想截断数据,那么
x <- 1:50
ret <- case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!cumany(x %% 6 == 0)]
# [1] "1" "2" "3" "4" "fizz"
如果你want/need留在case_when
电话内,那么也许
x <- 1:50
ret <- case_when(
cumany(x %% 6 == 0) ~ NA_character_,
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret
# [1] "1" "2" "3" "4" "fizz" NA NA NA NA NA NA NA NA NA NA NA NA
# [18] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
# [35] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## filtered
na.omit(ret)
# [1] "1" "2" "3" "4" "fizz"
# attr(,"na.action")
# [1] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
# [40] 45 46 47 48 49 50
# attr(,"class")
# [1] "omit"
## or more succinctly
ret[!is.na(ret)]
# [1] "1" "2" "3" "4" "fizz"
然后您可以 na.omit
或类似的。但同样,这不是中断或 short-circuiting,因此绝对不会节省执行时间。 (并且假设当找到 %% 6
条件之一时它会自行中断,因此无法提供 infinitely-large 向量。)
为了 short-circuit 对 LHS 的评估,您需要打破 case_when
的矢量化评估并自行完成所有操作。不幸的是,存在一个“问题”:case_when
保持通用性的方式是通过在调用环境的上下文中评估每个 LHS 表达式(和 RHS,就此而言)。例如,它在父框架(即调用 case_when
的环境)中计算 x %% 35 == 0
。 case_when
函数事先并不知道返回的向量将有多长,也不知道如何正确索引(单步执行)所有存在的变量。例如,使用 x %% 35 == 0
似乎很直观,我们首先要检查 x[1] %% 35 == 0
,然后是 x[2] %% 35 == 0
,等等……但是 (x+y) %% 35 == 0
又如何呢 y
可能是相同长度的向量、可整除长度的循环向量或单个值,其中 (x[50]+y[50]) %% 35 == 0
会导致 NA
(或索引错误)。
这里有一种环绕 case_when
的方法,只计算直到找到约束,然后停止。
x <- 1:50
ret <- numeric(length(x))
for (i in seq_along(x)) {
# constraint
if (x[i] %% 6 == 0) break
# regular piece-wise execution
ret[i] <- case_when(
x[i] %% 35 == 0 ~ "fizz buzz",
x[i] %% 5 == 0 ~ "fizz",
x[i] %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x[i])
)
}
ret <- head(ret, i - 1)
ret
# [1] "1" "2" "3" "4" "fizz"
这个循环迭代了6次,其中前5次正常执行,第6次迭代发现x[i] %% 6 == 0
为真,break
s.
如果我不解决这些方法的相对(低)效率,那我就是失职了。
x <- 1:50
microbenchmark::microbenchmark(
one = {
ret <- case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!cumany(x %% 6 == 0)]
},
two = {
ret <- case_when(
cumany(x %% 6 == 0) ~ NA_character_,
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
ret[!is.na(ret)]
},
three = {
ret <- numeric(length(x))
for (i in seq_along(x)) {
# constraint
if (x[i] %% 6 == 0) break
# regular piece-wise execution
ret[i] <- case_when(
x[i] %% 35 == 0 ~ "fizz buzz",
x[i] %% 5 == 0 ~ "fizz",
x[i] %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x[i])
)
}
ret <- head(ret, i - 1)
}
)
# Unit: microseconds
# expr min lq mean median uq max neval
# one 136.6 143.55 168.975 152.60 167.55 478.3 100
# two 156.9 171.10 199.213 180.05 206.80 427.3 100
# three 4772.7 5336.75 5854.889 5605.25 6073.20 12001.8 100
在这里应该清楚的是,使用这组表达式(一些模运算),在 R 中计算比我们需要的更多但使用矢量化方法比尝试限制多少要有效得多x
我们处理。
如果你很好奇,当 x
是 500K 长时,这个 third
方法仍然没有效率......
x <- 1:500
# Unit: microseconds
# expr min lq mean median uq max neval
# one 216.9 245.5 287.715 261.55 289.20 601.4 100
# two 220.9 260.8 300.539 277.75 295.75 691.5 100
# three 5578.7 6164.9 6802.093 6531.20 6884.25 13667.9 100
x <- 1:5000
# Unit: microseconds
# expr min lq mean median uq max neval
# one 1468.2 1644.50 3809.862 1708.65 1879.90 196632.1 100
# two 780.9 852.25 986.799 889.90 952.45 6761.6 100
# three 8061.9 8785.15 9836.741 9249.85 9803.70 17088.5 100
x <- 1:50000
# Unit: milliseconds
# expr min lq mean median uq max neval
# one 15.9505 20.33195 26.18902 22.60755 26.75880 230.6372 100
# two 6.8114 8.33300 12.92443 8.95825 14.18375 236.4153 100
# three 34.6127 43.44130 48.28222 47.23290 53.26485 71.2169 100
x <- 1:500000
# Unit: milliseconds
# expr min lq mean median uq max neval
# one 201.1099 220.5286 278.7940 238.9214 280.8388 548.7299 100
# two 82.8113 104.9474 139.0557 118.3804 136.0794 380.3658 100
# three 295.7582 310.8903 335.8939 322.4250 349.4466 567.1915 100
但最终在 5M 处达到了某种奇偶校验:
x <- 1:5000000
# Unit: seconds
# expr min lq mean median uq max neval
# one 2.713632 2.794410 3.371636 3.175023 3.820303 4.682576 10
# two 1.105257 1.278336 1.535301 1.371372 1.854551 2.281774 10
# three 3.082974 3.116061 3.292641 3.314118 3.476838 3.513049 10
(随着 computation-cost 的变化,这会有很大的不同。)