适用于Tidyverse的combn技巧问询:无排列配对及值关联实现
嘿,我懂你想要的是无排列的元素组合对(只保留a-b这种单向配对,不会重复出现b-a),同时还要把每个元素对应的数值都保留下来,而且希望代码比现有的更优雅、更贴合tidyverse的风格对吧?我给你几个清爽的解决方案:
首先先确认我们的示例数据:
library(tibble) data <- tibble(letter = letters[1:5], value = 1:5)
方法1:全tidyverse流水线风格(推荐)
用tidyr::expand_grid生成笛卡尔积,再过滤掉重复配对,最后整理列名,全程不离开tibble:
library(tidyr) library(dplyr) data %>% # 生成自身和自身的笛卡尔积 expand_grid(item1 = ., item2 = .) %>% # 过滤出字母顺序靠前的配对,避免重复 filter(item1$letter < item2$letter) %>% # 整理成清晰的列结构 transmute( letter1 = item1$letter, value1 = item1$value, letter2 = item2$letter, value2 = item2$value )
方法2:用combn直接操作行索引
如果习惯用combn,可以直接对行索引取组合,然后提取对应行的数值,不用转置矩阵:
library(purrr) library(dplyr) combn(seq_len(nrow(data)), 2, # 每个组合生成一个小tibble ~ tibble( letter1 = data$letter[.x], value1 = data$value[.x], letter2 = data$letter[.y], value2 = data$value[.y] ), # 不要简化成矩阵,保留列表形式 simplify = FALSE) %>% # 把所有小tibble合并成一个 bind_rows()
方法3:dplyr 1.1.0+专属简化版
如果你用的是较新版本的dplyr,可以用cross_join替代expand_grid,代码更简洁:
library(dplyr) data %>% cross_join(., .) %>% filter(letter.x < letter.y) %>% # 批量重命名列,把.x/.y改成1/2 rename_with(~sub("\\.x", "1", .), ends_with(".x")) %>% rename_with(~sub("\\.y", "2", .), ends_with(".y"))
这些方法都是全程在tibble的语境下操作,不需要来回转换矩阵和数据框,可读性和维护性都比现有方法好很多~
内容的提问来源于stack exchange,提问作者Ploulack
相关产品推荐
相关产品推荐

