Cuente el número de todas las palabras en una cadena

82

¿Existe una función para contar el número de palabras en una cadena? Por ejemplo:

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

para devolver un resultado de 7.

Juan
fuente
Basado en la respuesta de @ Martin a continuación, creé una función countwordpersentence.R que cuenta el número de palabras por oración en una cadena de texto dada. Para un texto largo que contiene varias oraciones, contará las palabras en todas ellas y dará como resultado el número medio de palabras por oración y el número total de palabras.
Paul Rougieux
1
str_count (temp $ question1, "") +1 sería fácil si sabes que cada palabra está separada por un espacio. Está en la biblioteca stringr.
Vivek Srivastava

Respuestas:

22

Puede utilizar strsplity sapplyfunciones

sapply(strsplit(str1, " "), length)
AVSuresh
fuente
Solo una actualización que ahora puede usar la lengthsfunción algo nueva en la base R, que encuentra la longitud de cada elemento:lengths(strsplot(str, " "))
Nick Tierney
esto es muy bueno el problema es cuando tienes algo como "palabra, palabra, palabra" en ese caso devolverá 1
Dimitrios Zacharatos
71

Utilice el símbolo de expresión regular \\Wpara hacer coincidir los caracteres que no son palabras, utilizando +para indicar uno o más en una fila, junto con gregexprpara buscar todas las coincidencias en una cadena. Las palabras son el número de separadores de palabras más 1.

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

Este fallará con cadenas en blanco al principio o al final del vector de caracteres, cuando una "palabra" no satisface \\W's noción de no-palabra (se podría trabajar con otras expresiones regulares, \\S+, [[:alpha:]], etc., pero siempre habrá ser casos extremos con un enfoque de expresiones regulares), etc. Es probable que sea más eficiente que las strsplitsoluciones, que asignarán memoria para cada palabra. Las expresiones regulares se describen en ?regex.

Actualización Como se señaló en los comentarios y en una respuesta diferente de @Andri, el enfoque falla con (cero) y cadenas de una palabra, y con puntuación final

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

Muchas de las otras respuestas también fallan en estos o casos similares (p. Ej., Espacios múltiples). Creo que la advertencia de mi respuesta sobre la "noción de una palabra" en la respuesta original cubre problemas con la puntuación (solución: elija una expresión regular diferente, por ejemplo, [[:space:]]+), pero los casos de una palabra y cero son un problema; La solución de @ Andri no distingue entre cero y una palabra. Así que adoptar un enfoque 'positivo' para encontrar palabras que uno podría

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

Llevando a

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

Nuevamente, la expresión regular podría refinarse para diferentes nociones de "palabra".

Me gusta el uso de gregexpr()porque es eficiente en la memoria. Una alternativa al usar strsplit()(como @ user813966, pero con una expresión regular para delimitar palabras) y hacer uso de la noción original de delimitar palabras es

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

Esto necesita asignar nueva memoria para cada palabra que se crea y para la lista intermedia de palabras. Esto podría resultar relativamente caro cuando los datos son "grandes", pero probablemente sea eficaz y comprensible para la mayoría de los propósitos.

Martín Morgan
fuente
str1 <- c('s ss sss ss', "asdf asd hello this is your life!"); sapply(gregexpr("\\W+", str1), length) + 1devuelve 4y 8. Primero correcto, segundo demasiado. Creo que está contando la puntuación.
Francis Smart
Creo que está contando la puntuación al final de la oración. Estoy bastante seguro de que querrá decirle a regex que ignore las coincidencias de inicio y finalización (lo siento, no es bueno o lo arreglaría yo mismo).
Francis Smart
sapply(gregexpr("\\W+", "word"), length) + 1devuelve 2
jaycode
Gracias @fsmart: creo que la preocupación por la puntuación está cubierta por el descargo de responsabilidad sobre la 'noción de no palabra' en la respuesta original. Actualicé la respuesta.
Martin Morgan
Gracias @jaycode, la incapacidad de contar 1 (o cero) palabras introducidas es un problema. Actualicé la respuesta original.
Martin Morgan
47

La forma más sencilla sería:

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

... contando todas las secuencias en caracteres sin espacios ( \\S+).

Pero, ¿qué pasa con una pequeña función que nos permite también decidir qué tipo de palabras nos gustaría contar y cuáles funcionan también en vectores completos ?

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
fuente
35

Uso la str_countfunción de la stringrbiblioteca con la secuencia de escape\w que representa:

cualquier carácter de 'palabra' (letra, dígito o subrayado en la configuración regional actual: en el modo UTF-8 solo se consideran letras y dígitos ASCII)

Ejemplo:

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

De todas las otras 9 respuestas que pude probar, solo dos (por Vincent Zoonekynd y por petermeissner) funcionaron para todas las entradas presentadas aquí hasta ahora, pero también requieren stringr.

Pero solo esta solución funciona con todas las entradas presentadas hasta ahora, más entradas como "foo+bar+baz~spam+eggs"o "Combien de mots sont dans cette phrase ?".

Punto de referencia:

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))

Salida:

