Policz liczbę wszystkich słów w ciągu

82

Czy istnieje funkcja licząca liczbę słów w ciągu? Na przykład:

str1 <- "How many words are in this sentence"

aby zwrócić wynik 7.

Jan
źródło
Na podstawie poniższej odpowiedzi @ Martina stworzyłem funkcję countwordpersentence.R, która zlicza liczbę słów w zdaniu w danym ciągu tekstowym. W przypadku długiego tekstu zawierającego kilka zdań policzy słowa we wszystkich i wyświetli średnią liczbę słów w zdaniu oraz całkowitą liczbę słów.
Paul Rougieux
1
str_count (temp $ question1, "") +1 byłoby łatwe, gdybyś wiedział, że każde słowo jest oddzielone spacją. Znajduje się w bibliotece stringr.
Vivek Srivastava

Odpowiedzi:

24

Możesz używać strspliti sapplyfunkcji

sapply(strsplit(str1, " "), length)
AVSuresh
źródło
2
Tylko aktualizacja, że ​​możesz teraz użyć nieco nowej lengthsfunkcji w bazie R, która znajduje długość każdego elementu:lengths(strsplot(str, " "))
Nick Tierney
to bardzo dobrze problem jest, gdy masz coś takiego jak „słowo, słowo, słowo” w takim przypadku zwróci 1
Dimitrios Zacharatos
71

Użyj symbolu wyrażenia regularnego, \\Waby dopasować znaki niebędące słowami, użyj, +aby wskazać jeden lub więcej w wierszu, a także, gregexpraby znaleźć wszystkie dopasowania w ciągu. Słowa to liczba separatorów słów plus 1.

lengths(gregexpr("\\W+", str1)) + 1

To nie z pustymi strunami na początku lub na końcu wektora znaków, gdy „Słowo” nie spełnia \\W„s pojęcie nie-słowa (można pracować z innych wyrażeń regularnych \\S+, [[:alpha:]]itp, ale nie będzie zawsze być skrajnymi przypadkami z podejściem regex), itp. Prawdopodobnie jest bardziej wydajne niż strsplitrozwiązania, które przydzielą pamięć dla każdego słowa. Wyrażenia regularne opisano w ?regex.

Aktualizacja Jak zauważono w komentarzach i w innej odpowiedzi @Andri, podejście kończy się niepowodzeniem z ciągami (zero) i jednowyrazowymi oraz z końcową interpunkcją

str1 = c("", "x", "x y", "x y!" , "x y! z")
lengths(gregexpr("[A-z]\\W+", str1)) + 1L
# [1] 2 2 2 3 3

Wiele innych odpowiedzi również zawodzi w tych lub podobnych przypadkach (np. Wiele spacji). Myślę, że moje zastrzeżenie dotyczące „pojęcia jednego słowa” w pierwotnej odpowiedzi obejmuje problemy z interpunkcją (rozwiązanie: wybierz inne wyrażenie regularne, np. [[:space:]]+), Ale problemem są przypadki z zerami i jednym słowem; Rozwiązanie @ Andri nie rozróżnia między zerem a jednym słowem. Można więc przyjąć „pozytywne” podejście do znajdowania słów

sapply(gregexpr("[[:alpha:]]+", str1), function(x) sum(x > 0))

Prowadzący do

sapply(gregexpr("[[:alpha:]]+", str1), function(x) sum(x > 0))
# [1] 0 1 2 2 3

Ponownie wyrażenie regularne można by udoskonalić pod kątem różnych pojęć „słowo”.

Podoba mi się użycie, gregexpr()ponieważ jest wydajne pod względem pamięci. Alternatywą jest używanie strsplit()(jak @ user813966, ale z wyrażeniem regularnym do oddzielania słów) i używanie oryginalnego pojęcia rozdzielania słów

lengths(strsplit(str1, "\\W+"))
# [1] 0 1 2 2 3

To wymaga przydzielenia nowej pamięci dla każdego tworzonego słowa i dla pośredniej listy słów. Może to być stosunkowo kosztowne, gdy dane są „duże”, ale prawdopodobnie jest skuteczne i zrozumiałe w większości zastosowań.

