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 列仍然是一个列表。

所以唯一对我有用的是

完整代码:

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_acrossrowwise,这是矢量化的,应该也很快

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