通过将所有可能的列组合与另一个 table 分开来创建新的数据框

Create new dataframe by dividing all possibles columns combination from another table

我正在努力寻找一个简单快速的解决方案来创建一个新的数据框,方法是将它们之间的所有“组”列相乘。

数据举例

a1 <- rnorm(n = 10)
b1 <- rnorm(n = 10)
c1 <- rnorm(n = 10)
a2 <- rnorm(n = 10)
b2 <- rnorm(n = 10)
c2 <- rnorm(n = 10)

例如我的初始数据表中的这个

Original <- data.frame(
  date = seq(today()-9, today(), by = 1),
  a1 = a1,
  b1 = b1,
  c1 = c1,
  a2 = a2,
  b2 = b2,
  c2 = c2)

并且这个数据表是我想要实现的 (e.i., 以 1 结尾的列之间的所有可能组合的列和以 1 结尾的列之间的所有可能组合的列一个 2)

Objective <- data.frame(
  date = seq(today()-9, today(), by = 1),
  b1a1 = b1*a1,
  c1a1 = c1*a1,
  c1b1 = c1*b1,
  b2c2 = b2*c2,
  b2a2 = b2*a2,
  c2a2 = c2*a2)

我试过循环,但它不是一个非常优雅和高效的解决方案;或者至少我的不是。非常欢迎使用 tidyverse 的解决方案

提前致谢

I.T

这里是基础 R 选项 -

cbind(Original[1], do.call(cbind, 
      unname(lapply(split.default(Original[-1], 
      gsub('\D', '', names(Original[-1]))), function(x) {
           do.call(cbind, combn(names(x), 2, function(y) {
               setNames(data.frame(do.call(`*`, Original[y])), 
               paste0(y, collapse = ''))
  }, simplify = FALSE))
}))))

#         date     a1b1      a1c1    b1c1    a2b2    a2c2    b2c2
#1  2021-05-28 -0.06708  1.393018 -0.1213  0.1795 -1.0878 -0.0947
#2  2021-05-29  0.33234  0.045563  0.0201  0.0607  0.0247  0.9219
#3  2021-05-30  0.05043  0.160582  0.0341  0.1748 -0.3893 -0.1184
#4  2021-05-31  0.93642  0.980333  0.8156  0.0746 -1.1128 -0.1571
#5  2021-06-01 -1.21365 -0.256619  0.3268 -1.0106 -0.3542  2.1991
#6  2021-06-02 -0.09550  1.311417 -0.0754 -0.8243 -0.5532  1.1986
#7  2021-06-03  0.32514  0.373324  2.3262 -1.1904 -3.0764  0.7171
#8  2021-06-04 -0.41219  1.034527 -0.8338 -1.8588 -1.0202  2.6916
#9  2021-06-05  0.12488 -0.155639 -0.2294  0.2380  0.4288  0.3711
#10 2021-06-06 -0.00665  0.000139 -0.0105 -2.0117 -0.6363  1.0802

答案的解释-

  1. split.default用于将数据分组。
split.default(Original[-1], gsub('\D', '', names(Original[-1])))

#$`1`
#         a1      b1      c1
#1  -0.87773  0.0764 -1.5871
#2   0.86812  0.3828  0.0525
#3   0.48761  0.1034  0.3293
#4  -1.06095 -0.8826 -0.9240
#5   0.97625 -1.2432 -0.2629
#6  -1.28910  0.0741 -1.0173
#7  -0.22843 -1.4234 -1.6343
#8  -0.71512  0.5764 -1.4467
#9   0.29108  0.4290 -0.5347
#10 -0.00937  0.7098 -0.0149

#$`2`
#        a2     b2     c2
#1  -1.4360 -0.125  0.758
#2  -0.0403 -1.507 -0.612
#3  -0.7580 -0.231  0.514
#4   0.7270  0.103 -1.531
#5  -0.4035  2.505  0.878
#6   0.6168 -1.336 -0.897
#7   2.2599 -0.527 -1.361
#8  -0.8394  2.215  1.215
#9  -0.5244 -0.454 -0.818
#10  1.0886 -1.848 -0.585

其中 gsub 用于从用于创建组的列名中删除所有非数字字符。

