dplyr 通过评估查找单元格值来改变特定列
dplyr mutate specific columns by evaluating lookup cell value
我已经探索了使用 quosures、符号和求值的各种选项,但我似乎无法获得正确的语法。这是一个示例数据框。
data.frame("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
A B C D pastecols
1 a z a b B, C
2 b y c d B, D
3 c x e f B, C, D
4 d w g h <NA>
现在假设我想根据 pastecols 中的查找字符串粘贴不同列的值,并且我总是想包含 A 列。这是我想要的结果:
A B C D pastecols result
1 a z a b B, C a z a
2 b y c d B, D b y d
3 c x e f B, C, D c x e f
4 d w g h <NA> d
理想情况下,这可以在 dplyr 中完成。这是我得到的最接近的:
x %>% mutate(result = lapply(lapply(str_split(pastecols, ", "), c, "A"), na.omit))
A B C D pastecols result
1 a z a b B, C B, C, A
2 b y c d B, D B, D, A
3 c x e f B, C, D B, C, D, A
4 d w g h <NA> A
不是最优雅的解决方案,但仅使用基础 R 即可完成工作。如果列 A
从未出现在 pastecols
中,您可以从代码中删除 unique()
。
for(r in seq_len(nrow(df))) {
df$result[r] <- paste(
df[r, na.omit(unique(c("A", unlist(strsplit(df$pastecols[r], ", ")))))],
collapse = " "
)
}
df
A B C D pastecols result
1 a z a b B, C a z a
2 b y c d B, D b y d
3 c x e f B, C, D c x e f
4 d w g h <NA> d
数据-
df <- data.frame(
"A" = letters[1:4],
"B" = letters[26:23],
"C" = letters[c(1,3,5,7)],
"D" = letters[c(2,4,6,8)],
"pastecols" = c("B, C","B, D", "B, C, D", NA), stringsAsFactors = F
)
这是使用 pmap
做类似事情的一种方法。 pmap
可用于通过将每一行捕获为命名向量来有效地逐行处理数据帧;然后,您可以通过使用 ["pastecols"]
.
选择所需的列名称以将其索引为 cols
大多数匿名函数语法不是 tidyverse
的东西,而是基本的 R 东西。浏览它:
- 将数据帧作为列表传递给
pmap_chr
的 .l
参数。请记住,数据框是列的列表!
- 用
c(...)
捕获所有...
参数。基本上我们将数据帧的每一行作为函数的参数调用;现在 row
是包含该行的命名向量。请注意,如果您有列表列,这将中断,(但这里还有很多其他东西,所以我假设没有任何...)
- 我们可以从
row["pastecols"]
中得到我们想要的 row
的值,但是我们需要将(比如说)"B, C"
变成 c("A", "B", "C")
来做到这一点。下一行只是添加 "A"
,用 "A"
替换缺失值,如果有则拆分成多个部分,然后将索引返回到列表中。 [[
部分就是您在管道链中执行 list[[1]]"
的方式,它是运算符的前缀形式。您需要这个,因为 str_split
return 是一个列表,我们只需要向量。
- 使用此
cols
向量从 row
和 return 中获取所需的值,折叠成长度为 1 的字符向量!
library(tidyverse)
tbl <- tibble("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
tbl %>%
mutate(result = pmap_chr(
.l = .,
.f = function(...){
row <- c(...)
cols <- row["pastecols"] %>% str_c("A, ", .) %>% replace_na("A") %>% str_split(", ") %>% `[[`(1)
vals <- row[cols] %>% str_c(collapse = ", ")
return(vals)
}
))
#> # A tibble: 4 x 6
#> A B C D pastecols result
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 a z a b B, C a, z, a
#> 2 b y c d B, D b, y, d
#> 3 c x e f B, C, D c, x, e, f
#> 4 d w g h <NA> d
由 reprex package (v0.2.0) 创建于 2018-12-03。
这是一种不依赖于 apply
或 map
系列中的迭代函数的不同方式,如果您希望避免它们,并尝试利用 tidyr
tidyverse
的一侧。该方法基本上是将 gather
和 separate_rows
的数据框扩展到 pastecols
和实际列的每个组合,然后 filter
所以我们只保留每个匹配的那些rowid
。一旦我们有了它,我们就可以 group_by
和 summarise
将它恢复到每 rowid
一行。有一堆内务管理来处理你总是有列 A
的事实,请注意我在输出 pastecols
中留下了 A
,但如果你想,你可以删除它.
library(tidyverse)
tbl <- tibble("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
tbl %>%
rowid_to_column() %>%
mutate(
pastecols = str_c("A, ", pastecols),
pastecols = if_else(is.na(pastecols), "A", pastecols)
) %>%
gather(colname, value, -pastecols, -rowid) %>%
separate_rows(pastecols) %>%
filter(pastecols == colname) %>%
group_by(rowid) %>%
summarise(
pastecols = str_c(pastecols, collapse = ", "),
result = str_c(value, collapse = ", ")
)
#> # A tibble: 4 x 3
#> rowid pastecols result
#> <int> <chr> <chr>
#> 1 1 A, B, C a, z, a
#> 2 2 A, B, D b, y, d
#> 3 3 A, B, C, D c, x, e, f
#> 4 4 A d
由 reprex package (v0.2.0) 创建于 2018-12-03。
我已经探索了使用 quosures、符号和求值的各种选项,但我似乎无法获得正确的语法。这是一个示例数据框。
data.frame("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
A B C D pastecols
1 a z a b B, C
2 b y c d B, D
3 c x e f B, C, D
4 d w g h <NA>
现在假设我想根据 pastecols 中的查找字符串粘贴不同列的值,并且我总是想包含 A 列。这是我想要的结果:
A B C D pastecols result
1 a z a b B, C a z a
2 b y c d B, D b y d
3 c x e f B, C, D c x e f
4 d w g h <NA> d
理想情况下,这可以在 dplyr 中完成。这是我得到的最接近的:
x %>% mutate(result = lapply(lapply(str_split(pastecols, ", "), c, "A"), na.omit))
A B C D pastecols result
1 a z a b B, C B, C, A
2 b y c d B, D B, D, A
3 c x e f B, C, D B, C, D, A
4 d w g h <NA> A
不是最优雅的解决方案,但仅使用基础 R 即可完成工作。如果列 A
从未出现在 pastecols
中,您可以从代码中删除 unique()
。
for(r in seq_len(nrow(df))) {
df$result[r] <- paste(
df[r, na.omit(unique(c("A", unlist(strsplit(df$pastecols[r], ", ")))))],
collapse = " "
)
}
df
A B C D pastecols result
1 a z a b B, C a z a
2 b y c d B, D b y d
3 c x e f B, C, D c x e f
4 d w g h <NA> d
数据-
df <- data.frame(
"A" = letters[1:4],
"B" = letters[26:23],
"C" = letters[c(1,3,5,7)],
"D" = letters[c(2,4,6,8)],
"pastecols" = c("B, C","B, D", "B, C, D", NA), stringsAsFactors = F
)
这是使用 pmap
做类似事情的一种方法。 pmap
可用于通过将每一行捕获为命名向量来有效地逐行处理数据帧;然后,您可以通过使用 ["pastecols"]
.
cols
大多数匿名函数语法不是 tidyverse
的东西,而是基本的 R 东西。浏览它:
- 将数据帧作为列表传递给
pmap_chr
的.l
参数。请记住,数据框是列的列表! - 用
c(...)
捕获所有...
参数。基本上我们将数据帧的每一行作为函数的参数调用;现在row
是包含该行的命名向量。请注意,如果您有列表列,这将中断,(但这里还有很多其他东西,所以我假设没有任何...) - 我们可以从
row["pastecols"]
中得到我们想要的row
的值,但是我们需要将(比如说)"B, C"
变成c("A", "B", "C")
来做到这一点。下一行只是添加"A"
,用"A"
替换缺失值,如果有则拆分成多个部分,然后将索引返回到列表中。[[
部分就是您在管道链中执行list[[1]]"
的方式,它是运算符的前缀形式。您需要这个,因为str_split
return 是一个列表,我们只需要向量。 - 使用此
cols
向量从row
和 return 中获取所需的值,折叠成长度为 1 的字符向量!
library(tidyverse)
tbl <- tibble("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
tbl %>%
mutate(result = pmap_chr(
.l = .,
.f = function(...){
row <- c(...)
cols <- row["pastecols"] %>% str_c("A, ", .) %>% replace_na("A") %>% str_split(", ") %>% `[[`(1)
vals <- row[cols] %>% str_c(collapse = ", ")
return(vals)
}
))
#> # A tibble: 4 x 6
#> A B C D pastecols result
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 a z a b B, C a, z, a
#> 2 b y c d B, D b, y, d
#> 3 c x e f B, C, D c, x, e, f
#> 4 d w g h <NA> d
由 reprex package (v0.2.0) 创建于 2018-12-03。
这是一种不依赖于 apply
或 map
系列中的迭代函数的不同方式,如果您希望避免它们,并尝试利用 tidyr
tidyverse
的一侧。该方法基本上是将 gather
和 separate_rows
的数据框扩展到 pastecols
和实际列的每个组合,然后 filter
所以我们只保留每个匹配的那些rowid
。一旦我们有了它,我们就可以 group_by
和 summarise
将它恢复到每 rowid
一行。有一堆内务管理来处理你总是有列 A
的事实,请注意我在输出 pastecols
中留下了 A
,但如果你想,你可以删除它.
library(tidyverse)
tbl <- tibble("A" = letters[1:4], "B" = letters[26:23], "C" = letters[c(1,3,5,7)], "D" = letters[c(2,4,6,8)], "pastecols" = c("B, C","B, D", "B, C, D", NA))
tbl %>%
rowid_to_column() %>%
mutate(
pastecols = str_c("A, ", pastecols),
pastecols = if_else(is.na(pastecols), "A", pastecols)
) %>%
gather(colname, value, -pastecols, -rowid) %>%
separate_rows(pastecols) %>%
filter(pastecols == colname) %>%
group_by(rowid) %>%
summarise(
pastecols = str_c(pastecols, collapse = ", "),
result = str_c(value, collapse = ", ")
)
#> # A tibble: 4 x 3
#> rowid pastecols result
#> <int> <chr> <chr>
#> 1 1 A, B, C a, z, a
#> 2 2 A, B, D b, y, d
#> 3 3 A, B, C, D c, x, e, f
#> 4 4 A d
由 reprex package (v0.2.0) 创建于 2018-12-03。