有没有一种有效的方法可以只对满足条件的行进行变异?想想变异(当(条件))
Is there a efficient way to mutate only on rows that meet a condition? Think mutate(when(condition))
我希望仅在满足特定条件时才应用突变。
我知道我能做到...
data2 <- data1 %>%
group_by(a, b) %>%
mutate(
var1 = case_when(
condition ~ TRUE,
TRUE ~ FALSE,
NA
),
var2 = case_when(
condition ~ TRUE,
max(var28),
var2
),
var3 = case_when(
condition ~ TRUE,
"happy",
var3
),
...more vars here....
)
我想要的是看起来像这样的东西...
data2 <- data1 %>%
group_by(a, b) %>%
mutate(
when(condition),
var1 = FALSE,
var2 = max(var28),
var3 = "happy",
...more vars here....
)
不幸的是mutate(across(when(condition)))
没有成功。
有什么建议吗?
在 mutate 中没有执行此操作的功能,但是 Romain Francois has shared 您可以自己定义一个函数来执行此操作:
library(dplyr, warn.conflicts = F)
mutate_when <- function(.data, when, ...) {
dots <- enquos(...)
names <- names(dots)
mutate(.data, {
test <- {{ when }}
changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
out <- across(all_of(names))
# assuming `changed` and `out` have the same data frame type
out[test, ] <- changed[test, ]
out
})
}
tibble(x = 1:4, y = 1:4) %>%
mutate_when(x < 4, x = -x, y = -y)
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 -1 -1
#> 2 -2 -2
#> 3 -3 -3
#> 4 4 4
由 reprex package (v2.0.1)
于 2021-11-23 创建
另一种可能的解决方案是使用 across() 和 ifelse(),例如如果 x 列中的值小于四,则执行突变:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 3
#> 4 5 4
由 reprex package (v2.0.1)
于 2021-11-24 创建
您也可以将 ifelse 嵌套在一起,例如如果 x 列中的值小于 4,或者任何列中的值等于 3,则 mutate:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x,
ifelse(.x == 3, .x + 10, .x))))
#> # A tibble: 4 × 2
#> x y
#> <int> <dbl>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 13
#> 4 5 4
由 reprex package (v2.0.1)
于 2021-11-24 创建
然后,依此类推:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x,
ifelse(.x == 3, .x + 10,
ifelse(.x >= 5, "outlier", .x)))))
#> # A tibble: 4 × 2
#> x y
#> <chr> <dbl>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 13
#> 4 outlier 4
由 reprex package (v2.0.1)
于 2021-11-24 创建
--
要更有效地进行突变,请不要使用 dplyr::mutate。 ifelse() 函数是矢量化的(更多细节:https://swcarpentry.github.io/r-novice-gapminder/09-vectorization/),如果你有一个大数据框,单独的 ifelse 很可能比 tidyverse 函数更快,例如
library(tidyverse)
df <- tibble(x = c(1:2, 4:5), y = 1:4)
df$y <- ifelse(df$x < 4, -df$y, df$y)
df
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 1 -1
#> 2 2 -2
#> 3 4 3
#> 4 5 4
编辑
另一个可能的选择是通过赋值替换值:df$y[df$x < 4] <- -(df$y); df$x[df$x < 4] <- -(df$x)
(速度快,但有限制)。
以下是建议方法的快速基准测试,包含 100 万行:
library(tidyverse)
df <- tibble(x = sample(1:10, 1000000, replace = TRUE),
y = sample(1:10, 1000000, replace = TRUE))
mutate_func <- function(df){
df %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
}
ifelse_func <- function(df){
df$y <- ifelse(df$x < 4, -df$y, df$y)
}
replacement_func <- function(df) {
df$y[df$x < 4] <- -(df$y)
df$x[df$x < 4] <- -(df$x)
}
mutate_when_func <- function(df) {
mutate_when <- function(.data, when, ...) {
dots <- enquos(...)
names <- names(dots)
mutate(.data, {
test <- {{ when }}
changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
out <- across(all_of(names))
# assuming `changed` and `out` have the same data frame type
out[test, ] <- changed[test, ]
out
})
}
df %>%
mutate_when(x < 4, x = -x, y = -y)
}
library(microbenchmark)
result <- microbenchmark(mutate_func(df), ifelse_func(df),
mutate_when_func(df), replacement_func(df),
times = 10)
autoplot(result)
我希望仅在满足特定条件时才应用突变。
我知道我能做到...
data2 <- data1 %>%
group_by(a, b) %>%
mutate(
var1 = case_when(
condition ~ TRUE,
TRUE ~ FALSE,
NA
),
var2 = case_when(
condition ~ TRUE,
max(var28),
var2
),
var3 = case_when(
condition ~ TRUE,
"happy",
var3
),
...more vars here....
)
我想要的是看起来像这样的东西...
data2 <- data1 %>%
group_by(a, b) %>%
mutate(
when(condition),
var1 = FALSE,
var2 = max(var28),
var3 = "happy",
...more vars here....
)
不幸的是mutate(across(when(condition)))
没有成功。
有什么建议吗?
在 mutate 中没有执行此操作的功能,但是 Romain Francois has shared 您可以自己定义一个函数来执行此操作:
library(dplyr, warn.conflicts = F)
mutate_when <- function(.data, when, ...) {
dots <- enquos(...)
names <- names(dots)
mutate(.data, {
test <- {{ when }}
changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
out <- across(all_of(names))
# assuming `changed` and `out` have the same data frame type
out[test, ] <- changed[test, ]
out
})
}
tibble(x = 1:4, y = 1:4) %>%
mutate_when(x < 4, x = -x, y = -y)
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 -1 -1
#> 2 -2 -2
#> 3 -3 -3
#> 4 4 4
由 reprex package (v2.0.1)
于 2021-11-23 创建另一种可能的解决方案是使用 across() 和 ifelse(),例如如果 x 列中的值小于四,则执行突变:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 3
#> 4 5 4
由 reprex package (v2.0.1)
于 2021-11-24 创建您也可以将 ifelse 嵌套在一起,例如如果 x 列中的值小于 4,或者任何列中的值等于 3,则 mutate:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x,
ifelse(.x == 3, .x + 10, .x))))
#> # A tibble: 4 × 2
#> x y
#> <int> <dbl>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 13
#> 4 5 4
由 reprex package (v2.0.1)
于 2021-11-24 创建然后,依此类推:
library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x,
ifelse(.x == 3, .x + 10,
ifelse(.x >= 5, "outlier", .x)))))
#> # A tibble: 4 × 2
#> x y
#> <chr> <dbl>
#> 1 -1 -1
#> 2 -2 -2
#> 3 4 13
#> 4 outlier 4
由 reprex package (v2.0.1)
于 2021-11-24 创建--
要更有效地进行突变,请不要使用 dplyr::mutate。 ifelse() 函数是矢量化的(更多细节:https://swcarpentry.github.io/r-novice-gapminder/09-vectorization/),如果你有一个大数据框,单独的 ifelse 很可能比 tidyverse 函数更快,例如
library(tidyverse)
df <- tibble(x = c(1:2, 4:5), y = 1:4)
df$y <- ifelse(df$x < 4, -df$y, df$y)
df
#> # A tibble: 4 × 2
#> x y
#> <int> <int>
#> 1 1 -1
#> 2 2 -2
#> 3 4 3
#> 4 5 4
编辑
另一个可能的选择是通过赋值替换值:df$y[df$x < 4] <- -(df$y); df$x[df$x < 4] <- -(df$x)
(速度快,但有限制)。
以下是建议方法的快速基准测试,包含 100 万行:
library(tidyverse)
df <- tibble(x = sample(1:10, 1000000, replace = TRUE),
y = sample(1:10, 1000000, replace = TRUE))
mutate_func <- function(df){
df %>%
mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
}
ifelse_func <- function(df){
df$y <- ifelse(df$x < 4, -df$y, df$y)
}
replacement_func <- function(df) {
df$y[df$x < 4] <- -(df$y)
df$x[df$x < 4] <- -(df$x)
}
mutate_when_func <- function(df) {
mutate_when <- function(.data, when, ...) {
dots <- enquos(...)
names <- names(dots)
mutate(.data, {
test <- {{ when }}
changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
out <- across(all_of(names))
# assuming `changed` and `out` have the same data frame type
out[test, ] <- changed[test, ]
out
})
}
df %>%
mutate_when(x < 4, x = -x, y = -y)
}
library(microbenchmark)
result <- microbenchmark(mutate_func(df), ifelse_func(df),
mutate_when_func(df), replacement_func(df),
times = 10)
autoplot(result)