gsub删除不需要的精度

时间:2018-05-14 14:17:29

标签: r string gsub

有人可以帮助您在R中使用gsub来实现以下目标吗?

input string: a=5.00,b=120,c=0.0003,d=0.02,e=5.20, f=1200.0,g=850.02
desired output: a=5,b=120,c=0.0003,d=0.02,e=5.2, f=1200, g=850.02

实际上,如果它们都只是0,则删除小数点后的冗余0,如果存在实际分数,则不删除。

4 个答案:

答案 0 :(得分:3)

我无法单独使用gsub来解决此问题,但我们可以尝试在逗号上拆分输入向量,然后使用带有apply的{​​{1}}函数:

gsub

Demo

我实际上打了x <- "a=5.00,b=120,c=0.0003,d=0.02,e=5.20, f=1200.0,g=850.02" input <- sapply(unlist(strsplit(x, ",")), function(x) gsub("(?<=\\d)\\.$", "", gsub("(\\.[1-9]*)0+$", "\\1", x), perl=TRUE)) input <- paste(input, collapse=",") input [1] "a=5,b=120,c=0.0003,d=0.02,e=5.2, f=1200,g=850.02" 两次电话。如果数字为1,则第一个调用会删除小数点后出现的所有尾随零。第二个调用删除了杂散的小数点,对于像gsub这样的数字,第一个调用将保留为5.00而不是5.,后者是我们想要的。

答案 1 :(得分:2)

要删除小数点后的尾随0,请尝试:

编辑忘记了5.00

x = c('5.00', '0.500', '120', '0.0003', '0.02', '5.20', '1200', '850.02')
gsub("\\.$" "", gsub("(\\.(|[1-9]+))0+$", "\\1", x))
# [1] "5"    "0.5"    "120"    "0.0003" "0.02"   "5.2"    "1200"   "850.02"

HT @TimBiegeleisen:我将输入误读为字符串向量。对于单字符串输入,转换为字符串向量,您可以调用gsub,然后将输出折叠回单个字符串:

paste(
    gsub("\\.$", "", gsub("(\\.(|[1-9]+))0+$", "\\1",
    unlist(strsplit(x, ", ")))), 
        collapse=", ")
  

[1]“a = 5,b = 0.5,c = 120,d = 0.0003,e = 0.02,f = 5.2,g = 1200,h = 850.02”

答案 2 :(得分:1)

gsub文字处理工具,适用于字符级。它不知道任何语义解释。

但是,您特别感兴趣的是操纵这种语义解释,即文本中编码的数字的精确度。

所以使用它:解析文本中的数字,并以所需的精度写出:

parse_key_value_pairs = function (text) {
    parse_pair = function (pair) {
        pair = strsplit(pair, "\\s*=\\s*")[[1]]
        list(key = pair[1], value = as.numeric(pair[2]))
    }
    pairs = unlist(strsplit(text, "\\s*,\\s*"))
    structure(lapply(pairs, parse_pair), class = 'kvp')
}

as.character.kvp = function (x, ...) {
    format_pair = function (pair) {
        sprintf('%s = %g', pair[1], pair[2])
    }
    pairs = vapply(x, format_pair, character(1))
    paste(pairs, collapse = ", ")
}

使用如下:

text = "a=5.00,b=120,c=0.0003,d=0.02,e=5.20, f=1200.0,g=850.02"
parsed = parse_key_value_pairs(text)
as.character(parsed)

这使用了R的几个有趣特性:

  • 对于文本处理,它仍然使用正则表达式(在strsplit内)。
  • 要处理多个值,请使用lapply依次将解析函数应用于字符串的某些部分
  • 要重建键值对,请使用sprintf格式化字符串。 sprintf是一个从C改编的原始文本格式化工具。但它相当普遍,在我们的案例中它可以正常工作。
  • 解析后的值标有S3 class name。这就是R实现面向对象的方式。
  • 为我们的类型提供标准通用as.character的重载。这意味着任何接受对象并通过as.character显示它的现有函数都可以处理我们的解析数据类型。特别是,这适用于{glue} library

    > glue::glue("result: {parsed}")
    result: a = 5, b = 120, c = 0.0003, d = 0.02, e = 5.2, f = 1200, g = 850.02
    

答案 3 :(得分:1)

这可能不是最理想的解决方案,但出于教育目的,以下是一种使用条件正则表达式只调用一次的方法:

gsub

备注:

  1. x = 'a=5.00,b=120,c=0.0003,d=0.02,e=5.20, f=1200.0,g=850.02' gsub('(?!\\d+(?:,|$))(\\.[0-9]*[1-9])?(?(1)0+\\b|\\.0+(?=(,|$)))', '\\1', x, perl = TRUE) # [1] "a=5,b=120,c=0.0003,d=0.02,e=5.2, f=1200,g=850.02" 是一个负面的lookbehind,在逗号或字符串结尾后匹配数字一次或多次。这有效从整体正则表达式匹配中排除模式。

  2. (?!\\d+(?:,|$))匹配一个文字点,一个零或更多的数字和一个数字(零除外)。 (\\.[0-9]*[1-9])?使此模式可选,并且对于条件处理后向引用的方式至关重要。

  3. ?是逻辑(?(1)0+\\b|\\.0+(?=(,|$)))

    的条件
    • (?(IF)THEN|ELSE)(1)部分,用于检查捕获组1是否匹配。这是指(IF)

    • (\\.[0-9]*[1-9])0+\\b部分,仅当(THEN) TRUE 时才会匹配。在这种情况下,只有(IF)匹配时,正则表达式才会尝试在单词边界后一次或多次匹配

    • (\\.[0-9]*[1-9])\\.0+(?=(,|$))部分,仅当(ELSE) FALSE 时才会匹配。在这种情况下,只有当(IF) 没有匹配时,正则表达式才会尝试匹配一个字面点,在逗号或字符串结尾后一次或多次匹配

  4. 如果我们将2.和3.放在一起,我们会得到(\\.[0-9]*[1-9])(\\.[0-9]*[1-9])0+\\b

  5. \\.0+(?=(,|$))作为替换因此将\\1转换为(\\.[0-9]*[1-9])0+\\b(\\.[0-9]*[1-9])匹配的模式为空白。转换为:

      对于前者

    • \\.0+(?=(,|$))5.20 对于后者,

    • 5.25.0051200.0