Martin Morgan
źródło
str1 <- c('s ss sss ss', "asdf asd hello this is your life!"); sapply(gregexpr("\\W+", str1), length) + 1zwroty 4i 8. Pierwsza poprawna, druga za dużo. Myślę, że liczy się interpunkcja.
Francis Smart
Myślę, że liczy interpunkcję na końcu zdania. Całkiem pewny, że chciałbyś powiedzieć regex, aby ignorował dopasowania początkowe i końcowe (przepraszam, nie jest to dobre, bo sam bym to naprawił).
Francis Smart
sapply(gregexpr("\\W+", "word"), length) + 1zwraca 2
kod jaycode
Dzięki @fsmart - myślę, że obawy dotyczące interpunkcji są objęte zastrzeżeniem dotyczącym „pojęcia braku wyrazu” w oryginalnej odpowiedzi. Zaktualizowałem odpowiedź.
Martin Morgan
Dzięki @jaycode, brak możliwości zliczenia 1 (lub zera) słów jest problemem. Zaktualizowałem oryginalną odpowiedź.
Martin Morgan
49

Najprostszy sposób to:

require(stringr)
str_count("one,   two three 4,,,, 5 6", "\\S+")

... licząc wszystkie sekwencje na znaki inne niż spacje ( \\S+).

Ale co z małą funkcją, która pozwala nam również zdecydować, które rodzaje słów chcielibyśmy policzyć i która działa również na całych wektorach ?

require(stringr)
nwords <- function(string, pseudo=F){
  ifelse( pseudo, 
          pattern <- "\\S+", 
          pattern <- "[[:alpha:]]+" 
        )
  str_count(string, pattern)
}

nwords("one,   two three 4,,,, 5 6")
# 3

nwords("one,   two three 4,,,, 5 6", pseudo=T)
# 6
petermeissner
źródło
37

Używam str_countfunkcji z stringrbiblioteki z sekwencją ucieczki, \wktóra reprezentuje:

dowolny znak `` słowa '' (litera, cyfra lub podkreślenie w bieżącym języku: w trybie UTF-8 uwzględniane są tylko litery i cyfry ASCII)

Przykład:

> str_count("How many words are in this sentence", '\\w+')
[1] 7

Ze wszystkich pozostałych 9 odpowiedzi, które udało mi się przetestować, tylko dwie (autorstwa Vincenta Zoonekynda i Petermeissnera) działały dla wszystkich przedstawionych dotychczas danych wejściowych, ale one również wymagają stringr.

Ale tylko to rozwiązanie działa ze wszystkimi dotychczas przedstawionymi danymi wejściowymi oraz wejściami takimi jak "foo+bar+baz~spam+eggs"lub "Combien de mots sont dans cette phrase ?".

Reper:

library(stringr)

questions <-
  c(
    "", "x", "x y", "x y!", "x y! z",
    "foo+bar+baz~spam+eggs",
    "one,   two three 4,,,, 5 6",
    "How many words are in this sentence",
    "How  many words    are in this   sentence",
    "Combien de mots sont dans cette phrase ?",
    "
    Day after day, day after day,
    We stuck, nor breath nor motion;
    "
  )

answers <- c(0, 1, 2, 2, 3, 5, 6, 7, 7, 7, 12)

score <- function(f) sum(unlist(lapply(questions, f)) == answers)

funs <-
  c(
    function(s) sapply(gregexpr("\\W+", s), length) + 1,
    function(s) sapply(gregexpr("[[:alpha:]]+", s), function(x) sum(x > 0)),
    function(s) vapply(strsplit(s, "\\W+"), length, integer(1)),
    function(s) length(strsplit(gsub(' {2,}', ' ', s), ' ')[[1]]),
    function(s) length(str_match_all(s, "\\S+")[[1]]),
    function(s) str_count(s, "\\S+"),
    function(s) sapply(gregexpr("\\W+", s), function(x) sum(x > 0)) + 1,
    function(s) length(unlist(strsplit(s," "))),
    function(s) sapply(strsplit(s, " "), length),
    function(s) str_count(s, '\\w+')
  )

unlist(lapply(funs, score))

Wynik:

