从字符串向量中提取数字

165

我有一个像这样的字符串:

years<-c("20 years old", "1 years old")

我想从这个向量中仅提取数字。期望的输出是一个向量:

c(20, 1)

我该怎么做呢?

13个回答

127

怎么样?

# pattern is by finding a set of numbers in the start and capturing them
as.numeric(gsub("([0-9]+).*$", "\\1", years))
或者
# pattern is to just remove _years_old
as.numeric(gsub(" years old", "", years))
或者
# split by space, get the element in first index
as.numeric(sapply(strsplit(years, " "), "[[", 1))

3
为什么需要 .*?如果想要匹配字符串开头,为什么不使用 ^[[:digit:]]+ - sebastian-c
3
需要匹配整个字符串,因此.*是必要的。如果没有它,就不会删除任何内容。另外,请注意可以使用sub来替换gsub - Matthew Lundberg
29
如果数字不必出现在字符串的开头,使用以下代码:gsub(".*?([0-9]+).*", "\\1", years),它可以提取 years 字符串中的第一个数字。请注意,此代码段使用的是正则表达式。 - Tomas
我想得到27。我不明白为什么添加条件(例如添加转义的“-”)会使结果变长... gsub(".*?([0-9]+).*?", "\\1", "Jun. 27–30") 结果:[1] "2730" gsub(".*?([0-9]+)\\-.*?", "\\1", "Jun. 27–30") 结果:[1] "Jun. 27–30" - Lionel Trebuchon
惊人的答案!我一直回来看它! - stats_noob

121

更新 由于extract_numeric已被弃用,我们可以使用readr包中的parse_number

library(readr)
parse_number(years)

这里还有另一种选项,使用extract_numeric

library(tidyr)
extract_numeric(years)
#[1] 20  1

6
这个应用程序没问题,但请记住parse_number不适用于负数。请尝试使用parse_number("–27,633") - Nettle
2
@Nettle 是的,没错。如果有多个实例也是行不通的。 - akrun
8
负数解析错误已经被修复:https://github.com/tidyverse/readr/issues/308 readr::parse_number("-12,345") # [1] -12345 - Russ Hyde
3
extract_numeric现在已经被弃用,您将收到一个警告,建议使用readr::parse_number()。 - NorthLattitude
2
@NorthLattitude 如果你注意到了,我在“更新”中确实指定了那个。 - akrun

75

我认为替换是一种间接的获取解决方案的方法。如果您想检索所有数字,我建议使用gregexpr

matches <- regmatches(years, gregexpr("[[:digit:]]+", years))
as.numeric(unlist(matches))

如果一个字符串中有多个匹配项,这会获取所有匹配项。如果您只对第一个匹配项感兴趣,请使用regexpr而不是gregexpr,您可以跳过unlist


1
我没想到,但这个解决方案比其他任何一个都慢得多,相差一个数量级。 - Matthew Lundberg
@MatthewLundberg 是 gregexprregexpr 还是两者都要? - sebastian-c
1
gregexpr。我之前从未尝试过regexpr,但是它的效果差别非常大。在1e6数据集上使用regexpr可以将其排在Andrew和Arun的解决方案之间(第二快)。也许有趣的是,在Andrew的解决方案中使用sub并不能提高速度。 - Matthew Lundberg
1
这个基于小数点进行拆分。例如,2.5会变成c('2','5')。 - MBorg
回复 @MBorg,将正则表达式更改为"-?[[:digit:]]+(\\.[[:digit:]]+)?",我相信可以考虑到负数和小数。 - user3667133

56
或者简单地说:
as.numeric(gsub("\\D", "", years))
# [1] 20  1

这是最简单的答案,它运行良好! - bing-nagata-smirnov
2
\D是一个元字符,用于匹配非数字字符:https://www.w3schools.com/jsref/jsref_regexp_digit_non.asp - Giovanni Colitti

41

这里是对Arun第一个解决方案的替代方案,使用更简单的类Perl正则表达式:

as.numeric(gsub("[^\\d]+", "", years, perl=TRUE))

1
as.numeric(sub("\\D+","",years)). If there were letters before and |or after, then gsub - Onyambu

30

我们也可以使用来自stringrstr_extract

years<-c("20 years old", "1 years old")
as.integer(stringr::str_extract(years, "\\d+"))
#[1] 20  1

如果一个字符串中有多个数字,我们想要提取所有的数字,可以使用str_extract_all。与str_extract不同的是,它会返回所有匹配项。

years<-c("20 years old and 21", "1 years old")
stringr::str_extract(years, "\\d+")
#[1] "20"  "1"

stringr::str_extract_all(years, "\\d+")

#[[1]]
#[1] "20" "21"

#[[2]]
#[1] "1"

26

一个使用 stringr 管道的解决方案:

library(stringr)
years %>% str_match_all("[0-9]+") %>% unlist %>% as.numeric

谢谢Joe,但是这个答案没有提取字符串中数字前面的负号。 - Miao Cai

18
你也可以把所有的字母都删掉:
as.numeric(gsub("[[:alpha:]]", "", years))

可能这个并不太具有普适性。

3
有趣的是,Andrew 的解决方案在我的电脑上比这个快了五倍。 - Matthew Lundberg

8

从任何字符串的开头位置提取数字。

x <- gregexpr("^[0-9]+", years)  # Numbers with any number of digits
x2 <- as.numeric(unlist(regmatches(years, x)))

从字符串中提取数字,无论位置如何。

x <- gregexpr("[0-9]+", years)  # Numbers with any number of digits
x2 <- as.numeric(unlist(regmatches(years, x)))

6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接