Mostrando entradas con la etiqueta Función en R. Mostrar todas las entradas
Mostrando entradas con la etiqueta Función en R. Mostrar todas las entradas

2015-01-15

Corrector ortográfico en español para R

Title Hace algunos años Peter Norvig escribió el artículo How to Write a Spelling Corrector, creando un corrector ortográfico en 21 líneas de Python. Recientemente, encontré una versión creada por Rasmus Bååth para R, Peter Norvig's Spell Checker in Two Lines of Base R. En esta entrada me baso en el código de Rasmus Bååth para crear una versión en español. Antes de continuar, recomiendo leer los artículos anteriores.

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.

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

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.

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

2014-11-02

Regresión lineal simple para principiantes en R

Title La regresión lineal simple sirve para evaluar la relación entre dos variables, una variable independiente X o predictora y una variable dependiente o explicada Y. Se trata de encontrar una ecuación lineal con fines predictivos.

Datos

Como ejemplo, usamos los datos cats del peso corporal y del corazón de una muestra gatos machos y hembras del paquete MASS en R.

install.packages("MASS") # Si no está instalado
library(MASS)
str(cats)
summary(cats)
str(cats)
'data.frame': 144 obs. of  3 variables:
 $ Sex: Factor w/ 2 levels "F","M": 1 1 1 1 1 1 1 1 1 1 ...
 $ Bwt: num  2 2 2 2.1 2.1 2.1 2.1 2.1 2.1 2.1 ...
 $ Hwt: num  7 7.4 9.5 7.2 7.3 7.6 8.1 8.2 8.3 8.5 ...
Sex - sexo - factor con los niveles hembras "F" y machos "M"
Bwt - Body Weight, peso corporal
Hwt - Heart Weight, peso del corazón

summary(cats)
 Sex         Bwt             Hwt       
 F:47   Min.   :2.000   Min.   : 6.30  
 M:97   1st Qu.:2.300   1st Qu.: 8.95  
        Median :2.700   Median :10.10  
        Mean   :2.724   Mean   :10.63  
        3rd Qu.:3.025   3rd Qu.:12.12  
        Max.   :3.900   Max.   :20.50 

Diagrama de dispersión

with(cats, plot(Hwt ~ Bwt)) # Alternativa 1
plot(cats$Bwt, cats$Hwt, xlab = "Bwt", ylab = "Hwt") # Alternativa 2
plot(Hwt ~ Bwt, cats)  # Alternativa 3

Recta de regresión y R²

reglineal <- lm(cats$Hwt ~ cats$Bwt) # Alternativa 1
reglineal <- lm(Hwt ~ Bwt, cats) # Alternativa 2
abline(reglineal, col = "red") # Añadimos recta de regresión
# Pendiente e intersección
reglineal
   # Alternativas:
  reglineal[[1]]
  summary(reglineal)$coef[, 1]
  reglineal$coef
Call:
lm(formula = cats$Hwt ~ cats$Bwt)

Coefficients:
(Intercept)     cats$Bwt  
    -0.3567       4.0341  
(Intercept) - b, intersección Y
cats$Bwt - m, pendiente

Más información:

summary(reglineal)
 Call:
lm(formula = cats$Hwt ~ cats$Bwt)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.5694 -0.9634 -0.0921  1.0426  5.1238 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.3567     0.6923  -0.515    0.607    
cats$Bwt      4.0341     0.2503  16.119   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.452 on 142 degrees of freedom
Multiple R-squared:  0.6466, Adjusted R-squared:  0.6441 
F-statistic: 259.8 on 1 and 142 DF,  p-value: < 2.2e-16

Aplicación del modelo

Con la ecuación de la recta de regresión podemos calcular el valor de la variable dependiente Y introduciendo un valor de la variable independiente X en la misma: y = mx + b

Accedemos a la pendiente y la intersección en Y:

# Intersección
  # coef o coefficients son intercambiables
summary(reglineal)$coef[1, 1]
reglineal$coef[1]  
coef(reglineal)[1] # Sintaxis recomendada
(Intercept) 
 -0.3566624 
# Pendiente
summary(reglineal)$coef[2, 1]
reglineal$coef[2] 
coef(reglineal)[2] 
cats$Bwt 
4.034063

Ejemplos:

Si un gato pesa 5 Kg, ¿cuál es el peso estimado de su corazón?

# Gato de 5kg
pesocorazon <- 5*coef(reglineal)[2]+coef(reglineal)[1] 
pesocorazon
     Bwt 
19.81365 
Si un gato pesa 3 Kg, ¿cuál es el peso estimado de su corazón?

# Gato de 3kg
pesocorazon <- 3*coef(reglineal)[2]+coef(reglineal)[1] 
pesocorazon
     Bwt 
11.74553 

Función

Creamos una sencilla función que nos pregunte el peso del gato en la consola y calcule el peso estimado de su corazón.

fun <- function(){
  x1 <- as.numeric(readline("¿Cuál es peso del gato en kg?"))
    # Check if is NA instead of lenght >1 as we did with  
  if (is.na(x1))  {
    x1 <- 0
  }
  reglineal <- lm(cats$Hwt ~ cats$Bwt)
  pesocorazon <- x1*coef(reglineal)[2]+coef(reglineal)[1]
  print(c("El peso estimado del corazón en gramos es:", 
          round(unname(pesocorazon), 5)), quote = FALSE)
}
¿Cuál es peso del gato en kg?3
[1] El peso estimado del corazón en gramos es:
[2] 11.74553  

Bondad del modelo

El Coeficiente de correlación de Pearson r, nos indica el grado de correlación lineal entre la dos variables. Es la covarianza dividida por el producto de las desviaciones típicas de las dos variables

    -1 - correlación negativa perfecta
    0 - no existe relación lineal
    1 - correlación positiva perfecta

# Coeficiente de correlación de Pearson
cor(cats$Bwt, cats$Hwt)
[1] 0.8041274
Como es próximo a 1, concluimos que existe una correlación lineal positiva entre ambas variables.

El coeficiente de determinación R², nos informa de la bondad del ajuste del modelo. Se calcula elevando al cuadrado el coeficiente de correlación de Pearson. Varía entre 0 y 1. Podemos acceder a él mediante la función summary.lm, como hicimos anteriormente.

    0 - las variables son independientes
    1 - existe una relación perfecta entre las variables

# Coeficiente de determinación
cor(cats$Bwt, cats$Hwt)^2
summary(reglineal)$r.squared  # Mediante summary.lm
[1] 0.6466209
En nuestro ejemplo es 0,6466. Indica que conociendo el peso de un gato mejoramos un 64,66% nuestra estimación del peso del corazón, si usamos nuestro modelo en lugar del peso medio del corazón de un gato.

Referencias

Nube de datos