gsub('\D', '', names(Original[-1]))
#[1] "1" "1" "1" "2" "2" "2"
  1. 对于使用 lapply 的每个组,我们创建列名称 (combn(names(x), 2.....) 的每个组合,一次包含 2 列。

  2. 乘以每个组合 (do.call(*, Original[y])) 创建一个单列数据框并使用 setNames 给出列的名称组合 (paste0(y, collapse = ''))

  3. 步骤 3 中的所有组合都合并到一个数据帧中。 (do.call(cbind, combn.....).

  4. 所有这些组再次组合成一个数据帧(do.call(cbind, lapply...)。

  5. 带有日期的第一列保留在最终输出中 (cbind(Original[1], ....)。

很好的问题。 tidyverse 方法。这种方法将每组的列数不均匀。解释-

  • 数据被分成一个列表,每个子组作为列表中的一个单独项目。对于这个部门
    • 首先,使用pivot_longer
    • 对数据进行多头旋转
    • 然后使用 gsub 创建一个虚拟组(子组标识)列。您也可以使用 str_replace
  • 使用 dplyr::group_split
  • 创建的列表
  • 所有项目中的数据现在使用 purrr::map 中的 tidyr::pivot_wider 重塑回其原始形式
  • 此后列表的所有单个项目 -
    • 首先结合使用combnReduce。您也可以在此处使用 purrr::reduce
    • 其次是使用相同的 combnReduce
    • 生成的新列的名称
    • 这些名称在矩阵上方绑定到命名数据帧中。
  • 最后,将 purrr::reducedplyr::left_join 列表一起使用会转换回预期的形状
set.seed(123)
a1 <- rnorm(n = 10)
b1 <- rnorm(n = 10)
c1 <- rnorm(n = 10)
a2 <- rnorm(n = 10)
b2 <- rnorm(n = 10)
c2 <- rnorm(n = 10)

Original <- data.frame(
  date = seq(Sys.Date()-9, Sys.Date(), by = 1),
  a1 = a1,
  b1 = b1,
  c1 = c1,
  a2 = a2,
  b2 = b2,
  c2 = c2)

library(tidyverse)
Original %>% pivot_longer(!date) %>%
  mutate(grp = gsub('^\D*(\d)+$', '\1', name)) %>%
  group_split(grp, .keep = F) %>%
  map(~ .x %>% pivot_wider(names_from = name, values_from = value)) %>%
  map(~ combn(.x[-1], 2, FUN = Reduce, f = `*`) %>% as.data.frame() %>%
        setNames(combn(names(.x[-1]), 2, FUN = Reduce, f = paste0)) %>% cbind(.x[1], .)) %>%
  reduce(~left_join(.x, .y, by = 'date'))

         date        a1b1        a1c1        b1c1        a2b2         a2c2         b2c2
1  2021-05-28 -0.68606804  0.59848918 -1.30710356 -0.29626767  0.108031283 -0.175982140
2  2021-05-29 -0.08282104  0.05017292 -0.07843039  0.06135046  0.008423333  0.005935364
3  2021-05-30  0.62468579 -1.59924166 -0.41119329 -1.13268875 -0.038374446  0.054248120
4  2021-05-31  0.00780406 -0.05139295 -0.08067566  1.90463287  1.201815497  2.968438088
5  2021-06-01 -0.07186344 -0.08080991  0.34742254  0.99243873 -0.185489171 -0.272722771
6  2021-06-02  3.06467216 -2.89278864 -3.01397443 -0.77341778  1.044302702 -1.703161152
7  2021-06-03  0.22946735  0.38614963  0.41709268 -0.22316502 -0.857881519  0.623969018
8  2021-06-04  2.48789113 -0.19402639 -0.30162620  0.02889143 -0.036194437 -0.272813136
9  2021-06-05 -0.48172830  0.78173260 -0.79823906 -0.23864021 -0.037894774  0.096601990
10 2021-06-06  0.21070515 -0.55877763 -0.59279292  0.03171951 -0.082159505 -0.018002847

检查这个扩展数据集

set.seed(123)
a1 <- rnorm(n = 10)
b1 <- rnorm(n = 10)
c1 <- rnorm(n = 10)
a2 <- rnorm(n = 10)
b2 <- rnorm(n = 10)
c2 <- rnorm(n = 10)
d2 <- rnorm(n = 10)

Original <- data.frame(
  date = seq(Sys.Date()-9, Sys.Date(), by = 1),
  a1 = a1,
  b1 = b1,
  c1 = c1,
  a2 = a2,
  b2 = b2,
  c2 = c2,
  d2 = d2)

library(tidyverse)
Original %>% pivot_longer(!date) %>%
  mutate(grp = gsub('^\D*(\d)+$', '\1', name)) %>%
  group_split(grp, .keep = F) %>%
  map(~ .x %>% pivot_wider(names_from = name, values_from = value)) %>%
  map(~ combn(.x[-1], 2, FUN = Reduce, f = `*`) %>% as.data.frame() %>%
        setNames(combn(names(.x[-1]), 2, FUN = Reduce, f = paste0)) %>% cbind(.x[1], .)) %>%
  reduce(~left_join(.x, .y, by = 'date'))

         date        a1b1        a1c1        b1c1        a2b2         a2c2         a2d2         b2c2        b2d2        c2d2
1  2021-05-28 -0.68606804  0.59848918 -1.30710356 -0.29626767  0.108031283  0.161902656 -0.175982140 -0.26373820  0.09616971
2  2021-05-29 -0.08282104  0.05017292 -0.07843039  0.06135046  0.008423333  0.148221326  0.005935364  0.10444173  0.01433970
3  2021-05-30  0.62468579 -1.59924166 -0.41119329 -1.13268875 -0.038374446 -0.298262480  0.054248120  0.42163941  0.01428475
4  2021-05-31  0.00780406 -0.05139295 -0.08067566  1.90463287  1.201815497 -0.894445153  2.968438088 -2.20924515 -1.39402460
5  2021-06-01 -0.07186344 -0.08080991  0.34742254  0.99243873 -0.185489171 -0.880563395 -0.272722771 -1.29468307  0.24197936
6  2021-06-02  3.06467216 -2.89278864 -3.01397443 -0.77341778  1.044302702  0.209022041 -1.703161152 -0.34089562  0.46029226
7  2021-06-03  0.22946735  0.38614963  0.41709268 -0.22316502 -0.857881519  0.248271309  0.623969018 -0.18057692 -0.69416615
8  2021-06-04  2.48789113 -0.19402639 -0.30162620  0.02889143 -0.036194437 -0.003281582 -0.272813136 -0.02473471  0.03098700
9  2021-06-05 -0.48172830  0.78173260 -0.79823906 -0.23864021 -0.037894774 -0.282179411  0.096601990  0.71933645  0.11422674
10 2021-06-06  0.21070515 -0.55877763 -0.59279292  0.03171951 -0.082159505 -0.779997773 -0.018002847 -0.17091365  0.44269850

reprex package (v2.0.0)

于 2021-06-06 创建

您也可以使用以下解决方案,虽然不像其他答案那样简洁,但这里有一种不同的方法,可能有一些值得考虑的地方。我尝试用 tidyverse 等价物来模拟 combn 函数的大部分代码块。因此,导致 df2 数据集的第一个 chuck 创建了您想要计算其产品的所有组合,第二个块只是在 Original 数据集的上下文中评估它们。无论如何,谢谢你提出这个把我推向极限的奇妙问题。

library(dplyr)
library(tidyr)
library(purrr)
library(stringr)
library(rlang)

cols <- c("(\w1)", "(\w2)") 

cols %>% 
  map_dfc(~ names(Original)[str_detect(names(Original), .x)] %>%
            as_tibble() %>%
            mutate(value2 = rev(value)) %>%
            expand(value, value2) %>%
            filter(value != value2) %>%
            rowwise() %>%
            mutate(comb = paste0(sort(c(value, value2)), collapse = "*")) %>%
            select(comb) %>%
            distinct(comb)) %>%
  rename_with(~ str_remove(., "\.\.\."), everything()) %>%
  pivot_longer(everything(), names_to = c(".value", "id"), 
               names_pattern = "(\w+)(\d)") -> df2


df2 %>%
  select(comb) %>%
  rowwise() %>%
  mutate(data = map(comb, ~ eval_tidy(parse_expr(.x), data = Original))) %>%
  unnest(cols = c(data)) %>%
  group_by(comb) %>%
  mutate(id = row_number()) %>%
  pivot_wider(names_from = comb, values_from = data) %>%
  relocate(ends_with("1")) %>%
  bind_cols(Original$date) %>%
  rename_with(~ str_remove(., "\*"), everything()) %>%
  rename(Date = ...8) %>%
  relocate(Date) %>%
  select(-id)

# A tibble: 10 x 7
   Date           a1b1    a1c1      b1c1      a2b2     a2c2    b2c2
   <date>        <dbl>   <dbl>     <dbl>     <dbl>    <dbl>   <dbl>
 1 2021-05-28 -0.129    0.0912 -0.0838   -1.55     -1.52     2.11  
 2 2021-05-29 -0.477   -1.58    0.352    -3.55     -0.144    0.101 
 3 2021-05-30  0.195    0.708   0.105     0.910    -0.356   -0.177 
 4 2021-05-31 -0.194    0.0219 -0.0111   -1.35      0.261   -0.200 
 5 2021-06-01  0.0140   0.107   0.000601 -0.0279   -0.126    0.104 
 6 2021-06-02  0.242    0.141   0.174    -0.0174    0.695   -0.0570
 7 2021-06-03 -0.439   -0.360   0.589     0.804    -2.76    -1.79  
 8 2021-06-04 -1.02    -0.0349  0.0137    2.07      0.357    0.495 
 9 2021-06-05 -0.00670  0.550  -0.00161  -0.000907  0.00503 -0.925 
10 2021-06-06 -0.287   -0.505   0.718    -0.0290   -0.00351  0.0256