Extraction de nombres à partir de vecteurs de chaînes

101

J'ai une chaîne comme celle-ci:

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

Je voudrais grep uniquement le nombre numérique de ce vecteur. La sortie attendue est un vecteur:

c(20, 1)

Comment dois-je procéder?

user1471980
la source

Réponses:

83

Que diriez-vous

# pattern is by finding a set of numbers in the start and capturing them
as.numeric(gsub("([0-9]+).*$", "\\1", years))

ou

# pattern is to just remove _years_old
as.numeric(gsub(" years old", "", years))

ou

# split by space, get the element in first index
as.numeric(sapply(strsplit(years, " "), "[[", 1))
Arun
la source
1
Pourquoi le .*nécessaire? Si vous les voulez au départ, pourquoi ne pas les utiliser ^[[:digit:]]+?
sebastian-c
2
.*est nécessaire car vous devez faire correspondre la chaîne entière. Sans cela, rien n'est supprimé. Notez également que cela subpeut être utilisé ici à la place de gsub.
Matthew Lundberg
12
si le nombre ne doit pas nécessairement être au début de la chaîne, utilisez ceci:gsub(".*?([0-9]+).*", "\\1", years)
TMS
Je veux obtenir 27. Je ne comprends pas pourquoi, en ajoutant des conditions (comme l'ajout d'un "-" échappé, le résultat s'allonge ... gsub(".*?([0-9]+).*?", "\\1", "Jun. 27–30")Résultat: [1] "2730" gsub(".*?([0-9]+)\\-.*?", "\\1", "Jun. 27–30")Résultat: [1] "27 juin –30 "
Lionel Trebuchon
65

Je pense que la substitution est un moyen indirect d'arriver à la solution. Si vous souhaitez récupérer tous les numéros, je vous recommande gregexpr:

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

Si vous avez plusieurs correspondances dans une chaîne, cela les obtiendra toutes. Si vous n'êtes intéressé que par le premier match, utilisez à la regexprplace de gregexpret vous pouvez ignorer le unlist.

sébastien-c
la source
1
Je ne m'y attendais pas, mais cette solution est plus lente que toutes les autres, d'un ordre de grandeur.
Matthew Lundberg
@MatthewLundberg le gregexpr, regexprou les deux?
sebastian-c
1
gregexpr. Je n'avais pas essayé regexprjusqu'à maintenant. Énorme différence. L'utilisation le regexprmet entre les solutions d'Andrew et d'Arun (deuxième plus rapide) sur un set 1e6. Peut-être aussi intéressant, l'utilisation subdans la solution d'Andrew n'améliore pas la vitesse.
Matthew Lundberg
Cela divise en fonction des points décimaux. Par exemple 2,5 devient c ('2', '5')
MBorg
65

Update Depuis extract_numericest obsolète, nous pouvons utiliser parse_numberfrom readrpackage.

library(readr)
parse_number(years)

Voici une autre option avec extract_numeric

library(tidyr)
extract_numeric(years)
#[1] 20  1
Akrun
la source
2
Bien pour cette application, mais gardez à l'esprit parse_numberque ne joue pas avec des nombres négatifs. Essayer parse_number("–27,633")
Nettle
@Nettle Oui, c'est vrai et cela ne fonctionnera pas s'il y a plusieurs instances également
akrun
3
Le bogue d'analyse des nombres négatifs a été corrigé: github.com/tidyverse/readr/issues/308 readr::parse_number("-12,345") # [1] -12345
Russ Hyde
35

Voici une alternative à la première solution d'Arun, avec une expression régulière plus simple de type Perl:

as.numeric(gsub("[^\\d]+", "", years, perl=TRUE))
Andrew
la source
as.numeric(sub("\\D+","",years)). S'il y avait des lettres avant et | ou après, alorsgsub
Onyambu
21

Ou simplement:

as.numeric(gsub("\\D", "", years))
# [1] 20  1
989
la source
19

Une stringrsolution en pipeline:

library(stringr)
years %>% str_match_all("[0-9]+") %>% unlist %>% as.numeric
Joe
la source
Merci Joe, mais cette réponse n'extrait pas les signes négatifs avant les nombres dans la chaîne.
Miao Cai
16

Vous pouvez également vous débarrasser de toutes les lettres:

as.numeric(gsub("[[:alpha:]]", "", years))

Cela est probablement moins généralisable.

Tyler Rinker
la source
3
Curieusement, la solution d'Andrew bat cela par un facteur de 5 sur ma machine.
Matthew Lundberg
5

Extrayez les nombres de n'importe quelle chaîne à la position de début.

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

Extraire les nombres de n'importe quelle chaîne INDÉPENDANTE de position.

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

Nous pouvons également utiliser str_extractdestringr

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

S'il y a plusieurs nombres dans la chaîne et que nous voulons tous les extraire, nous pouvons utiliser str_extract_allqui, contrairement à, str_extractrenvoie tous les macthes.

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"
Ronak Shah
la source
2

Après le message de Gabor Grothendieck, postez sur la liste de diffusion r-help

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

library(gsubfn)
pat <- "[-+.e0-9]*\\d"
sapply(years, function(x) strapply(x, pat, as.numeric)[[1]])
Juanbretti
la source