通过带有两个参数的函数创建新列的 purrr 方法
purrr approach for creating new columns through function with two arguments
我很确定有办法到达那里,但我找不到。
我有一个包含多列的数据框。我现在想在从这些列 (0/1) 采样时向包含信息的数据框添加新列。如果我想从每列中采样相同数量的元素,我有一个 across
的整洁解决方案。当从每一列中采样不同数量的元素时,我也有一个(甚至更丑陋的)解决方案 across
,但我希望使用 purrr
有一个更简单的解决方案,我只提供列名作为一个参数和要作为另一个参数采样的元素数量,然后将获得我的新列。
有什么想法吗?
数据
df <- data.frame(x = runif(10),
y = runif(10),
z = runif(10))
df[1, 1] <- NA
df[2, 2] <- NA
df[3, 3] <- NA
sampling <- c(2, 3, 4)
names(sampling) <- c("random_x", "random_y", "random_z")
采样相同数量元素的解决方案
df %>%
mutate(across(everything(),
~if_else(is.na(.),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.)), size = 3))),
.names = "{.col}_random"))
采样不同数量元素的解决方案
df %>%
mutate(across(everything(),
~if_else(is.na(.),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.)), size = sampling[str_detect(names(sampling), paste0(cur_column(), "$"))]))),
.names = "{.col}_random"))
想要的咕噜声方式
……也许沿着这些路线?
df %>%
map2(.x = c("x", "y", "z"),
.y = sampling,
.f = ~if_else(is.na(.x),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.x)), size = .y))))
purrr 方式的问题显然是我没有使用正确的语法,因为我将字符向量作为 .x 而不是 df 中的列传递。
期望的输出
(抛开结果的随机性)
x y z x_random y_random z_random
1 NA 0.06686268 0.7663706 NA 0 0
2 0.7551366 NA 0.5550793 0 NA 1
3 0.7437531 0.61971712 NA 0 0 NA
4 0.5238451 0.57510689 0.7637622 1 0 0
5 0.9593917 0.17481769 0.4443493 0 0 0
6 0.2821633 0.86972254 0.2284449 0 0 0
7 0.3941531 0.61981285 0.8202302 0 0 1
8 0.1473573 0.58482156 0.9078447 0 1 1
9 0.7063327 0.77550907 0.9271699 1 1 1
10 0.6320678 0.06011700 0.2139956 0 1 0
- 如果将
.x
、.y
分别传递给 map2
,则不应使用 df %>% map2(...)
。
is.na(.x)
不正确,因为 .x
是字符值(如 "x"
、"y"
和 "z"
)。我使用 df[[.x]]
对值进行子集化。
- 由于我们没有使用
df %>% ...
,所以 row_number()
将不起作用,因此将其更改为 seq_along
。
这是一种使用 map2_dfc
创建新列的方法,我们使用 bind_cols
将其绑定到原始数据框。
library(dplyr)
library(purrr)
bind_cols(df, map2_dfc(.x = c("x", "y", "z"),
.y = sampling,
.f = ~tibble(!!paste0(.x, "_random") :=
if_else(is.na(df[[.x]]), NA_integer_,
as.integer(seq_along(df[[.x]]) %in% sample(which(!is.na(df[[.x]])), size = .y))))))
# x y z x_random y_random z_random
#1 NA 0.02358698 0.222022714 NA 0 1
#2 0.15099912 NA 0.878007560 0 NA 0
#3 0.20228598 0.92222805 NA 0 0 NA
#4 0.10955137 0.68713928 0.485866574 1 1 1
#5 0.57361508 0.56205208 0.367087414 1 1 0
#6 0.30534642 0.75997029 0.006055428 0 0 1
#7 0.76949447 0.78142772 0.279323093 0 0 0
#8 0.07178739 0.73181961 0.187739444 0 0 1
#9 0.52645525 0.48321814 0.213029355 0 1 0
#10 0.30858707 0.20973381 0.450931534 0 0 0
另一个可能的解决方案,使用 purrr::imap_dfc
:
library(tidyverse)
mutate(df, imap_dfc(sampling, ~ +(1:nrow(df) %in% sample(setdiff(1:nrow(df),
which(is.na(df[, str_sub(.y, nchar(.y))]))), .x))) * ifelse(is.na(df), NA, 1))
#> x y z random_x random_y random_z
#> 1 NA 0.5784770 0.87429843 NA 0 0
#> 2 0.483728093 NA 0.87502533 0 NA 0
#> 3 0.294748405 0.3057474 NA 0 0 NA
#> 4 0.993350082 0.4282864 0.02936437 0 0 1
#> 5 0.344054454 0.4872465 0.65317911 0 0 1
#> 6 0.465265657 0.6721587 0.77952998 0 1 1
#> 7 0.659649583 0.9923243 0.01262495 1 1 0
#> 8 0.314616988 0.7686583 0.99389609 1 0 0
#> 9 0.009670492 0.1558185 0.73083388 0 0 0
#> 10 0.102769163 0.1543078 0.84348806 0 1 1
仅供参考,我是如何解决的。与@Ronak Shah 的解决方案非常相似,但这里和那里有点不同:
df %>%
add_column(map2_dfc(.x = c("x", "y", "z"),
.y = sampling,
.f = ~if_else(is.na(df[.x]),
NA_integer_,
as.integer(row_number(df[.x]) %in% sample(which(!is.na(df[.x])), size = .y)))) %>%
rename_with(.cols = everything(),
.fn = ~names(sampling)))
我很确定有办法到达那里,但我找不到。
我有一个包含多列的数据框。我现在想在从这些列 (0/1) 采样时向包含信息的数据框添加新列。如果我想从每列中采样相同数量的元素,我有一个 across
的整洁解决方案。当从每一列中采样不同数量的元素时,我也有一个(甚至更丑陋的)解决方案 across
,但我希望使用 purrr
有一个更简单的解决方案,我只提供列名作为一个参数和要作为另一个参数采样的元素数量,然后将获得我的新列。
有什么想法吗?
数据
df <- data.frame(x = runif(10),
y = runif(10),
z = runif(10))
df[1, 1] <- NA
df[2, 2] <- NA
df[3, 3] <- NA
sampling <- c(2, 3, 4)
names(sampling) <- c("random_x", "random_y", "random_z")
采样相同数量元素的解决方案
df %>%
mutate(across(everything(),
~if_else(is.na(.),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.)), size = 3))),
.names = "{.col}_random"))
采样不同数量元素的解决方案
df %>%
mutate(across(everything(),
~if_else(is.na(.),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.)), size = sampling[str_detect(names(sampling), paste0(cur_column(), "$"))]))),
.names = "{.col}_random"))
想要的咕噜声方式
……也许沿着这些路线?
df %>%
map2(.x = c("x", "y", "z"),
.y = sampling,
.f = ~if_else(is.na(.x),
NA_integer_,
as.integer(row_number() %in% sample(which(!is.na(.x)), size = .y))))
purrr 方式的问题显然是我没有使用正确的语法,因为我将字符向量作为 .x 而不是 df 中的列传递。
期望的输出
(抛开结果的随机性)
x y z x_random y_random z_random
1 NA 0.06686268 0.7663706 NA 0 0
2 0.7551366 NA 0.5550793 0 NA 1
3 0.7437531 0.61971712 NA 0 0 NA
4 0.5238451 0.57510689 0.7637622 1 0 0
5 0.9593917 0.17481769 0.4443493 0 0 0
6 0.2821633 0.86972254 0.2284449 0 0 0
7 0.3941531 0.61981285 0.8202302 0 0 1
8 0.1473573 0.58482156 0.9078447 0 1 1
9 0.7063327 0.77550907 0.9271699 1 1 1
10 0.6320678 0.06011700 0.2139956 0 1 0
- 如果将
.x
、.y
分别传递给map2
,则不应使用df %>% map2(...)
。 is.na(.x)
不正确,因为.x
是字符值(如"x"
、"y"
和"z"
)。我使用df[[.x]]
对值进行子集化。- 由于我们没有使用
df %>% ...
,所以row_number()
将不起作用,因此将其更改为seq_along
。
这是一种使用 map2_dfc
创建新列的方法,我们使用 bind_cols
将其绑定到原始数据框。
library(dplyr)
library(purrr)
bind_cols(df, map2_dfc(.x = c("x", "y", "z"),
.y = sampling,
.f = ~tibble(!!paste0(.x, "_random") :=
if_else(is.na(df[[.x]]), NA_integer_,
as.integer(seq_along(df[[.x]]) %in% sample(which(!is.na(df[[.x]])), size = .y))))))
# x y z x_random y_random z_random
#1 NA 0.02358698 0.222022714 NA 0 1
#2 0.15099912 NA 0.878007560 0 NA 0
#3 0.20228598 0.92222805 NA 0 0 NA
#4 0.10955137 0.68713928 0.485866574 1 1 1
#5 0.57361508 0.56205208 0.367087414 1 1 0
#6 0.30534642 0.75997029 0.006055428 0 0 1
#7 0.76949447 0.78142772 0.279323093 0 0 0
#8 0.07178739 0.73181961 0.187739444 0 0 1
#9 0.52645525 0.48321814 0.213029355 0 1 0
#10 0.30858707 0.20973381 0.450931534 0 0 0
另一个可能的解决方案,使用 purrr::imap_dfc
:
library(tidyverse)
mutate(df, imap_dfc(sampling, ~ +(1:nrow(df) %in% sample(setdiff(1:nrow(df),
which(is.na(df[, str_sub(.y, nchar(.y))]))), .x))) * ifelse(is.na(df), NA, 1))
#> x y z random_x random_y random_z
#> 1 NA 0.5784770 0.87429843 NA 0 0
#> 2 0.483728093 NA 0.87502533 0 NA 0
#> 3 0.294748405 0.3057474 NA 0 0 NA
#> 4 0.993350082 0.4282864 0.02936437 0 0 1
#> 5 0.344054454 0.4872465 0.65317911 0 0 1
#> 6 0.465265657 0.6721587 0.77952998 0 1 1
#> 7 0.659649583 0.9923243 0.01262495 1 1 0
#> 8 0.314616988 0.7686583 0.99389609 1 0 0
#> 9 0.009670492 0.1558185 0.73083388 0 0 0
#> 10 0.102769163 0.1543078 0.84348806 0 1 1
仅供参考,我是如何解决的。与@Ronak Shah 的解决方案非常相似,但这里和那里有点不同:
df %>%
add_column(map2_dfc(.x = c("x", "y", "z"),
.y = sampling,
.f = ~if_else(is.na(df[.x]),
NA_integer_,
as.integer(row_number(df[.x]) %in% sample(which(!is.na(df[.x])), size = .y)))) %>%
rename_with(.cols = everything(),
.fn = ~names(sampling)))