【问题标题】:string split values in two columns, and then concatenate them into a new column字符串拆分两列中的值,然后将它们连接到一个新列中
【发布时间】:2021-10-04 16:12:47
【问题描述】:

我正在尝试为两列(ProteinsPositions.within.proteins)调用 str_split 函数,然后将相应的值连接到一个名为 ID 的新列中。

df <- data.frame(Proteins = c("Q99755;A2A3N6", "O00329", "O00444", 
"O14965", "O14976", "Q6A1A2;O15530", "O43318", "O43526", "O43930;P51817", 
"O60331"), Positions.within.proteins = c("276;223", "708", "41", 
"162", "175", "84;111", "63", "628", "78;78", "270"))

这是我的代码。

my.function <- function(x, y){
  protein.names <- str_split(x, ";")[[1]]
  position.names <- str_split(y, ";")[[1]]
  ID <- list()
    for (i in 1:length(protein.names)){
      ID[i] <- paste(protein.names[i], position.names[i], sep ="_")
    }
  ID.2 <- unlist(ID)
  return(ID.2)
}

当我在单行上调用该函数时,它在某种程度上起作用。

row1 <- my.function(df$Proteins[1], df$Positions.within.proteins[1])

"Q99755_276" "A2A3N6_223"

但我的问题是:

  1. 如何将此函数应用于整个数据框?
  2. 如何将"Q99755_276" "A2A3N6_223" 转换为我想要的"Q99755_276;A2A3N6_223"

我想使用apply 函数,但不确定apply 函数是否可以接受两个参数。

这里显示了它应该是什么样子。

df.final <- data.frame(Proteins = c("Q99755;A2A3N6", "O00329", "O00444", 
"O14965", "O14976", "Q6A1A2;O15530", "O43318", "O43526", "O43930;P51817", 
"O60331"), Positions.within.proteins = c("276;223", "708", "41", 
"162", "175", "84;111", "63", "628", "78;78", "270"), ID = c("Q99755_276;A2A3N6_223", 
"O00329_708", "O00444_41", "O14965_162", "O14976_175", "Q6A1A2_84;O15530_111", 
"O43318_63", "O43526_628", "O43930_78;P51817_78", "O60331_270"
))

有谁知道如何实现这些?非常感谢您的帮助!

【问题讨论】:

  • 您能否展示列出了 ID 的示例表以及数据如何?看起来你需要 join group_byconcatenate
  • @JasonMathews 感谢您的回复!我更新了我的帖子。我展示了示例表的前 10 行。
  • 图片没有帮助,请与dput(head(data))分享可重现的数据样本。
  • @AnoushiravanR 很高兴知道这个功能dput(head(data))。非常有帮助。我更新了我的帖子。
  • @GuedesBF 我提供了可编辑的代码。你能解释一下\(x)在你的代码中做了什么吗?

标签: r string dataframe apply strsplit


【解决方案1】:

你可以使用你的函数tidyverse

使用 mutate()map2(.f = my.function) 创建一个嵌套 ID 列,其中包含一个列表列,每行包含所有 ID(在示例数据中有些有 1 个 ID,有些有两个)。然后您可以unnest_wider() 创建几个不同的 ID 列,稍后您可以使用 tidyr::unite() 折叠这些列

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

df %>% mutate(ID=map2(Proteins, Positions.within.proteins, my.function))%>%
        unnest_wider(ID, names_sep = '.')%>%
        unite(contains('ID'), col='ID', remove = TRUE, sep=";", na.rm=TRUE)

# A tibble: 10 x 3
   Proteins      Positions.within.proteins ID                   
   <chr>         <chr>                     <chr>                
 1 Q99755;A2A3N6 276;223                   Q99755_276;A2A3N6_223
 2 O00329        708                       O00329_708           
 3 O00444        41                        O00444_41            
 4 O14965        162                       O14965_162           
 5 O14976        175                       O14976_175           
 6 Q6A1A2;O15530 84;111                    Q6A1A2_84;O15530_111 
 7 O43318        63                        O43318_63            
 8 O43526        628                       O43526_628           
 9 O43930;P51817 78;78                     O43930_78;P51817_78  
10 O60331        270                       O60331_270 

