字符串将值拆分为两列,然后将它们连接成一个新列

string split values in two columns, and then concatenate them into a new column

我正在尝试为两列(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"
))

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

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

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

试试看。它需要一个数据框(在本例中为 iris)、新列的名称(new_column)、您想连接在一起的列(Species 和 Sepal.Length)以及您想要的值用(分号)分隔它们。 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

您可以使用您的函数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 

一个简短的基础 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

这是使用 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