6 10 10  8  9  9  7  6  6 11
arekolek
fuente
Este enfoque es excelente, pero un problema con el que todavía me encuentro es que cuenta dos veces las palabras que contienen un apóstrofe (por ejemplo, "I'm" o "John's"). ¿Hay alguna forma de abordar esto?
Thredolsen
2
@Thredolsen, si está seguro de que no habrá apóstrofos que deban tratarse como separadores de palabras, puede usar una clase de caracteres '[\\w\']+'(no se puede probar, por lo que puede aplicarse xkcd.com/1638 ); de lo contrario, no estoy seguro de si regex es lo suficientemente potente como para manejarlo en el caso general :)
arekolek
1
No estoy seguro si esa es una buena suposición, pero si siempre hay solo una o dos letras después del apóstrofe, entonces '\\w+(\'\\w{1,2})?'podría ser una buena solución.
arekolek
Gracias. Ambos enfoques funcionan en su mayor parte, pero '[\\ w \'] + 'parece ser mejor en mi caso, ya que algunas palabras contienen más de 2 caracteres después de un apóstrofe (por ejemplo: en punto). Pregunta de seguimiento relacionada: ¿hay alguna forma de excluir también los casos en los que dos puntos son seguidos directamente por un carácter numérico (por ejemplo, cuente '10: 15 'como una palabra, en lugar de dos)?
Thredolsen
2
En este comentario, usaré una sintaxis de expresiones regulares simple, por lo que los ejemplos necesitarán algunas barras invertidas adicionales. Para cubrir palabras como o'clocky friggin'podrías hacer \w+('\w*)?(no sé si hay palabras que comiencen con apóstrofe). Para manejar adicionalmente las horas, puede intentar hacerlas coincidir \d?\d:\d\d|\w+('\w*)?o hacer algo aún más complicado según sus necesidades. Pero esto se trata cada vez menos de R y más de cómo define una palabra, por lo que tal vez pueda publicar una pregunta separada para cubrir sus necesidades específicas.
arekolek
15
str2 <- gsub(' {2,}',' ',str1)
length(strsplit(str2,' ')[[1]])

Se gsub(' {2,}',' ',str1)asegura de que todas las palabras estén separadas por un solo espacio, reemplazando todas las apariciones de dos o más espacios con un espacio.

El strsplit(str,' ')divide la frase en cada espacio y devuelve el resultado en una lista. El [[1]]toma el vector de palabras de esa lista. La lengthcuenta hasta cuántas palabras.

> 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
cafe matematico
fuente
¿Qué pasa con las pestañas, las nuevas líneas o los espacios irrompibles?
bartektartanus
¡Manera de resucitar una respuesta de 5 años! Utilice '\ s' (en R, '\\ s') para incluir cualquier tipo de espacio en blanco en lugar de ''.
mathematical.coffee
Recibí una notificación sobre mi respuesta y miré a otras para mejorarlas ligeramente: D ¡No te enojes! :) PD. ¡Me gustan las matemáticas y el café también!
bartektartanus
13

Puede usar str_match_all, con una expresión regular que identifique sus palabras. A continuación se trabaja con espacios iniciales, finales y duplicados.

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
fuente
11

Prueba esta función del stringipaquete

   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
fuente
6
@bartektartanusthat es una buena funcionalidad!
Juan
5
Gracias :) ¡Consulta el resto de funciones de este paquete! Estoy seguro de que encontrará algo interesante :) ¡Cualquier comentario es bienvenido!
bartektartanus
7

Puede usar la función wc en la biblioteca qdap :

> str1 <- "How many words are in this sentence"
> wc(str1)
[1] 7
Yuqian
fuente
6

Puede eliminar los espacios dobles y contar el número de " "en la cadena para obtener el recuento de palabras. Utilice stringr y rm_white{ qdapRegex }

str_count(rm_white(s), " ") +1
Murali Menon
fuente
5

Prueba esto

length(unlist(strsplit(str1," ")))
Sangram
fuente
5

También del stringipaquete, la función directastri_count_words

stringi::stri_count_words(str1)
#[1] 7
Sotos
fuente
4

La solución 7 no da el resultado correcto en el caso de que solo haya una palabra. No solo debe contar los elementos en el resultado de gregexpr (que es -1 si no coincide), sino contar los elementos> 0.

Es decir:

sapply(gregexpr("\\W+", str1), function(x) sum(x>0) ) + 1 
Andri
fuente
Esto seguirá teniendo problemas si str1comienza o termina con caracteres que no son palabras. Si eso es una preocupación, esta versión solo buscará espacios entre palabras:sapply(gregexpr("\\b\\W+\\b", str, perl=TRUE), function(x) sum(x>0) ) + 1
Adam Bradley
4
require(stringr)
str_count(x,"\\w+")

estará bien con espacios dobles / triples entre palabras

Todas las demás respuestas tienen problemas con más de un espacio entre las palabras.

CJunk
fuente
2

require (stringr)

Define una función muy simple

str_words <- function(sentence) {

  str_count(sentence, " ") + 1

}

Cheque

str_words(This is a sentence with six words)
JDie
fuente
1

Utilizar nchar

si se llama vector de cadenas x

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

Averigüe la cantidad de espacios y luego agregue uno

Jonny
fuente
1

He encontrado que la siguiente función y expresión regular son útiles para el recuento de palabras, especialmente al tratar con guiones simples o dobles, donde el primero generalmente no debería contar como un salto de palabra, por ejemplo, bien conocido, de alta fidelidad; mientras que el guión doble es un delimitador de puntuación que no está delimitado por espacios en blanco, como ocurre con los comentarios entre paréntesis.

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 es un paquete útil. Pero cuenta en exceso las palabras en este ejemplo debido al guión.

stringi::stri_count_words(txt) #11 words
Soren
fuente
0

Con stringr paquete , también se puede escribir un script simple que pueda atravesar un vector de cadenas, por ejemplo, a través de un bucle for.

Digamos

df $ texto

contiene un vector de cadenas que nos interesa analizar. Primero, agregamos columnas adicionales al marco de datos existente df como se muestra a continuación:

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

Luego ejecutamos un bucle for sobre el vector de cadenas de la siguiente manera:

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
}

Las columnas resultantes: cadenas y carácter contendrán los recuentos de palabras y caracteres y esto se logrará de una sola vez para un vector de cadenas.

Arenoso
fuente