6 10 10  8  9  9  7  6  6 11
arekolek
źródło
To podejście jest doskonałe, ale wciąż napotykam na to, że podwójnie liczy słowa, które zawierają apostrof (np. „Jestem” lub „Jana”). Czy jest jakiś sposób na rozwiązanie tego problemu?
Thredolsen
2
@ Thredolsen, jeśli jesteś pewien, że nie będzie apostrofów, które powinny być traktowane jako separatory słów, możesz użyć klasy znaków '[\\w\']+'(nie można tego przetestować, więc xkcd.com/1638 może mieć zastosowanie), w przeciwnym razie nie jestem pewien, czy regex jest wystarczająco potężny, aby sobie z tym poradzić w ogólnym przypadku :)
arekolek
1
Nie jestem pewien, czy to dobre założenie, ale jeśli po apostrofie zawsze jest tylko jedna lub dwie litery, '\\w+(\'\\w{1,2})?'może to być dobre rozwiązanie.
arekolek
Dziękuję Ci. Oba podejścia działają w większości, ale „[\\ w \ '] +” wydaje się być lepsze w moim przypadku, ponieważ niektóre słowa zawierają więcej niż 2 znaki po apostrofie (np. Godzina). Powiązane pytanie uzupełniające: czy istnieje sposób, aby wykluczyć również przypadki, w których po dwukropku występuje bezpośrednio znak numeryczny (np. Licz „10:15” jako jedno słowo, a nie dwa)?
Thredolsen
2
W tym komentarzu będę używał zwykłej składni wyrażeń regularnych, więc przykłady będą wymagały dodatkowych ukośników odwrotnych. Aby zakryć słowa takie jak o'clocki friggin'możesz to zrobić \w+('\w*)?(nie wiem, czy są słowa zaczynające się od apostrofu?). Aby dodatkowo obsłużyć godziny, możesz spróbować je dopasować \d?\d:\d\d|\w+('\w*)?lub zrobić coś jeszcze bardziej skomplikowanego w zależności od potrzeb. Ale coraz mniej dotyczy to języka R, a więcej dotyczy tego, jak definiujesz słowo, więc może możesz zadać osobne pytanie, aby zaspokoić swoje konkretne potrzeby?
arekolek
15
str2 <- gsub(' {2,}',' ',str1)
length(strsplit(str2,' ')[[1]])

Te gsub(' {2,}',' ',str1)marki, że wszystkie słowa są oddzielone tylko jednej przestrzeni, poprzez zastąpienie wszystkich wystąpień dwóch lub więcej pomieszczeń z jednego miejsca.

strsplit(str,' ')Dzieli zdanie w każdej przestrzeni i zwraca wynik w postaci listy. [[1]]Chwyta wektor słów z tej listy. lengthLiczy się jak najwięcej słów.

> str1 <- "How many words are in this     sentence"
> str2 <- gsub(' {2,}',' ',str1)
> str2
[1] "How many words are in this sentence"
> strsplit(str2,' ')
[[1]]
[1] "How"      "many"     "words"    "are"      "in"       "this"     "sentence"
> strsplit(str2,' ')[[1]]
[1] "How"      "many"     "words"    "are"      "in"       "this"     "sentence"
> length(strsplit(str2,' ')[[1]])
[1] 7
kawa matematyczna
źródło
A co z tabulatorami, nowymi liniami lub nierozerwalnymi spacjami?
bartektartanus,
Sposób na wskrzeszenie 5-letniej odpowiedzi! Użyj '\ s' (w R, '\\ s'), aby dołączyć dowolny typ spacji zamiast ''.
mathematical.coffee
Otrzymałem powiadomienie o mojej odpowiedzi i spojrzałem na innych, aby je nieco poprawić: D Nie wściekaj się! :) PS. Lubię też matematykę i kawę!
bartektartanus
13

Możesz użyć str_match_all, z wyrażeniem regularnym, które identyfikowałoby twoje słowa. Poniższe informacje dotyczą spacji początkowych, końcowych i powielonych.

library(stringr)
s <-  "
  Day after day, day after day,
  We stuck, nor breath nor motion;
"
m <- str_match_all( s, "\\S+" )  # Sequences of non-spaces
length(m[[1]])
Vincent Zoonekynd
źródło
11

Wypróbuj tę funkcję z stringipakietu

   require(stringi)
   > s <- c("Lorem ipsum dolor sit amet, consectetur adipisicing elit.",
    +        "nibh augue, suscipit a, scelerisque sed, lacinia in, mi.",
    +        "Cras vel lorem. Etiam pellentesque aliquet tellus.",
    +        "")
    > stri_stats_latex(s)
        CharsWord CharsCmdEnvir    CharsWhite         Words          Cmds        Envirs 
              133             0            30            24             0             0 
