如何从具有相同前缀的 .y 中减去多个 .x
How to substract multiple .x from .y with same prefixes
我有这个问题:
# A tibble: 2 x 8
a.x b.x c.x d.x a.y b.y c.y d.y
<int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2
2 17 11 0 0 16 2 0 0
df <- structure(list(a.x = c(13L, 17L), b.x = c(13L, 11L), c.x = c(12L,
0L), d.x = c(11L, 0L), a.y = c(7L, 16L), b.y = 1:2, c.y = c(4L,
0L), d.y = c(2L, 0L)), row.names = c(NA, -2L), class = c("tbl_df",
"tbl", "data.frame"))
我要计算: a.x - a.y
, b.x - b.y
, c.x - c.y
,等等....
我想要的输出:
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
我可以通过以下方式实现:
df %>%
mutate(a = a.x-a.y,
b = b.x-b.y,
c = c.x-c.y,
d = d.x-d.y)
我想学:
- 如何提取新列名称的前缀。
- 自动计算
.x - .y
。
使用 cur_column
的一种方法 - 遍历 ends_with
.x
的列,通过更改 [=27] 替换列名 (cur_column()
) 中的子字符串=] 到 'y', get
的值,减去并更改 .names
中的列名
library(dplyr)
library(stringr)
df %>%
mutate(across(ends_with('.x'),
~ . - get(str_replace(cur_column(), 'x', 'y')),
.names = "{str_remove(.col, fixed('.x'))}"))
-输出
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
或通过 pivot_longer
重塑
library(tidyr)
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value"),
names_pattern = "(.)\..*") %>%
group_by(rn) %>%
summarise(across(everything(), ~ -diff(.))) %>%
select(-rn) %>%
bind_cols(df, .)
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
我在 github {dplyover} 上有一个用于此类操作的程序包。我们可以使用 dplyover::across2
来计算。如果我们在.names
参数中指定"{pre}"
,我们可以提取每对变量的公共前缀。
常规 {dplyr} 解决方案的主要优点是我们不一定需要具有相似名称的列。缺点是 across2
的性能不如 dplyr::across
.
library(dplyr)
library(dplyover) # https://github.com/TimTeaFan/dplyover
df %>%
mutate(across2(ends_with(".x"),
ends_with(".y"),
~ .x - .y,
.names = "{pre}"))
#> # A tibble: 2 x 12
#> a.x b.x c.x d.x a.y b.y c.y d.y a b c d
#> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 13 13 12 11 7 1 4 2 6 12 8 9
#> 2 17 11 0 0 16 2 0 0 1 9 0 0
由 reprex package (v0.3.0)
于 2021-07-26 创建
一个 base R 适合你的方法:
cbind(df, mapply(\(x, y) x - y, df[endsWith(names(df), ".x")],
df[endsWith(names(df), ".y")]) |>
as.data.frame() |>
setNames(letters[seq_len(ncol(df)/2)]))
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
类似的tidyverse
解决方案:
library(dplyr)
library(purrr)
df %>%
bind_cols(
map2_df(".x", ".y", ~ df[grepl(.x, names(df))] - df[grepl(.y, names(df))]) %>%
rename_with(~ gsub(".x", "", .), everything())
)
亲爱的 @Henrik
建议的一种非常简单紧凑的方法
cbind(df, setNames(df[endsWith(names(df), ".x")] - df[endsWith(names(df), ".y")],
sub("\..*","", names(df[endsWith(names(df), ".x")]))))
另一种方法是:
df %>%
mutate(across(ends_with('x'), .names = "{str_remove(.col, '.x')}")
- across(ends_with('y')))
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
在基础 R 中,您可以使用 split.default
:
a <- do.call('-', split.default(df, sub('.', '', names(df))))
cbind(df, setNames(a, sub('..$', '', names(a))))
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
我有这个问题:
# A tibble: 2 x 8
a.x b.x c.x d.x a.y b.y c.y d.y
<int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2
2 17 11 0 0 16 2 0 0
df <- structure(list(a.x = c(13L, 17L), b.x = c(13L, 11L), c.x = c(12L,
0L), d.x = c(11L, 0L), a.y = c(7L, 16L), b.y = 1:2, c.y = c(4L,
0L), d.y = c(2L, 0L)), row.names = c(NA, -2L), class = c("tbl_df",
"tbl", "data.frame"))
我要计算: a.x - a.y
, b.x - b.y
, c.x - c.y
,等等....
我想要的输出:
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
我可以通过以下方式实现:
df %>%
mutate(a = a.x-a.y,
b = b.x-b.y,
c = c.x-c.y,
d = d.x-d.y)
我想学:
- 如何提取新列名称的前缀。
- 自动计算
.x - .y
。
使用 cur_column
的一种方法 - 遍历 ends_with
.x
的列,通过更改 [=27] 替换列名 (cur_column()
) 中的子字符串=] 到 'y', get
的值,减去并更改 .names
library(dplyr)
library(stringr)
df %>%
mutate(across(ends_with('.x'),
~ . - get(str_replace(cur_column(), 'x', 'y')),
.names = "{str_remove(.col, fixed('.x'))}"))
-输出
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
或通过 pivot_longer
library(tidyr)
df %>%
mutate(rn = row_number()) %>%
pivot_longer(cols = -rn, names_to = c(".value"),
names_pattern = "(.)\..*") %>%
group_by(rn) %>%
summarise(across(everything(), ~ -diff(.))) %>%
select(-rn) %>%
bind_cols(df, .)
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
我在 github {dplyover} 上有一个用于此类操作的程序包。我们可以使用 dplyover::across2
来计算。如果我们在.names
参数中指定"{pre}"
,我们可以提取每对变量的公共前缀。
常规 {dplyr} 解决方案的主要优点是我们不一定需要具有相似名称的列。缺点是 across2
的性能不如 dplyr::across
.
library(dplyr)
library(dplyover) # https://github.com/TimTeaFan/dplyover
df %>%
mutate(across2(ends_with(".x"),
ends_with(".y"),
~ .x - .y,
.names = "{pre}"))
#> # A tibble: 2 x 12
#> a.x b.x c.x d.x a.y b.y c.y d.y a b c d
#> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 13 13 12 11 7 1 4 2 6 12 8 9
#> 2 17 11 0 0 16 2 0 0 1 9 0 0
由 reprex package (v0.3.0)
于 2021-07-26 创建一个 base R 适合你的方法:
cbind(df, mapply(\(x, y) x - y, df[endsWith(names(df), ".x")],
df[endsWith(names(df), ".y")]) |>
as.data.frame() |>
setNames(letters[seq_len(ncol(df)/2)]))
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
类似的tidyverse
解决方案:
library(dplyr)
library(purrr)
df %>%
bind_cols(
map2_df(".x", ".y", ~ df[grepl(.x, names(df))] - df[grepl(.y, names(df))]) %>%
rename_with(~ gsub(".x", "", .), everything())
)
亲爱的 @Henrik
建议的一种非常简单紧凑的方法cbind(df, setNames(df[endsWith(names(df), ".x")] - df[endsWith(names(df), ".y")],
sub("\..*","", names(df[endsWith(names(df), ".x")]))))
另一种方法是:
df %>%
mutate(across(ends_with('x'), .names = "{str_remove(.col, '.x')}")
- across(ends_with('y')))
# A tibble: 2 x 12
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0
在基础 R 中,您可以使用 split.default
:
a <- do.call('-', split.default(df, sub('.', '', names(df))))
cbind(df, setNames(a, sub('..$', '', names(a))))
a.x b.x c.x d.x a.y b.y c.y d.y a b c d
1 13 13 12 11 7 1 4 2 6 12 8 9
2 17 11 0 0 16 2 0 0 1 9 0 0