gsub 删除不需要的精度
gsub to remove unwanted precision
任何人都可以用 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就去掉,如果有实数就不要去掉。
要删除小数点后的尾随 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"
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
我无法单独使用 gsub
使它工作,但我们可以尝试用逗号分割你的输入向量,然后使用 apply
函数和 gsub
:
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"
实际上我打了两次电话给 gsub
。如果数字有一个,第一个调用会去掉所有出现在 小数点后 的尾随零。在像 5.00
这样的数字的情况下,第二次调用会删除杂散的小数点,第一次调用将保留为 5.
而不是我们想要的 5
。
这可能不是最理想的解决方案,但出于教育目的,这是一种使用 条件正则表达式仅 一次 调用 gsub
的方法:
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"
备注:
(?!\d+(?:,|$))
是一种负向后视,它与逗号或字符串结尾后的数字匹配一次或多次。这有效地从整个正则表达式匹配中排除模式。
(\.[0-9]*[1-9])?
匹配文字点、数字零次或多次以及数字(零除外)。 ?
使此模式可选,并且对于条件如何处理反向引用至关重要。
(?(1)0+\b|\.0+(?=(,|$)))
是条件逻辑 (?(IF)THEN|ELSE)
(1)
是 (IF)
部分,它检查捕获组 1 是否匹配。这是指(\.[0-9]*[1-9])
0+\b
是 (THEN)
部分,仅当 (IF)
为 TRUE 时才匹配。在这种情况下,仅当 (\.[0-9]*[1-9])
匹配时,正则表达式才会尝试匹配单词边界后的零一次或多次
\.0+(?=(,|$))
是 (ELSE)
部分,仅当 (IF)
为 FALSE 时才匹配。在这种情况下,仅当 (\.[0-9]*[1-9])
没有 匹配时,正则表达式才会尝试匹配文字点,在逗号或字符串结尾后出现一次或多次零
如果我们将 2. 和 3. 放在一起,我们会得到 (\.[0-9]*[1-9])0+\b
或 \.0+(?=(,|$))
\1
作为替换因此将 (\.[0-9]*[1-9])0+\b
变为 (\.[0-9]*[1-9])
匹配的模式或将 \.0+(?=(,|$))
变为空白。转化为:
5.20
到5.2
为前者
5.00
到 5
和 1200.0
到 1200
后者
任何人都可以用 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就去掉,如果有实数就不要去掉。
要删除小数点后的尾随 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"
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
我无法单独使用 gsub
使它工作,但我们可以尝试用逗号分割你的输入向量,然后使用 apply
函数和 gsub
:
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"
实际上我打了两次电话给 gsub
。如果数字有一个,第一个调用会去掉所有出现在 小数点后 的尾随零。在像 5.00
这样的数字的情况下,第二次调用会删除杂散的小数点,第一次调用将保留为 5.
而不是我们想要的 5
。
这可能不是最理想的解决方案,但出于教育目的,这是一种使用 条件正则表达式仅 一次 调用 gsub
的方法:
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"
备注:
(?!\d+(?:,|$))
是一种负向后视,它与逗号或字符串结尾后的数字匹配一次或多次。这有效地从整个正则表达式匹配中排除模式。(\.[0-9]*[1-9])?
匹配文字点、数字零次或多次以及数字(零除外)。?
使此模式可选,并且对于条件如何处理反向引用至关重要。(?(1)0+\b|\.0+(?=(,|$)))
是条件逻辑(?(IF)THEN|ELSE)
(1)
是(IF)
部分,它检查捕获组 1 是否匹配。这是指(\.[0-9]*[1-9])
0+\b
是(THEN)
部分,仅当(IF)
为 TRUE 时才匹配。在这种情况下,仅当(\.[0-9]*[1-9])
匹配时,正则表达式才会尝试匹配单词边界后的零一次或多次\.0+(?=(,|$))
是(ELSE)
部分,仅当(IF)
为 FALSE 时才匹配。在这种情况下,仅当(\.[0-9]*[1-9])
没有 匹配时,正则表达式才会尝试匹配文字点,在逗号或字符串结尾后出现一次或多次零
如果我们将 2. 和 3. 放在一起,我们会得到
(\.[0-9]*[1-9])0+\b
或\.0+(?=(,|$))
\1
作为替换因此将(\.[0-9]*[1-9])0+\b
变为(\.[0-9]*[1-9])
匹配的模式或将\.0+(?=(,|$))
变为空白。转化为:5.20
到5.2
为前者5.00
到5
和1200.0
到1200
后者