Unlist/unnest 将列分成几列
Unlist/unnest list column into several columns
我知道在数据框中取消嵌套列表列的问题已被提出并多次回答。但是,这可能是 237. 这类问题。
我有以下数据:
set.seed(666)
dat <- data.frame(sysRespNum = c(1,2,3,4,5,6),
product1 = sqrt(rnorm(6, 20, 5)^2),
product2 = sqrt(rnorm(6, 20, 5)^2),
product3 = sqrt(rnorm(6, 20, 5)^2))
数据:
sysRespNum product1 product2 product3
1 1 23.766555 13.46907 24.32327
2 2 30.071773 15.98740 11.39922
3 3 18.224328 11.03880 20.67063
4 4 30.140839 19.78984 19.62087
5 5 8.915628 30.75021 24.29150
6 6 23.791981 11.14885 21.72450
现在,我想计算每个产品占所有产品总和的比例,所以我想计算product1/sum(my three products)
,然后产品2和3也一样。所以我期待三个新的列。
我试过以下方法:
library(tidyverse)
dat %>%
mutate(sum_Product = apply(across(-sysRespNum), 1, function(x) list(sum_Product = x/sum(x))))
(附带问题:是否有更直接的方法可以直接对其进行变异而无需创建列表。我现在可以先创建一个总和列,然后再进行简单的变异和跨越。但是我'我想知道是否可以在不先创建临时总和列的情况下实现计算)
现在我的问题是很难解除 sum_Product
列表列的嵌套。 unnest_wider
不起作用,sum_Product
列仍然是一个列表。
所以唯一对我有用的是
- 遵循此解决方案:
- 更改上面的代码并将
list
部分替换为 data.frame
:
完整代码:
dat %>%
mutate(sum_Product = apply(across(-sysRespNum), 1, function(x) data.frame(sum_Product = x/sum(x)))) %>%
unnest(cols = everything()) %>%
mutate(product = rep(1:3, nrow(.)/3)) %>%
pivot_wider(values_from = sum_Product,
names_from = product,
names_prefix = "share_product")
给出正确的结果:
# A tibble: 6 x 7
sysRespNum product1 product2 product3 share_product1 share_product2
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 23.8 13.5 24.3 0.386 0.219
2 2 30.1 16.0 11.4 0.523 0.278
3 3 18.2 11.0 20.7 0.365 0.221
4 4 30.1 19.8 19.6 0.433 0.285
5 5 8.92 30.8 24.3 0.139 0.481
6 6 23.8 11.1 21.7 0.420 0.197
# … with 1 more variable: share_product3 <dbl>
然而,用pivot_wider.pivot_wider.
unnest 一切然后reshape 感觉没必要复杂
那么 a) 是否有更优雅的方法来计算我的份额变量 b) 是否有更 elegant/shorter/less 将列表列重塑为多个向量列的更冗长的方法?
这样做更容易 rowSums
,即将以关键字 'product' 开头的列上的 'product1' 除以 rowSums
。而不是用 c_across
做 rowwise
,这是矢量化的,应该也很快
library(dplyr)
dat %>%
mutate(sum_product = product1/rowSums(select(., starts_with('product'))))
注意:混合了 base R
代码 (apply
) 和带有 across
的 tidyverse 选项,这似乎不是最佳方式
如果我们需要对所有 'product' 列执行此操作,请先使用 mutate
创建一个 sum
列,然后在以 across
开头的列上使用 across
'product' 将列除以 'Sum_col'
dat %>%
mutate(Sum_col = rowSums(select(., starts_with('product'))),
across(starts_with('product'),
~ ./Sum_col, .names = '{.col}_sum_product')) %>%
select(-Sum_col)
-输出
#ysRespNum product1 product2 product3 product1_sum_product product2_sum_product product3_sum_product
#1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998 0.3951219
#2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431 0.1983909
#3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688 0.4139610
#4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348 0.2821054
#5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925 0.3798079
#6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490 0.3833826
或使用base R
nm1 <- startsWith(names(dat), 'product')
dat[paste0('sum_product', seq_along(nm1))] <- dat[nm1]/rowSums(dat[nm1])
我想下面的基本 R 代码应该适合你
cbind(
dat,
setNames(dat[-1] / rowSums(dat[-1]), paste0("share_product", seq_along(dat[-1])))
)
这给出了
sysRespNum product1 product2 product3 share_product1 share_product2
1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998
2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431
3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688
4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348
5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925
6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490
share_product3
1 0.3951219
2 0.1983909
3 0.4139610
4 0.2821054
5 0.3798079
6 0.3833826
好老朴实的基本R
rdat <- dat[-1]
rdat <- rdat/rowSums(rdat)
colnames(rdat) <- paste0("share_", colnames(rdat))
cbind(dat, rdat)
给出:
sysRespNum product1 product2 product3 share_sum_product1 share_sum_product2
1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998
2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431
3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688
4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348
5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925
6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490
share_sum_product3
1 0.3951219
2 0.1983909
3 0.4139610
4 0.2821054
5 0.3798079
6 0.3833826
我知道在数据框中取消嵌套列表列的问题已被提出并多次回答。但是,这可能是 237. 这类问题。
我有以下数据:
set.seed(666)
dat <- data.frame(sysRespNum = c(1,2,3,4,5,6),
product1 = sqrt(rnorm(6, 20, 5)^2),
product2 = sqrt(rnorm(6, 20, 5)^2),
product3 = sqrt(rnorm(6, 20, 5)^2))
数据:
sysRespNum product1 product2 product3
1 1 23.766555 13.46907 24.32327
2 2 30.071773 15.98740 11.39922
3 3 18.224328 11.03880 20.67063
4 4 30.140839 19.78984 19.62087
5 5 8.915628 30.75021 24.29150
6 6 23.791981 11.14885 21.72450
现在,我想计算每个产品占所有产品总和的比例,所以我想计算product1/sum(my three products)
,然后产品2和3也一样。所以我期待三个新的列。
我试过以下方法:
library(tidyverse)
dat %>%
mutate(sum_Product = apply(across(-sysRespNum), 1, function(x) list(sum_Product = x/sum(x))))
(附带问题:是否有更直接的方法可以直接对其进行变异而无需创建列表。我现在可以先创建一个总和列,然后再进行简单的变异和跨越。但是我'我想知道是否可以在不先创建临时总和列的情况下实现计算)
现在我的问题是很难解除 sum_Product
列表列的嵌套。 unnest_wider
不起作用,sum_Product
列仍然是一个列表。
所以唯一对我有用的是
- 遵循此解决方案:
- 更改上面的代码并将
list
部分替换为data.frame
:
完整代码:
dat %>%
mutate(sum_Product = apply(across(-sysRespNum), 1, function(x) data.frame(sum_Product = x/sum(x)))) %>%
unnest(cols = everything()) %>%
mutate(product = rep(1:3, nrow(.)/3)) %>%
pivot_wider(values_from = sum_Product,
names_from = product,
names_prefix = "share_product")
给出正确的结果:
# A tibble: 6 x 7
sysRespNum product1 product2 product3 share_product1 share_product2
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 23.8 13.5 24.3 0.386 0.219
2 2 30.1 16.0 11.4 0.523 0.278
3 3 18.2 11.0 20.7 0.365 0.221
4 4 30.1 19.8 19.6 0.433 0.285
5 5 8.92 30.8 24.3 0.139 0.481
6 6 23.8 11.1 21.7 0.420 0.197
# … with 1 more variable: share_product3 <dbl>
然而,用pivot_wider.pivot_wider.
unnest 一切然后reshape 感觉没必要复杂那么 a) 是否有更优雅的方法来计算我的份额变量 b) 是否有更 elegant/shorter/less 将列表列重塑为多个向量列的更冗长的方法?
这样做更容易 rowSums
,即将以关键字 'product' 开头的列上的 'product1' 除以 rowSums
。而不是用 c_across
做 rowwise
,这是矢量化的,应该也很快
library(dplyr)
dat %>%
mutate(sum_product = product1/rowSums(select(., starts_with('product'))))
注意:混合了 base R
代码 (apply
) 和带有 across
的 tidyverse 选项,这似乎不是最佳方式
如果我们需要对所有 'product' 列执行此操作,请先使用 mutate
创建一个 sum
列,然后在以 across
开头的列上使用 across
'product' 将列除以 'Sum_col'
dat %>%
mutate(Sum_col = rowSums(select(., starts_with('product'))),
across(starts_with('product'),
~ ./Sum_col, .names = '{.col}_sum_product')) %>%
select(-Sum_col)
-输出
#ysRespNum product1 product2 product3 product1_sum_product product2_sum_product product3_sum_product
#1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998 0.3951219
#2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431 0.1983909
#3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688 0.4139610
#4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348 0.2821054
#5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925 0.3798079
#6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490 0.3833826
或使用base R
nm1 <- startsWith(names(dat), 'product')
dat[paste0('sum_product', seq_along(nm1))] <- dat[nm1]/rowSums(dat[nm1])
我想下面的基本 R 代码应该适合你
cbind(
dat,
setNames(dat[-1] / rowSums(dat[-1]), paste0("share_product", seq_along(dat[-1])))
)
这给出了
sysRespNum product1 product2 product3 share_product1 share_product2
1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998
2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431
3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688
4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348
5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925
6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490
share_product3
1 0.3951219
2 0.1983909
3 0.4139610
4 0.2821054
5 0.3798079
6 0.3833826
好老朴实的基本R
rdat <- dat[-1]
rdat <- rdat/rowSums(rdat)
colnames(rdat) <- paste0("share_", colnames(rdat))
cbind(dat, rdat)
给出:
sysRespNum product1 product2 product3 share_sum_product1 share_sum_product2
1 1 23.766555 13.46907 24.32327 0.3860783 0.2187998
2 2 30.071773 15.98740 11.39922 0.5233660 0.2782431
3 3 18.224328 11.03880 20.67063 0.3649701 0.2210688
4 4 30.140839 19.78984 19.62087 0.4333597 0.2845348
5 5 8.915628 30.75021 24.29150 0.1393996 0.4807925
6 6 23.791981 11.14885 21.72450 0.4198684 0.1967490
share_sum_product3
1 0.3951219
2 0.1983909
3 0.4139610
4 0.2821054
5 0.3798079
6 0.3833826