bartektartanus
źródło
6
@bartektartanusthat to niezła funkcjonalność!
John
5
Dziękuję :) Sprawdź pozostałe funkcje z tego pakietu! Na pewno znajdziesz coś ciekawego :) Wszelkie uwagi mile widziane!
bartektartanus
7

Możesz użyć funkcji wc w bibliotece qdap :

> str1 <- "How many words are in this sentence"
> wc(str1)
[1] 7
yuqian
źródło
6

Możesz usunąć podwójne spacje i policzyć liczbę " "w ciągu, aby uzyskać liczbę słów. Użyj stringr i rm_white{ qdapRegex }

str_count(rm_white(s), " ") +1
Murali Menon
źródło
5

Spróbuj tego

length(unlist(strsplit(str1," ")))
Sangram
źródło
5

Również z stringipakietu, prosta funkcjastri_count_words

stringi::stri_count_words(str1)
#[1] 7
Sotos
źródło
4

Rozwiązanie 7 nie daje prawidłowego wyniku w przypadku, gdy jest tylko jedno słowo. Powinieneś nie tylko liczyć elementy w wyniku gregexpr (czyli -1, jeśli tam, gdzie nie pasuje), ale policzyć elementy> 0.

Ergo:

sapply(gregexpr("\\W+", str1), function(x) sum(x>0) ) + 1 
Andri
źródło
Będzie to nadal powodować problemy, jeśli str1zaczyna się lub kończy znakami niebędącymi słowami. Jeśli to dotyczy, ta wersja będzie szukać tylko spacji między słowami:sapply(gregexpr("\\b\\W+\\b", str, perl=TRUE), function(x) sum(x>0) ) + 1
Adam Bradley,
4
require(stringr)
str_count(x,"\\w+")

będzie dobrze z podwójnymi / potrójnymi odstępami między słowami

We wszystkich innych odpowiedziach występuje więcej niż jedna spacja między wyrazami.

CJunk
źródło
2

wymagają (stringr)

Zdefiniuj bardzo prostą funkcję

str_words <- function(sentence) {

  str_count(sentence, " ") + 1

}

Czek

str_words(This is a sentence with six words)
JDie
źródło
1

Posługiwać się nchar

jeśli wywoływany jest wektor ciągów x

(nchar(x) - nchar(gsub(' ','',x))) + 1

Sprawdź liczbę spacji, a następnie dodaj jedną

Jonny
źródło
1

Zauważyłem, że następująca funkcja i wyrażenie regularne są przydatne do liczenia słów, zwłaszcza w przypadku łączników pojedynczych i podwójnych, gdzie ta pierwsza generalnie nie powinna być liczona jako podział wyrazu, np. Dobrze znane, hi-fi; podczas gdy podwójny łącznik jest separatorem interpunkcyjnym, który nie jest ograniczony białymi znakami - na przykład w przypadku uwag w nawiasach.

txt <- "Don't you think e-mail is one word--and not two!" #10 words
words <- function(txt) { 
length(attributes(gregexpr("(\\w|\\w\\-\\w|\\w\\'\\w)+",txt)[[1]])$match.length) 
}

words(txt) #10 words

Stringi to przydatny pakiet. Jednak wyrazy w tym przykładzie są zbyt liczone z powodu łącznika.

stringi::stri_count_words(txt) #11 words
Soren
źródło
0

Ze stringr pakietu można również napisać prosty skrypt, który mógłby przejść przez wektor łańcuchów, na przykład przez pętlę for.

Powiedzmy

df $ tekst

zawiera wektor ciągów, które chcemy przeanalizować. Najpierw dodajemy dodatkowe kolumny do istniejącej ramki danych df, jak poniżej:

df$strings    = as.integer(NA)
df$characters = as.integer(NA)

Następnie uruchamiamy pętlę for na wektorze ciągów, jak poniżej:

for (i in 1:nrow(df)) 
{
   df$strings[i]    = str_count(df$text[i], '\\S+') # counts the strings
   df$characters[i] = str_count(df$text[i])         # counts the characters & spaces
}

Wynikowe kolumny: łańcuchy i znak będą zawierały liczbę słów i znaków, a zostanie to osiągnięte za jednym razem dla wektora ciągów.

Piaszczysty
źródło