平行pivot_longer 两组列
parallel pivot_longer of two sets of columns
我有以下数据框:
library(tidyverse)
dat <- tribble(
~Scenario, ~V1, ~V2, ~V3, ~V4,
1, 0.97, 0.46, 0.79, 0.25,
1, 0.21, 0.45, 0.23, 0.63,
1, 0.95, 0.97, 0.07, 0.61,
1, 0.93, 0.79, 0.23, 0.86,
2, 0.22, 0.01, 0.42, 0.47,
2, 0.71, 0.17, 0.16, 0.88,
3, 0.73, 0.38, 0.10, 0.77,
3, 0.49, 0.37, 0.90, 0.52,
3, 0.99, 0.71, 0.66, 0.05,
3, 0.72, 0.75, 0.69, 0.01,
3, 0.15, 0.87, 0.12, 0.02,
4, 0.94, 0.30, 0.91, 0.99)
我要向此数据添加四个新列,其中每个新列代表按 Scenario
:
分组的每个 V1:V4 列的总和
dat_new <- dat %>%
group_by(Scenario) %>%
mutate_at(vars(-group_cols()), .funs = list(sum = sum))
现在我想将这些数据转换为长格式,其中一组是我的 V1:V4 列,第二组是我的 V1_sum:V4_sum 列。正常的 pivot_longer
不起作用,因为它只接受一个值列,但是,我需要两个。
我在 tidyverse 参考(最底部的示例)中找到了一个潜在的解决方案,但我未能定义正确的 names_pattern
。 https://tidyr.tidyverse.org/reference/pivot_longer.html
有没有一种简单直接的方法(最好是 tidyverse)我可以只做这两个 pivot_longer 组?感觉这是重塑数据集时的基本任务之一,但我无法让它工作。
预期输出:
Scenario set V sum
1 1 0.97 3.06
1 2 0.46 2.67
1 3 0.79 1.32
1 4 0.25 2.35
...
4 4 0.99 0.99
注意: 列名 "set"、"V" 和 "sum" 只是示例,我'如果其他列名更容易即时生成,我可以使用它们。
也许你应该在计算总和之前尝试旋转:
dat %>% pivot_longer(., -Scenario, names_to = "V", values_to = "Value") %>%
group_by(Scenario, V) %>%
mutate(Sum = sum(Value))
# A tibble: 48 x 4
# Groups: Scenario, V [16]
Scenario V Value Sum
<dbl> <chr> <dbl> <dbl>
1 1 V1 0.97 3.06
2 1 V2 0.46 2.67
3 1 V3 0.79 1.32
4 1 V4 0.25 2.35
5 1 V1 0.21 3.06
6 1 V2 0.45 2.67
7 1 V3 0.23 1.32
8 1 V4 0.63 2.35
9 1 V1 0.95 3.06
10 1 V2 0.97 2.67
# … with 38 more rows
是您要找的吗?
我在 vignette("pivot")
中的“每行多个观察”标题下找到了这个解决方案。
每组变量需要一个平行的命名结构,并且需要一个分隔符。
在你的情况下(这不是很普遍),从 mutate_at
函数出来,你得到 _sum
附加到每个变量。但是您需要在前四个变量的下划线之后添加一些内容。我用标签 _orig
重命名了您原来的四个变量,并且为了避免双下划线而粗略地将总和变量的名称从 V1_orig_sum
简化为 V1_sum
。
dat <- tribble(
~Scenario, ~V1_orig, ~V2_orig, ~V3_orig, ~V4_orig,
1, 0.97, 0.46, 0.79, 0.25,
1, 0.21, 0.45, 0.23, 0.63,
1, 0.95, 0.97, 0.07, 0.61,
1, 0.93, 0.79, 0.23, 0.86,
2, 0.22, 0.01, 0.42, 0.47,
2, 0.71, 0.17, 0.16, 0.88,
3, 0.73, 0.38, 0.10, 0.77,
3, 0.49, 0.37, 0.90, 0.52,
3, 0.99, 0.71, 0.66, 0.05,
3, 0.72, 0.75, 0.69, 0.01,
3, 0.15, 0.87, 0.12, 0.02,
4, 0.94, 0.30, 0.91, 0.99)
dat_new <- dat %>%
group_by(Scenario) %>%
mutate_at(vars(-group_cols()), .funs = list(sum = sum))%>%
rename(V1_sum=V1_orig_sum, V2_sum=V2_orig_sum, V3_sum=V3_orig_sum, V4_sum=V4_orig_sum)
所以这里 dat_new
看起来有两组,每组四个变量,变量名在 _
之前,描述符在:
之后
> head(dat_new)
# A tibble: 6 x 9
# Groups: Scenario [2]
Scenario V1_orig V2_orig V3_orig V4_orig V1_orig_sum V2_orig_sum V3_orig_sum V4_orig_sum
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0.97 0.46 0.79 0.25 3.06 2.67 1.32 2.35
2 1 0.21 0.45 0.23 0.63 3.06 2.67 1.32 2.35
3 1 0.95 0.97 0.07 0.61 3.06 2.67 1.32 2.35
4 1 0.93 0.79 0.23 0.86 3.06 2.67 1.32 2.35
5 2 0.22 0.01 0.42 0.47 0.93 0.18 0.58 1.35
6 2 0.71 0.17 0.16 0.88 0.93 0.18 0.58 1.35
然后你调用 pivot_longer
但在 names_to
参数中给它 两个 列名:一个是你所说的这些objects(在本例中为 Var
)和 ".value"
表示应该有尽可能多的新列,在 _
分隔符之后有唯一标记:
dat_new %>%
pivot_longer(-Scenario,
names_to = c("Var", ".value"),
names_sep="_" )
# A tibble: 48 x 4
# Groups: Scenario [4]
Scenario Var orig sum
<dbl> <chr> <dbl> <dbl>
1 1 V1 0.97 3.06
2 1 V2 0.46 2.67
3 1 V3 0.79 1.32
4 1 V4 0.25 2.35
5 1 V1 0.21 3.06
6 1 V2 0.45 2.67
7 1 V3 0.23 1.32
8 1 V4 0.63 2.35
9 1 V1 0.95 3.06
10 1 V2 0.97 2.67
# ... with 38 more rows
请注意,如果您将 8 个变量设置为反向命名系统(即 orig_V1、sum_V1 等),您可以颠倒命名参数中的顺序:names_to=c(".value", "Var)
。此解决方案不要求值是同一类型(即 orig 和 sum 在这里都是数字,但一个可能是数字,一个是字符)。
出乎意料的困难。我认为也许最简单的方法是将数据转换为最长格式,然后再重组为 2 组。这是 R 中给出的 Anscombe's quartet 的示例:
> anscombe
x1 x2 x3 x4 y1 y2 y3 y4
1 10 10 10 8 8.0 9.1 7.5 6.6
2 8 8 8 8 7.0 8.1 6.8 5.8
3 13 13 13 8 7.6 8.7 12.7 7.7
4 9 9 9 8 8.8 8.8 7.1 8.8
5 11 11 11 8 8.3 9.3 7.8 8.5
6 14 14 14 8 10.0 8.1 8.8 7.0
7 6 6 6 8 7.2 6.1 6.1 5.2
8 4 4 4 19 4.3 3.1 5.4 12.5
9 12 12 12 8 10.8 9.1 8.2 5.6
10 7 7 7 8 4.8 7.3 6.4 7.9
11 5 5 5 8 5.7 4.7 5.7 6.9
所以我们看到有 2 组变量,每组 4 个。我们想要一个只有 3 个变量的数据集:x、y 和系列,后者仅指示值属于 4 个中的哪一组。这是我的做法:
> anscombe %>%
+ mutate(row = 1:n()) %>%
+ pivot_longer(cols = -row) %>%
+ separate(col = name, into = c("var", "series"), sep = 1) %>%
+ pivot_wider(id_cols = c(row, series), names_from = "var", values_from = "value") %>% print(n=Inf)
# A tibble: 44 × 4
row series x y
<int> <chr> <dbl> <dbl>
1 1 1 10 8.04
2 1 2 10 9.14
3 1 3 10 7.46
4 1 4 8 6.58
5 2 1 8 6.95
6 2 2 8 8.14
7 2 3 8 6.77
8 2 4 8 5.76
9 3 1 13 7.58
10 3 2 13 8.74
11 3 3 13 12.7
12 3 4 8 7.71
13 4 1 9 8.81
14 4 2 9 8.77
15 4 3 9 7.11
16 4 4 8 8.84
17 5 1 11 8.33
18 5 2 11 9.26
19 5 3 11 7.81
20 5 4 8 8.47
21 6 1 14 9.96
22 6 2 14 8.1
23 6 3 14 8.84
24 6 4 8 7.04
25 7 1 6 7.24
26 7 2 6 6.13
27 7 3 6 6.08
28 7 4 8 5.25
29 8 1 4 4.26
30 8 2 4 3.1
31 8 3 4 5.39
32 8 4 19 12.5
33 9 1 12 10.8
34 9 2 12 9.13
35 9 3 12 8.15
36 9 4 8 5.56
37 10 1 7 4.82
38 10 2 7 7.26
39 10 3 7 6.42
40 10 4 8 7.91
41 11 1 5 5.68
42 11 2 5 4.74
43 11 3 5 5.73
44 11 4 8 6.89
一步一步:
- 添加一个只是行号的 id 列。
- 将除 id 以外的所有内容转为长格式。
- 将 x1...x4 和 y1...y4 分成两列。
- 恢复为 x 和 y 两列的宽格式。
我们可以绘制数据以确保我们做对了:
#data from above)
ggplot(aes(x, y)) +
geom_point() +
geom_smooth(method = "lm", se = F) +
facet_wrap("series") +
ggtitle("Anscombe's quartlet: Same correlations but different data")
我有以下数据框:
library(tidyverse)
dat <- tribble(
~Scenario, ~V1, ~V2, ~V3, ~V4,
1, 0.97, 0.46, 0.79, 0.25,
1, 0.21, 0.45, 0.23, 0.63,
1, 0.95, 0.97, 0.07, 0.61,
1, 0.93, 0.79, 0.23, 0.86,
2, 0.22, 0.01, 0.42, 0.47,
2, 0.71, 0.17, 0.16, 0.88,
3, 0.73, 0.38, 0.10, 0.77,
3, 0.49, 0.37, 0.90, 0.52,
3, 0.99, 0.71, 0.66, 0.05,
3, 0.72, 0.75, 0.69, 0.01,
3, 0.15, 0.87, 0.12, 0.02,
4, 0.94, 0.30, 0.91, 0.99)
我要向此数据添加四个新列,其中每个新列代表按 Scenario
:
dat_new <- dat %>%
group_by(Scenario) %>%
mutate_at(vars(-group_cols()), .funs = list(sum = sum))
现在我想将这些数据转换为长格式,其中一组是我的 V1:V4 列,第二组是我的 V1_sum:V4_sum 列。正常的 pivot_longer
不起作用,因为它只接受一个值列,但是,我需要两个。
我在 tidyverse 参考(最底部的示例)中找到了一个潜在的解决方案,但我未能定义正确的 names_pattern
。 https://tidyr.tidyverse.org/reference/pivot_longer.html
有没有一种简单直接的方法(最好是 tidyverse)我可以只做这两个 pivot_longer 组?感觉这是重塑数据集时的基本任务之一,但我无法让它工作。
预期输出:
Scenario set V sum
1 1 0.97 3.06
1 2 0.46 2.67
1 3 0.79 1.32
1 4 0.25 2.35
...
4 4 0.99 0.99
注意: 列名 "set"、"V" 和 "sum" 只是示例,我'如果其他列名更容易即时生成,我可以使用它们。
也许你应该在计算总和之前尝试旋转:
dat %>% pivot_longer(., -Scenario, names_to = "V", values_to = "Value") %>%
group_by(Scenario, V) %>%
mutate(Sum = sum(Value))
# A tibble: 48 x 4
# Groups: Scenario, V [16]
Scenario V Value Sum
<dbl> <chr> <dbl> <dbl>
1 1 V1 0.97 3.06
2 1 V2 0.46 2.67
3 1 V3 0.79 1.32
4 1 V4 0.25 2.35
5 1 V1 0.21 3.06
6 1 V2 0.45 2.67
7 1 V3 0.23 1.32
8 1 V4 0.63 2.35
9 1 V1 0.95 3.06
10 1 V2 0.97 2.67
# … with 38 more rows
是您要找的吗?
我在 vignette("pivot")
中的“每行多个观察”标题下找到了这个解决方案。
每组变量需要一个平行的命名结构,并且需要一个分隔符。
在你的情况下(这不是很普遍),从 mutate_at
函数出来,你得到 _sum
附加到每个变量。但是您需要在前四个变量的下划线之后添加一些内容。我用标签 _orig
重命名了您原来的四个变量,并且为了避免双下划线而粗略地将总和变量的名称从 V1_orig_sum
简化为 V1_sum
。
dat <- tribble(
~Scenario, ~V1_orig, ~V2_orig, ~V3_orig, ~V4_orig,
1, 0.97, 0.46, 0.79, 0.25,
1, 0.21, 0.45, 0.23, 0.63,
1, 0.95, 0.97, 0.07, 0.61,
1, 0.93, 0.79, 0.23, 0.86,
2, 0.22, 0.01, 0.42, 0.47,
2, 0.71, 0.17, 0.16, 0.88,
3, 0.73, 0.38, 0.10, 0.77,
3, 0.49, 0.37, 0.90, 0.52,
3, 0.99, 0.71, 0.66, 0.05,
3, 0.72, 0.75, 0.69, 0.01,
3, 0.15, 0.87, 0.12, 0.02,
4, 0.94, 0.30, 0.91, 0.99)
dat_new <- dat %>%
group_by(Scenario) %>%
mutate_at(vars(-group_cols()), .funs = list(sum = sum))%>%
rename(V1_sum=V1_orig_sum, V2_sum=V2_orig_sum, V3_sum=V3_orig_sum, V4_sum=V4_orig_sum)
所以这里 dat_new
看起来有两组,每组四个变量,变量名在 _
之前,描述符在:
> head(dat_new)
# A tibble: 6 x 9
# Groups: Scenario [2]
Scenario V1_orig V2_orig V3_orig V4_orig V1_orig_sum V2_orig_sum V3_orig_sum V4_orig_sum
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0.97 0.46 0.79 0.25 3.06 2.67 1.32 2.35
2 1 0.21 0.45 0.23 0.63 3.06 2.67 1.32 2.35
3 1 0.95 0.97 0.07 0.61 3.06 2.67 1.32 2.35
4 1 0.93 0.79 0.23 0.86 3.06 2.67 1.32 2.35
5 2 0.22 0.01 0.42 0.47 0.93 0.18 0.58 1.35
6 2 0.71 0.17 0.16 0.88 0.93 0.18 0.58 1.35
然后你调用 pivot_longer
但在 names_to
参数中给它 两个 列名:一个是你所说的这些objects(在本例中为 Var
)和 ".value"
表示应该有尽可能多的新列,在 _
分隔符之后有唯一标记:
dat_new %>%
pivot_longer(-Scenario,
names_to = c("Var", ".value"),
names_sep="_" )
# A tibble: 48 x 4
# Groups: Scenario [4]
Scenario Var orig sum
<dbl> <chr> <dbl> <dbl>
1 1 V1 0.97 3.06
2 1 V2 0.46 2.67
3 1 V3 0.79 1.32
4 1 V4 0.25 2.35
5 1 V1 0.21 3.06
6 1 V2 0.45 2.67
7 1 V3 0.23 1.32
8 1 V4 0.63 2.35
9 1 V1 0.95 3.06
10 1 V2 0.97 2.67
# ... with 38 more rows
请注意,如果您将 8 个变量设置为反向命名系统(即 orig_V1、sum_V1 等),您可以颠倒命名参数中的顺序:names_to=c(".value", "Var)
。此解决方案不要求值是同一类型(即 orig 和 sum 在这里都是数字,但一个可能是数字,一个是字符)。
出乎意料的困难。我认为也许最简单的方法是将数据转换为最长格式,然后再重组为 2 组。这是 R 中给出的 Anscombe's quartet 的示例:
> anscombe
x1 x2 x3 x4 y1 y2 y3 y4
1 10 10 10 8 8.0 9.1 7.5 6.6
2 8 8 8 8 7.0 8.1 6.8 5.8
3 13 13 13 8 7.6 8.7 12.7 7.7
4 9 9 9 8 8.8 8.8 7.1 8.8
5 11 11 11 8 8.3 9.3 7.8 8.5
6 14 14 14 8 10.0 8.1 8.8 7.0
7 6 6 6 8 7.2 6.1 6.1 5.2
8 4 4 4 19 4.3 3.1 5.4 12.5
9 12 12 12 8 10.8 9.1 8.2 5.6
10 7 7 7 8 4.8 7.3 6.4 7.9
11 5 5 5 8 5.7 4.7 5.7 6.9
所以我们看到有 2 组变量,每组 4 个。我们想要一个只有 3 个变量的数据集:x、y 和系列,后者仅指示值属于 4 个中的哪一组。这是我的做法:
> anscombe %>%
+ mutate(row = 1:n()) %>%
+ pivot_longer(cols = -row) %>%
+ separate(col = name, into = c("var", "series"), sep = 1) %>%
+ pivot_wider(id_cols = c(row, series), names_from = "var", values_from = "value") %>% print(n=Inf)
# A tibble: 44 × 4
row series x y
<int> <chr> <dbl> <dbl>
1 1 1 10 8.04
2 1 2 10 9.14
3 1 3 10 7.46
4 1 4 8 6.58
5 2 1 8 6.95
6 2 2 8 8.14
7 2 3 8 6.77
8 2 4 8 5.76
9 3 1 13 7.58
10 3 2 13 8.74
11 3 3 13 12.7
12 3 4 8 7.71
13 4 1 9 8.81
14 4 2 9 8.77
15 4 3 9 7.11
16 4 4 8 8.84
17 5 1 11 8.33
18 5 2 11 9.26
19 5 3 11 7.81
20 5 4 8 8.47
21 6 1 14 9.96
22 6 2 14 8.1
23 6 3 14 8.84
24 6 4 8 7.04
25 7 1 6 7.24
26 7 2 6 6.13
27 7 3 6 6.08
28 7 4 8 5.25
29 8 1 4 4.26
30 8 2 4 3.1
31 8 3 4 5.39
32 8 4 19 12.5
33 9 1 12 10.8
34 9 2 12 9.13
35 9 3 12 8.15
36 9 4 8 5.56
37 10 1 7 4.82
38 10 2 7 7.26
39 10 3 7 6.42
40 10 4 8 7.91
41 11 1 5 5.68
42 11 2 5 4.74
43 11 3 5 5.73
44 11 4 8 6.89
一步一步:
- 添加一个只是行号的 id 列。
- 将除 id 以外的所有内容转为长格式。
- 将 x1...x4 和 y1...y4 分成两列。
- 恢复为 x 和 y 两列的宽格式。
我们可以绘制数据以确保我们做对了:
#data from above)
ggplot(aes(x, y)) +
geom_point() +
geom_smooth(method = "lm", se = F) +
facet_wrap("series") +
ggtitle("Anscombe's quartlet: Same correlations but different data")