【讨论】:

    【解决方案2】:

    这是使用 strsplitmapply 的基本 R 方式 -

    df$ID <- mapply(function(x, y) paste(x, y, collapse = ';', sep = '_'), 
            strsplit(df$Proteins, ';'), strsplit(df$Positions.within.proteins, ';'))
    df
    
    #        Proteins Positions.within.proteins                    ID
    #1  Q99755;A2A3N6                   276;223 Q99755_276;A2A3N6_223
    #2         O00329                       708            O00329_708
    #3         O00444                        41             O00444_41
    #4         O14965                       162            O14965_162
    #5         O14976                       175            O14976_175
    #6  Q6A1A2;O15530                    84;111  Q6A1A2_84;O15530_111
    #7         O43318                        63             O43318_63
    #8         O43526                       628            O43526_628
    #9  O43930;P51817                     78;78   O43930_78;P51817_78
    #10        O60331                       270            O60331_270
    

    【讨论】:

      【解决方案3】:

      一个简短的基本 R 解决方案。

      df$ID <- apply(df, 1, \(x) paste(do.call(\(y, z) paste0(y, "_", z), 
                                               unname(strsplit(x, ';'))), collapse=';'))
      df
      #         Proteins Positions.within.proteins                    ID
      # 1  Q99755;A2A3N6                   276;223 Q99755_276;A2A3N6_223
      # 2         O00329                       708            O00329_708
      # 3         O00444                        41             O00444_41
      # 4         O14965                       162            O14965_162
      # 5         O14976                       175            O14976_175
      # 6  Q6A1A2;O15530                    84;111  Q6A1A2_84;O15530_111
      # 7         O43318                        63             O43318_63
      # 8         O43526                       628            O43526_628
      # 9  O43930;P51817                     78;78   O43930_78;P51817_78
      # 10        O60331                       270            O60331_270
      

      【讨论】:

        【解决方案4】:

        你要找的是tidyr::unite():

        tidyr::unite(data = iris, col = "new_column", Species, Sepal.Length, sep = ";")
        

        试试看。它需要一个数据框(在本例中为 iris)、新列的名称(new_column)、您想要连接在一起的列(Species 和 Sepal.Length),以及您想要将它们分开的值(a分号)。 tidyr::separate()unite() 的对立面——它根据原来的分隔符创建了两个新列。

        编辑

        好吧,你需要更有创意...尝试使用tidyr::separate() 将每个蛋白质分成自己的列,对蛋白质位置执行相同的操作,然后将每个蛋白质与其位置结合起来。然后,用分号作为分隔符将两种蛋白质结合在一起。最后,删除仅使用一种蛋白质的情况下的缺失值(始终具有相同的形式,以;NA_NA 格式结尾)。中提琴:

        library(tidyr)
        library(dplyr)
        library(stringr)
        
        df %>% 
          separate(col = Proteins, c("protein1", "protein2"), ";", remove = FALSE) %>% 
          separate(col = Positions.within.proteins, into = c("position_p1", "position_p2"), ";", remove = FALSE) %>% 
          unite(col = "id_part1", sep = "_", protein1, position_p1) %>% 
          unite(col = "id_part2", sep = "_", protein2, position_p2) %>% 
          unite(col = "id", sep = ";", id_part1, id_part2) %>% 
          mutate(id = str_remove_all(id, ";NA_NA"))
        

        另一个编辑

        我做了一些基准测试,我的实现也快了一点:

        rbenchmark::benchmark(
          mine = df %>% 
            separate(col = Proteins, c("protein1", "protein2"), ";", remove = FALSE) %>% 
            separate(col = Positions.within.proteins, into = c("position_p1", "position_p2"), ";", remove = FALSE) %>% 
            unite(col = "id_part1", sep = "_", protein1, position_p1) %>% 
            unite(col = "id_part2", sep = "_", protein2, position_p2) %>% 
            unite(col = "id", sep = ";", id_part1, id_part2) %>% 
            mutate(id = str_remove_all(id, ";NA_NA")),
          
        alt_implementation = df %>% 
            rowwise() %>%
            mutate(ID = map2(Proteins, Positions.within.proteins, my.function)) %>%
            unnest_wider(ID, names_sep = '.') %>%
            unite(contains('ID'), col = 'ID', remove = TRUE, sep = ";", na.rm = TRUE),
        
        replications = 1000
        )
        #                  test replications elapsed relative user.self sys.self user.child sys.child
        # 1                mine         1000    9.06    1.000      8.97     0.05         NA        NA
        # 2  alt_implementation         1000   11.77    1.299     11.73     0.00         NA        NA
        

        【讨论】:

        • 不,tidyr::unite 只是简单地将两列连接在一起。我想要的比这更复杂。例如,tidyr::unite 将结合Q99755;A2A3N6276;223 得到Q99755;A2A3N6;276;223。但我想要的是Q99755_276;A2A3N6_223
        • 我也在考虑一个单独的 %>% 单独 %>% unite %>% unite 解决方案,但你先得到它。不错的工作。虽然有点冗长,但这个解决方案很容易理解。不过,我认为这种微小的性能差异并不重要。
        • @GuedesBF 您对性能的看法可能是对的,但您永远不知道。医疗数据集往往是不守规矩的。基准测试是出于好奇而不是其他任何事情。谢谢你的好话。
        • 我自己就是一名医生,是的,“不守规矩”是对医学数据集的一个很好的描述……
        • 在替代实现中不需要逐行
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        • 2013-10-04
        • 1970-01-01
        • 1970-01-01
        • 2022-06-13
        相关资源
        最近更新 更多