Wyodrębnianie liczb z wektorów łańcuchów

103

Mam taki ciąg:

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

Chciałbym zebrać tylko liczbę liczbową z tego wektora. Oczekiwanym wynikiem jest wektor:

c(20, 1)

Jak mam to zrobić?

user1471980
źródło

Odpowiedzi:

87

Co powiesz na

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

lub

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

lub

# split by space, get the element in first index
as.numeric(sapply(strsplit(years, " "), "[[", 1))
Bieg
źródło
1
Dlaczego jest to .*konieczne? Jeśli chcesz je mieć na początku, dlaczego nie użyć ^[[:digit:]]+?
sebastian-c
2
.*jest konieczne, ponieważ musisz dopasować cały ciąg. Bez tego nic nie zostanie usunięte. Zwróć też uwagę, że submożna tego użyć zamiast gsub.
Matthew Lundberg
14
jeśli liczba nie musi znajdować się na początku ciągu, użyj tego:gsub(".*?([0-9]+).*", "\\1", years)
TMS
Chcę otrzymać 27. Nie rozumiem dlaczego, dodając warunki (np. Dodając znak „-” ze znakiem ucieczki, wynik jest dłuższy… gsub(".*?([0-9]+).*?", "\\1", "Jun. 27–30")Wynik: [1] „2730” gsub(".*?([0-9]+)\\-.*?", "\\1", "Jun. 27–30")Wynik: [1] „27 czerwca –30 ”
Lionel Trebuchon
66

Myślę, że podstawienie jest pośrednim sposobem dojścia do rozwiązania. Jeśli chcesz odzyskać wszystkie numery, polecam gregexpr:

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

Jeśli masz wiele dopasowań w ciągu, otrzymasz je wszystkie. Jeśli interesuje Cię tylko pierwsze dopasowanie, użyj regexprzamiast gregexpri możesz pominąć unlist.

Sebastian-c
źródło
1
Nie spodziewałem się tego, ale to rozwiązanie jest wolniejsze niż inne, o rząd wielkości.
Matthew Lundberg
@MatthewLundberg gregexpr, regexprczy jedno i drugie?
sebastian-c
1
gregexpr. Nie próbowałem regexpraż do teraz. Duża różnica. Użycie regexprstawia go pomiędzy rozwiązaniami Andrew i Aruna (drugi najszybszy) w zestawie 1e6. Być może również interesujące, użycie subw rozwiązaniu Andrew nie poprawia szybkości.
Matthew Lundberg
Dzieli się na podstawie miejsc dziesiętnych. Na przykład 2,5 staje się c ('2', '5')
MBorg
66

Aktualizacja Ponieważ extract_numericjest przestarzała, możemy użyć parse_numberz readrpakietu.

library(readr)
parse_number(years)

Oto inna opcja z extract_numeric

library(tidyr)
extract_numeric(years)
#[1] 20  1
akrun
źródło
2
W porządku dla tej aplikacji, ale pamiętaj, parse_numberże nie gra z liczbami ujemnymi. Spróbuj parse_number("–27,633")
Pokrzywa
@Nettle Tak, zgadza się i nie zadziała, jeśli jest również wiele instancji
akrun,
3
Naprawiono błąd parsowania liczb ujemnych: github.com/tidyverse/readr/issues/308 readr::parse_number("-12,345") # [1] -12345
Russ Hyde
35

Oto alternatywa dla pierwszego rozwiązania Arun, z prostszym wyrażeniem regularnym podobnym do Perla:

as.numeric(gsub("[^\\d]+", "", years, perl=TRUE))
Andrzej
źródło
as.numeric(sub("\\D+","",years)). Jeśli były listy przed i | lub po, togsub
Onyambu
21

Lub po prostu:

as.numeric(gsub("\\D", "", years))
# [1] 20  1
989
źródło
19

Rozwiązanie stringrrurociągowe:

library(stringr)
years %>% str_match_all("[0-9]+") %>% unlist %>% as.numeric
Joe
źródło
Dzięki Joe, ale ta odpowiedź nie wyodrębnia znaków ujemnych przed liczbami w ciągu.
Miao Cai
16

Ty też możesz pozbyć się wszystkich liter:

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

Prawdopodobnie jest to jednak mniej generalizowalne.

Tyler Rinker
źródło
3
Co dziwne, rozwiązanie Andrew przewyższa to pięciokrotnie na moim komputerze.
Matthew Lundberg
5

Wyodrębnij liczby z dowolnego ciągu na pozycji początkowej.

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

Wyodrębnij liczby z dowolnego ciągu NIEZALEŻNEGO od pozycji.

x <- gregexpr("[0-9]+", years)  # Numbers with any number of digits
x2 <- as.numeric(unlist(regmatches(years, x)))
sbaniwal
źródło
4

Możemy również użyć str_extractfromstringr

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

Jeśli w ciągu znajduje się wiele liczb i chcemy wyodrębnić je wszystkie, możemy użyć funkcji, str_extract_allktóra w przeciwieństwie do str_extractzwraca wszystkie makty.

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
źródło
2

Po wpisie od Gabora Grothendiecka wpis na listę mailingową 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
źródło