在列中拆分字符串并创建新行

时间:2019-03-22 23:01:39

标签: r

我有一个包含2列的数据框。第2列包含由;分隔的基因,例如A;BA;B;C;D。这些基因的数量可以从2到许多不等。我想将基因分成2对,然后放入新的行中。重要的是,我希望所有这些基因的所有可能组合(最初在一起)用于创建新行。当然,如果只有2个基因开头,则无需执行任何操作。另外,我想为新创建的行保留第1列中的值(文本)。 我没有尝试任何事情,因为我不知道如何解决该问题。 下面给出了示例输入和我想要的输出。 任何有关如何解决此问题的帮助或建议,将不胜感激。我确实对tidyverse有一些(有限的)经验。 谢谢您的宝贵时间。

input = data.frame(col1 = c("example1", "example2"), col2 = c("A;B", "A;B;C;D"))

output = data.frame(col1 = c("example1", 
                             "example2", 
                             "example2", 
                             "example2", 
                             "example2", 
                             "example2"), col2 = c("A;B",
                                                   "A;B", 
                                                   "A;C",
                                                   "A;D", 
                                                   "B;C", 
                                                   "B;D", 
                                                   "C;D"))

3 个答案:

答案 0 :(得分:2)

首先创建一个辅助函数(从您的input起,我已经stringsAsFactors = FALSE;如果您不愿意,只需在辅助函数中添加as.character()

char_comb <- function(aString) {

   str_split(aString, pattern = ';') %>% 
      unlist() %>% 
      combn(2, paste0, collapse = ";")

}

NB-通过最少的示例运行来演示辅助功能:char_comb(aString = "A;B;C;D")

然后:

map2(.x = input[['col1']], 
     .y = input[['col2']], .f = function(idx, cell) {


    if(nchar(cell) > 3) {

        res <- char_comb(cell)
        tibble(col1 = idx, col2 = res)

    } else {

        tibble(col1 = idx, col2 = cell)
        }
    }) %>% 
    bind_rows()

Result:
  col1     col2 
  <chr>    <chr>
1 example1 A;B  
2 example2 A;B  
3 example2 A;C  
4 example2 A;D  
5 example2 B;C  
6 example2 B;D  
7 example2 C;D  

p.s。我的解决方案还包括“ A; B”。排除它们是微不足道的。

编辑:添加基准。

从评论看来,@ ip2018担心执行时间。 将两个解决方案都包装在一个函数中...

microbenchmark("pasqui" = res_fn(col1 = input[['col1']], col2 = input[['col2']]), 
               "H 1" = h1_res()
               )

基准测试结果

Unit: milliseconds
   expr      min       lq      mean    median        uq       max neval cld
 pasqui 1.137418 1.198085  1.403278  1.297016  1.611183  2.310684   100  a 
    H 1 9.140376 9.811799 12.126555 10.730754 13.036842 45.186844   100   b

答案 1 :(得分:2)

这是一种tidyverse的方法:

library(tidyverse)

input %>%
  filter(grepl(";", col2)) %>%
  mutate(x = str_split(col2, ";") %>% map(~combn(., 2, paste0, collapse = ";"))) %>%
  unnest() %>%
  select(-col2)

# A tibble: 7 x 2
# Groups:   col1 [2]
  col1     x    
  <fct>    <chr>
1 example1 A;B  
2 example2 A;B  
3 example2 A;C  
4 example2 A;D  
5 example2 B;C  
6 example2 B;D  
7 example2 C;D 

答案 2 :(得分:0)

这是另一个计算for循环中所有内容的解决方案,不确定在处理时间方面如何与pasqui的答案进行比较。

col1<-c()
col2<-c()

for(i in 1:nrow(input)){
 name<-input$col1[i]
 splt<-unlist(strsplit(input$col2[i],";"))

 #Generate Matrix of Pair-Wise Combinations
 combs<-sapply(splt, function(x) sapply(splt, function(y) paste(x,y, sep=";") ))

 #Limit to Unique Combinations
 uni_combinations<-combs[lower.tri(combs)] 

 #Save vector of names
 n<-length(uni_combinations)
 col1<-c(col1,rep(name,n))

 # Save vector of gene combinations
 col2<-c(col2, uni_combinations)
}

# Put results in data.frame()
result<-data.frame(col1=col1, col2 = col2)
result