sub():反向引用的处理

时间:2011-08-10 14:30:49

标签: regex r

我说我有一个像“C5Cl2NO2S”这样的分子式,我想用R计算分子量。我最简单的方法是使用正则表达式来分析公式并将其拆分为元素组件,然后将它们交给执行计算的单独函数。但是,我面临的问题是,当我交出RegEx的反向引用时,这些不会被评估,而是以“\\ 1”,“\\ 2”的形式移交。

这是我的尝试:

masses <- list(
  C  = 12,
  H  = 1.01,
  Cl = 34.97,
  N  = 14.00,
  O  = 15.99,
  P  = 30.97,
  S  = 31.97
)

elementMass <- function( element, count ) {
  if( count == "" ) {
    count <- "1"
  }
  return( as.character( masses[[ element ]] * as.numeric( count ) ) )
}


sumFormula2Mass <- function( x ){
  y <- 0.0
  for( e in x ) {
    if( e != "" ) {
      y <- y + as.numeric( sub( "^(C|H|Cl|N|O|P|S)([0-9]*)$", elementMass("\\1", "\\2"), e ) )
    }
  }
  return( y )
}

sub(
  "^(C[0-9]*)?(H[0-9]*)?(Cl[0-9]*)?(N[0-9]*)?(O[0-9]*)?(P[0-9]*)?(S[0-9]*)?$",
  sumFormula2Mass( c("\\1", "\\2", "\\3", "\\4", "\\5", "\\6", "\\7") ),
  "C5Cl2NO2S"
)

任何想法如何改善这一点? 非常感谢

4 个答案:

答案 0 :(得分:5)

下面我们假设问题中的公式形式,即一串组件,每个组件都是大写字母,后面可选地跟小写字母,后跟可选的数字。我们在gsubfn包中使用gsubfn。它类似gsub,但替换字符串可以是各种其他对象。这是一个原型对象。 proto对象是一个环境,此处用于包含属性sum和两个方法prefun。在开始时,pre会自动运行,并具有初始化sum的效果。然后,每次正则表达式匹配时,proto对象和两个引用的字符串将传递给fun,并运行fun来处理它们。最后p$sum包含结果。变量masses在问题中定义。

library(gsubfn)
p <- proto(pre = function(this) this$sum <- 0,
    fun = function(this, name, count) {
        count <- as.numeric(count)
        if (is.na(count)) count <- 1
        this$sum <- this$sum + masses[[name]] * count
        ""
    })
gsubfn("([[:upper:]][[:lower:]]*)(\\d*)", p, "C5Cl2NO2S")
p$sum # 207.89

答案 1 :(得分:3)

我认为sub()中的反向引用并不像这样。您似乎将它们视为返回值,当它们是输入时。

这是一个不同的解决方案。它需要一种非常不同的方法,即将字符串分成单独的部分,然后参考这些。但是,它有一些局限性。首先,它假定不能用括号处理化学式。其次,它假定原子是合理写入的(即氯写成Cl - 大写C和小写l)。可能存在许多其他限制,但这应该让您了解此解决方案的外观。

sumFormula2Mass2 <- function(x,masses){
  summedMasses <- NULL
  for(e in x){
    ## split up the string
    split.e <- unlist(strsplit(e,''))
    ## join letters from individual elements (since subequent letters should be lower case)
    ilower <- grep('[a-z]',split.e)
    if(length(ilower) > 0){
      for(i in 1:length(ilower)){
        j <- ilower[i]
        split.e <- c(if(j > 2) split.e[1:(j-2)],
                     paste(split.e[(j-1):j],collapse=''),
                     if(j < length(split.e)) split.e[(j+1):length(split.e)])
        ilower <- ilower - 1
      }
    }
    ## join numbers together (in case there are more than 10 atoms)
    inum <- grep('[0-9]',split.e)
    if(length(inum) > 1){
      for(i in 1:(length(inum)-1)){ 
        if(inum[i + 1] == inum[i] + 1){
          j <- inum[i]
          split.e <- c(split.e[1:(j-1)],
                       paste(split.e[j:(j+1)],collapse=''),
                       if(j+2 <= length(split.e)) split.e[(j+2):length(split.e)])
          inum <- inum - 1
        }
      }
    }
    ## add up the mass
    sumMass = 0
    for(i in 1:length(split.e)){
      if(length(grep('[1-9]',split.e[i])) > 0){
        next 
      } else if(split.e[i] %in% names(masses)){
        nMolecules <- 1
        if(i != length(split.e) && length(grep('[1-9]',split.e[i+1])) > 0)
          nMolecules <- as.numeric(split.e[i+1])
        sumMass <- sumMass + nMolecules * masses[[split.e[i]]]
      } else {
        warning(sprintf('Could not match element %s',split.e[i]))
        next
      }
    }
    summedMasses <- c(summedMasses,sumMass)
  }
  return(summedMasses)
}

以下是您的化合物加上一些化妆品(我不是化学家)的一些结果:

> sumFormula2Mass2(c("C5Cl2NO2S","C5Cl2NO2S4","C5Cl10NO2S4"),masses)
[1] 207.89 303.80 583.56

答案 2 :(得分:0)

答案 3 :(得分:0)

看看

RSiteSearch ("molecular weight")

我想第二次或第三次命中是你正在寻找的(第一种是蛋白质)。

(抱歉,没有看到你对另一个答案的评论 - 但是,如果有人真的在寻找分子量计算,我会留下这个。)