【问题标题】:Convert percentage to decimal within text在文本中将百分比转换为小数
【发布时间】:2018-03-08 14:13:42
【问题描述】:

我有几行包含计算规则的 data.frame。在该字符串中,我需要将文本转换为:

"{p500} * 65% >= {q600}"

进入

"{p500} * 0.65 >= {q600}"

我是正则表达式的新手,但我认为 gsub 在这里会有所帮助。 有人可以帮忙吗?

【问题讨论】:

  • 请提供更多不同的输入字符串,以便解决方案对其他类型的计算字符串也更通用。
  • 我的数据包含几行相似的data.frame(总是像“{} * 24%

标签: r regex string decimal percentage


【解决方案1】:

您可以使用gsubfn对正则表达式进行计算:

library(gsubfn)
gsubfn("(\\d+)%", function(x) strtoi(x)/100,"{p500} * 9% >= {q600}")
[1] "{p500} * 0.09 >= {q600}"

注意,捕获组值通过x = \\1传递给匿名替换函数

如果您的文本与您提供的示例一致,您可以在 base 中执行多个 gsub,就像 @snoram 建议的那样:

gsub("(\\d)%","0.0\\1",gsub("(\\d{2})%","0.\\1","{p500} * 9% >= {q600}"))
[1] "{p500} * 0.09 >= {q600}"

(\\d{2}) 正好匹配 2 个数字,括号创建一个组,以后可以在 gsub 中调用。通过在替换中调用\\1,您正在反向引用您在原始字符串中捕获的组。外部 gsub (\\d)% 正好匹配 1 个数字,在这种情况下会替换一个额外的 0。

我自己对正则表达式还是比较陌生,但我发现这个备忘单很有帮助:R Regular Expressions

【讨论】:

  • 这在 9% 上失败
  • 对,0.9 是否等于 9%? ;)
【解决方案2】:

base-R 中的简单解决方案,但使用(可选)magrittr 以提高可读性

# data + library
str <- c("{p500} * 65% >= {q600}", "{p500} * 9% >= {q600}")
library(magrittr)

# Apply gsub twice:
str %>% 
  gsub("(\\d{2})%", "0\\.\\1", .) %>%
  gsub("(\\d{1})%", "0\\.0\\1", .)
[1] "{p500} * 0.65 >= {q600}" "{p500} * 0.09 >= {q600}"

编辑

鉴于 cmets 中的新案例,我想最好放弃上面的简单方法并尝试不同的方法。这是一个快速而肮脏的例子(仍然依赖于好的 ol' base-R):

# New data
str <- c(
  "{p500} * 65% >= {q600}", 
  "{p500} * 9% >= {q600}",
  "{p500} * 190% >= {q600}", 
  "{p500} * 2.4% >= {q600}")

# Quick and dirty
strmat <- do.call(rbind, strsplit(str, " "))
strmat[, 3] <- as.double(gsub("%", "", lapply(strsplit(str, " "), "[", 3))) / 100
apply(strmat, 1, paste, collapse=" ")
[1] "{p500} * 0.65 >= {q600}"  "{p500} * 0.09 >= {q600}"  "{p500} * 1.9 >= {q600}"  
[4] "{p500} * 0.024 >= {q600}"

【讨论】:

  • 谢谢。如果值是 190% 和 2.4%,是否有适应?
  • 喜欢这个挑战,并使用strsplit() 尽可能快地提出了一个新的解决方案,但它绝对可以用更多的时间(或技能)做得更干净。
  • 这不是只有在百分比始终处于相同位置时才有效吗? (所以它可以与第三列中的百分比分开)?输入值的长度可能不同...
  • 有点,这取决于恒定的空格数。
  • 它们在您给出的示例中。如果您的问题包含更多数据可以采用的不同形式,您会得到更好的答案。正则表达式需要...规律性。
【解决方案3】:

您可以使用regmatches。首先捕获您需要的那些数字,然后除以 100 使它们精确到小数点。然后用小数点替换它们: 警告。这将替换您原来的 str。也许需要创建一个副本:

 str <- c("{p500} * 65% >= {q600}","{p500} * 9% >= {q600}",
             "{p500} * 190% >= {q600}","{p500} * 2.4% >= {q600}") 
 str1=str 
 regmatches(str1,regexpr("\\d\\S*%",str))=as.numeric(sub(".*?(\\d\\S*)%.*","\\1",str))/100
 str1
[1] "{p500} * 0.65 >= {q600}"  "{p500} * 0.09 >= {q600}" 
[3] "{p500} * 1.9 >= {q600}"   "{p500} * 0.024 >= {q600}"

编辑:

如果其他人缺少% 标志并且他们需要保持不变:

str <- c("{p500} * 65% >= {q600}","{p500} * 65 >= {q600}","{p500} * 9% >= {q600}",
   "{p500} * 190 >= {q600}", "{p500} * 190% >= {q600}","{p500} * 2.4% >= {q600}",
    "{p500} * 2.4 >= {q600}") 

 str1=str # Create a copy
 m=regexpr("\\d\\S*%",str)# Find only those that contain % sign

 regmatches(str1,m)=as.numeric(sub("%","",regmatches(str,m)))/100
str1
[1] "{p500} * 0.65 >= {q600}"  "{p500} * 65 >= {q600}"   
[3] "{p500} * 0.09 >= {q600}"  "{p500} * 190 >= {q600}"  
[5] "{p500} * 1.9 >= {q600}"   "{p500} * 0.024 >= {q600}"
[7] "{p500} * 2.4 >= {q600}"  

检查第二个元素、第四个元素和最后一个元素,你会发现它们没有被改变。

【讨论】:

  • 这也是一个很好的解决方案。但是如果 str 还包含没有 % 的值怎么办?这段代码不起作用。感谢您的进一步帮助。
  • 嗯,这是一个从未被提及的观点。这是一种矢量化格式,没有 for 循环。给出的答案在代码中有lapplyapply 2 for 循环,这意味着如果数据很大,代码将失败。最后为什么没有%sign?那为什么要改成十进制。好的,如果没有百分号,那么我们的捕获组将是 "\\s\\d\\S*" 这可用于捕获您需要在 * 符号之后和 &gt; 符号之前捕获的所有内容
  • 如果没有 % 那么这个值 str 必须被跳过/保持原样。
  • 哇,但即使给出的答案也没有做到这一点。您是否正在寻找执行此操作的代码?
  • 老实说,我认为这是一个很好的答案。 @Sven,下次您提出问题时应该小心。
猜你喜欢
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-11
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多