es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Buscando una alternativa más rápida para sapply()

He escrito una función que cuenta el número de palabras (unigramas) en una oración:

library(ngram)
library(stringi)
library(tidyverse)
set.seed(123)
get_unigrams <- function(text) {
  sapply(text, function(text){
    unigram<-  ngram(text, n = 1) %>% get.ngrams() %>% length()
    return(unigram)
  })
}

Para hacer esto, utilicé la función sapply que aplica mi función get_unigrams a cada fila en el conjunto de datos. Esto también funciona hasta ahora:

### ejemplo de conjunto de datos:
df<-sample.int(5, 5, replace = T) %>% 
  map(.,  ~ stri_randstrings(.x, 10) %>% paste(collapse = " ")) %>%
  unlist() %>% 
  tibble(text = .)

### aplicando mi función
df %>% mutate(n=get_unigrams((text)))

### A tibble: 5 x 2
text                                 n
  <chr>                            <int>
1 SxSgZ6tF2K xtgdzehXaH 9xtgn1TlDJ     3
2 E8PPM98ESG r2Rn7YC7kt Nf5NHoRoon     3
3 Rkdi0TDNbL 6FfPm6Qzts                2
4 A8eLeJBm5S VbKUxTtubP                2
5 9vI3wi8Yxa PeJJDMz958 gctfjWeomy     3

Sin embargo, dado que la función get_unigrams se aplica para cada fila, esto consume mucho tiempo. Por lo tanto, me gustaría preguntar si hay una alternativa rápida para la función sapply que acelere significativamente mi función get_unigrams.

### conjunto de datos con 50.000 filas:
df<-sample.int(50, 50000, replace = T) %>% 
  map(.,  ~ stri_randstrings(.x, 10) %>% paste(collapse = " ")) %>%
  unlist() %>% 
  tibble(text = .)

system.time({
  df %>% mutate(n=get_unigrams((text)))
})
User      System verstrichen
21.35        0.11       22.06

Para un conjunto de datos con 50.000 filas, mi función necesita 22.06 segundos (“verstrichen”). ¡Esto claramente es demasiado para mí! ¿Alguien puede ayudarme a aumentar la velocidad? ¿Quizás con una función vectorizada?

La construcción dentro de la función get_unigrams debe permanecer igual:

unigram <- ngram(text, n = 1) %>% get.ngrams() %>% length()
return(unigram)

Solo me refiero a la función sapply. ¡Muchas gracias de antemano!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, en lugar de usar sapply para recorrer cada fila del data frame, puedes utilizar el paquete stringr y un patrón regex para dividir cada oración en palabras y luego contar el número de elementos resultantes. Aquí tienes una versión actualizada de tu función que utiliza este enfoque:

    library(stringr)
    
    get_unigrams <- function(text) {
      unigrams <- str_split(text, "\\s+") %>% 
        lengths()
      return(unigrams)
    }
    

    La función str_split() divide cada oración en palabras utilizando el patrón regex \\s+, que coincide con uno o más caracteres de espacio en blanco. La lista resultante de vectores de palabras se pasa luego a lengths(), que devuelve un vector de la misma longitud que la lista de entrada, donde cada elemento es la longitud del vector de palabras correspondiente (es decir, el número de palabras en la oración).

    Luego puedes aplicar esta función actualizada a tu data frame utilizando mutate() de la siguiente manera:

    df %>% mutate(n = get_unigrams(text))
    

    Esto debería ser mucho más rápido que usar sapply, ya que la división y el conteo están vectorizados en lugar de realizarse en un bucle.

Comments are closed.