首页 > 解决方案 > 在 r 中,使用字符串,就像我输入它一样

问题描述

我正在处理 r 的一个方面,这确实让我感到困惑。我构建的是一行代码调用 str_remove 保存为字符串。如果我将该字符串复制粘贴到我想使用这行代码的位置,它会按预期完美运行。但是我无法让 r 正确解释此代码。我曾尝试使用例如解析,但用于 str_remove 正则表达式的转义字符会引发错误。

有没有一种简单的方法可以将字符串视为一行输入的代码?

这是我的可重现示例:

制作玩具数据:

maf_list_context <- list(as.data.frame(cbind(c("ATTATCGAATT", "ATTATTTTAAA"), c("this one", "not that one"))),
                     as.data.frame(cbind(c("ATTACGTAATT", "ATTATTTTAAA"), c("this one too", "not that one either")))   )

maf_list_context <- lapply(maf_list_context, function(x)
{colnames(x) <- c("CONTEXT", "want_it")
return(x)
})

这个想法是上下文将是一个函数的参数并且它可以是灵活的,因此用户可以提供任意数量的以逗号分隔的感兴趣的上下文。这些将是 stringr 正则表达式,旨在在一串 11 个碱基中查找 DNA 中的特定上下文。例如,在这里我们可以使用两个感兴趣的上下文。下面的代码将这些组合在一起,形成一个表达式,供以后从列表中的数据框中选择适当的行时使用。

context <- "\\w{5}CG\\w{4}, \\w{4}CG\\w{5}"

contextvec <- unlist(str_split(context, pattern = ", "))

contextexpression <- c()

for(i in 1:length(contextvec)){
  
  contextexpression <- paste0(contextexpression, "str_detect(x$CONTEXT, pattern = '", contextvec[i], "') |")
  
}

contextexpression <- str_remove(contextexpression, pattern = " \\|$")

“上下文表达式”现在是:

[1] "str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}')"

如果我将这个表达式直接粘贴到 apply 中,它会完全按照我的意愿工作。

 > lapply(maf_list_context, function(x){
+   
+   x[str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}'), ]
+   
+ })

[[1]]
      CONTEXT  want_it
1 ATTATCGAATT this one

[[2]]
      CONTEXT      want_it
1 ATTACGTAATT this one too

但当然,如果我在那里使用字符串,它不会。

> lapply(maf_list_context, function(x){
+   
+   x[contextexpression, ]
+   
+ })

[[1]]
   CONTEXT want_it
NA    <NA>    <NA>

[[2]]
   CONTEXT want_it
NA    <NA>    <NA>

我尝试了许多不同的功能,但没有一个可以使这项工作。有没有办法让 r 解释这个字符串,就好像我直接输入它一样?

整个代表:

if (!require("stringr") {
  install.packages("stringr", dependencies = TRUE)
  library("stringr")

maf_list_context <- list(as.data.frame(cbind(c("ATTATCGAATT", "ATTATTTTAAA"), c("this one", "not that one"))),
                         as.data.frame(cbind(c("ATTACGTAATT", "ATTATTTTAAA"), c("this one too", "not that one either")))   )

maf_list_context <- lapply(maf_list_context, function(x){
  colnames(x) <- c("CONTEXT", "want_it")
  return(x)
})

context <- "\\w{5}CG\\w{4}, \\w{4}CG\\w{5}"

contextvec <- unlist(str_split(context, pattern = ", "))

contextexpression <- c()

for(i in 1:length(contextvec)){
  
  contextexpression <- paste0(contextexpression, "str_detect(x$CONTEXT, pattern = '", contextvec[i], "') |")
  
}

contextexpression <- str_remove(contextexpression, pattern = " \\|$")

maf_list_select <- lapply(maf_list_context, function(x){
  
  x[contextexpression, ]
  
})

标签: rstringtypesstringr

解决方案


我不确定我是否完全遵循您希望输入的内容以及如何应用它,但您的问题似乎与您传递给子集运算符的内容有关,即x[<codehere>]

子集运算符需要一个逻辑向量。当您“粘贴表达式”时,您实际上是在粘贴一个被评估为逻辑向量的表达式,因此它可以正确地进行子集化。当您传递变量contextexpression时,您实际上是在传递一个字符串。正如 R 所见:

x[ "str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}')", ]

而不是(注意语法突出显示的差异):

x[ str_detect(x$CONTEXT, pattern = '\\w{5}CG\\w{4}') |str_detect(x$CONTEXT, pattern = '\\w{4}CG\\w{5}'), ]

您希望将每个上下文应用于列表的每个成员以获取逻辑向量,然后获取子集。

purrr::map2(maf_list_context, contextvec, ~.x[str_detect(.x$CONTEXT, .y), ])

如果您想将 中的每个项目与 中contextvec的每个项目进行比较maf_list_context,那么它有点复杂但可行。

purrr::map2(
  maf_list_context, 
  purrr::map(
    maf_list_context, 
    function(data){
      purrr::reduce(contextvec, 
                    function(prev, cond) str_detect(data$CONTEXT, cond) | prev, 
                    .init = logical(length(contextvec))
      )
    } 
  ),
  ~.x[.y]
)

可能有一种更有效的方法可以将匹配项与 中的项目短路maf_list_context,但一般方法适用。str_detect处理单个条件与单个maf_list项目的比较。该reduce调用将与contextvec单个项目的所有比较结果组合maf_list_context到单个布尔值中。内部map遍历maf_list_context. 外层map2遍历由内层创建的布尔值列表,mapmaf_list_context找到匹配的子集。

如果maf_list_context有 n 个项目并且contextvec有 m 个项目:

  • reduce进行 m 次比较,得到 1 个值
  • map进行 n 次调用以reduce产生 n 个值
  • map2对子集进行 n 次迭代maf_list_context

推荐阅读