Código desglosado
# Código modificado
raw_test <- read.csv("https://sites.google.com/site/nubededatosblogspotcom/crea.txt",
header = FALSE) # Importa como data frame
sorted_words <- as.table(as.matrix(raw_test)) # Convierte data frame a table
# Código idéntico al de Rasmus Bååth
correct <- function(word) {
# Calcula la distancia entre la palabra y el resto de palabras ordenadas (sorted words).
edit_dist <- adist(word, sorted_words)
# Calcula la distancia mínima y devuelve una palabra existente en crea.txt
# con un límite de 2 ediciones.
min_edit_dist <- min(edit_dist, 2)
# Genera un vector con todas las palabras con el mínimo de distancia.
# Como sorted_words está ordenada de más a menos común, el vector
# resultante tendrá la primera coincidencia más común/probable.
proposals_by_prob <- c(sorted_words[ edit_dist <= min(edit_dist, 2)])
# En caso de que proposals_by_prob esté vacío asignamos la palabra evaluada
proposals_by_prob <- c(proposals_by_prob, word)
# ... y devuelve la palabra primera/más probable en el vector.
proposals_by_prob[1]
}
Funcionamiento
correct("correrctor")
[1] "corrector"
correct("ortogrfaico")
[1] "ortográfico"
correct("wn")
[1] "en"
correct("foncionamento")
[1] "funcionamiento"
El código define la función correct y como argumento incluimos entre comillas la palabra evaluada. La distancia máxima entre la palabra evaluada y el objetivo (una palabra incluida en el corpus, correcta) es de dos ediciones. Pues, de acuerdo a Norvig, la literatura sobre el tema afirma que la inmensa mayoría de errores ortográficos se encuentran a una distancia de 2 o 1. Si la palabra está incluida en el corpus o si no encuentra una corrección de la misma en dos ediciones, no corregirá dicha palabra.
# Distancia 2 ediciones
correct("edictioness")
[1] "ediciones"
# Distancia 3 ediciones
correct("edictiomess")
[1] "edictiomess"
Notas
Básicamente el código original, prácticamente idéntico al código con nuestro propio corpus, extraía las palabras del fichero big.txt de Norvig y creaba un vector en orden descendente en función de la frecuencia de las palabras. Inicialmente pensé que bastaría con crear fichero equivalente en español, concatenando textos en español del proyecto Gutenberg.
Pero buscando un listado de palabras más frecuentes encontré el Corpus de Referencia del Español Actual (CREA), un listado de frecuencias de palabras. Las ventajas de utilizar el CREA frente a una recopilación de textos mía son:
- Mayor representatividad de la frecuencia de las palabras por la selección de texto de la RAE.Una desventaja es que si quisiéramos hacer un corrector más sofisticado, como por ejemplo tener en cuenta el contexto de la palabra dentro del texto, tal y como menciona Norvig, el Corpus de Referencia del Español Actual (CREA) no sería de ayuda.- Simplificación del código pues ya no hay que extraer o contar la frecuencia de cada palabra.
- Fichero de texto de menor tamaño.
La lista total de frecuencias comprende 737.799 palabras, pero para ganar en rapidez he seleccionado las primeras 100.000 en el fichero crea.txt. El código anterior (código desglosado) importa este fichero de texto, ya en orden descendente, como data frame y lo transforma en la clase table de R. Después utiliza la función creada por Rasmus Bååth.
Código con nuestro propio corpus
En el caso de que queramos crear nuestro propio corpus agrupando texto en español.
# En dos líneas
sorted_words <- names(sort(table(strsplit(tolower(paste(readLines("corpus.txt"), collapse = " ")), "[^abcdefghijklmnñopqrstuvwxyzáéíóúäëïöüçàèìòùâêîôû']+")), decreasing = TRUE))
correct <- function(word) { c(sorted_words[ adist(word, sorted_words) <= min(adist(word, sorted_words), 2)], word)[1] }
# Desglosado
raw_text <- paste(readLines("corpus.txt"), collapse = " ")
split_text <- strsplit(raw_text, "[^abcdefghijklmnñopqrstuvwxyzáéíóúäëïöüçàèìòùâêîôû']+")
sorted_words <- as.table(as.matrix(data.frame(split_text)))
correct <- function(word) {
edit_dist <- adist(word, sorted_words)
min_edit_dist <- min(edit_dist, 2)
proposals_by_prob <- c(sorted_words[ edit_dist <= min(edit_dist, 2)])
proposals_by_prob <- c(proposals_by_prob, word)
proposals_by_prob[1]
}
He sustituido el rango de caracteres "[^a-z]+" por "[^abcdefghijklmnñopqrstuvwxyzáéíóúäëïöüçàèìòùâêîôû']+" especificando los caracteres para que al crear el vector con las palabras no las divida erróneamente cuando encuentre acentos, apostrofes o letras como la ñ y la ç.
Código en dos líneas
# Código modificado
sorted_words <- as.table(as.matrix(read.csv("https://sites.google.com/site/nubededatosblogspotcom/crea.txt", header = FALSE)))
# Código idéntico al de Rasmus Bååth
correct <- function(word) { c(sorted_words[ adist(word, sorted_words) <= min(adist(word, sorted_words), 2)], word)[1] }
# Código original de Rasmus Bååth
sorted_words <- names(sort(table(strsplit(tolower(paste(readLines("http://www.norvig.com/big.txt"), collapse = " ")), "[^a-z]+")), decreasing = TRUE))
correct <- function(word) { c(sorted_words[ adist(word, sorted_words) <= min(adist(word, sorted_words), 2)], word)[1] }
Referencias
Gracias por el aporte me ha sido super útil!
ResponderEliminarMe alegro Sara. ¿Le has dado alguna aplicación práctica? Gracias
EliminarHola Nube de datos. Excelente aporte. Muy bueno tu blog. Un saludo.
ResponderEliminarMuchas gracias por tu comentario, Alex. Un saludo.
Eliminarhola, la base de datos corpus.txt cual es ?
ResponderEliminarHola Daniel. En el ejemplo en la parte superior de esta entrada, utilizo como corpus un listado de palabras (CREA) de la Real Academia Española (RAE). En la parte inferior, a la que te refieres, sería en el caso de que quisieras crear o tuvieras otro listado o corpus contra el que evaluar las palabras. A modo de ejemplo lo he llamado corpus.txt.
EliminarExcelente aporte. Lo utilizaré para validar datos mal digitados por usuarios
ResponderEliminarFenomenal. Muchas gracias por el comentario. Saludos.
Eliminaruna pregunta si tengo mi dataframe con palabras erroneas y ese dataframe se llama dtm_d sustituyo "word" y pongo ahi dtm_d para que se haga el corregimiento de palabras verdad?
ResponderEliminar