Datanalytics

Archivo

Entradas Etiquetadas ‘r’

Charla: un lematizador probabilístico con R

Lunes, 13 de mayo de 2013 Sin comentarios

El jueves 16 de mayo hablaré en el Grupo de Interés Local de Madrid de R sobre lematizadores probabilísticos.

Hablaré sobre el proceso de lematizacion y trataré de mostrar su importancia dentro del mundo del llamado procesamiento del lenguaje natural (NLP). La lematización es un proceso humilde dentro del NLP del que apenas nadie habla: su ejercicio solo ha hecho famoso a Martin Porter. Lo eclipsan otras aplicaciones más vistosas, como el siempre sobrevalorado análisis del sentimiento. Sin embargo, es una pieza fundamental que subyace (o debería subyacer) en cualquier aplicación seria que analice textos.

En la charla repasaré las tres grandes familias de soluciones para el problema de la lematización:

  • las basadas en reglas duras,
  • las basadas en diccionarios y, finalmente,
  • las más interesantes, las probabilísticas.

Y, en particular, describiré con cierto detalle —aunque tratando de obviar los aspectos técnicos más áridos— un algoritmo que combina oportunísticamente diccionarios y modelos ocultos de Markov y que debería ver la luz en producción dentro del conjunto de APIs lingüísticas de Molino de Ideas.

data.table (II): agregaciones

Jueves, 9 de mayo de 2013 5 comentarios

Sigo con mi lacónica serie sobre data.table.

La protagonista:

frases[sample(1:nrow(frases), 3),]
#pos.es pos.en length.es length.en en        es frase          tfe      qjilm          num
#1:     15     43        72        72  i        de  2632 4.881416e-02 0.01369863 6.686871e-04
#2:     33     48        46        48  X    países  5321 2.726146e-06 0.02040816 5.563563e-08
#3:      2     35        53        66 in preguntar  4582 2.424379e-08 0.01492537 3.618476e-10
dim(frases)
#[1] 6340091      10

El tiempo:

system.time({
  setkey(frases, "frase", "es")
  denominadores <- frases[, sum(num), by = key(frases)]
  setnames(denominadores, c("frase", "es", "den") )
  frases <- merge(frases, denominadores)
  frases$delta <- frases$num / frases$den
})
#user  system elapsed 
#5.628   0.208   5.841 

En particular,

system.time( denominadores <- frases[, sum(num), by = key(frases)] )
#user  system elapsed 
#0.228   0.000   0.228 

Increíble, ¿no?

Dependencias funcionales en R con foodweb

Miércoles, 8 de mayo de 2013 3 comentarios

El otro día tropecé con un problema de rendimiento con R y al utilizar Rprof() encontré muchas llamadas a funciones que yo no hacía directamente.

La principal sospechosa era la función daply (del paquete plyr) que parecía depender de bastantes otras. Uno puede navegar el código de las funciones para identificar esas dependencias, pero, mirad qué maravilla:

library(mvbutils)
library(plyr)
foodweb(find.funs("package:plyr"), prune = "laply")

genera

Ahí se ve la dependencia de daply con respecto a laply. Y uno adquiere, además, una visión panorámica del paquete plyr.

La función foodweb tiene argumentos adicionales (prune es uno de los más útiles) que los interesados podrán encontrar en ?foodweb.

Categories: computación, r Tags: , , ,

Mi primera aplicacion en Shiny: un detector de idiomas

Lunes, 6 de mayo de 2013 2 comentarios

Motivado por los experimentos de Gregorio Serrano con shiny e ilustrado por la charla que dio en el Grupo de Usuarios de R de Madrid, decidí colgar el otro día un entretenimiento que ocupó la mañana de un domingo —y las mañanas de mis domingos son proverbialmente breves— en la red.

Se trata de una aplicación que distingue el idioma en que está escrito un texto dentro de una selección de ellos: español, italiano, latín, francés, portugués y catalán.

¡Invitados quedáis a probarlo!

(Nota: esta aplicación me fue sugerida, en forma de reto, por Elena Álvarez de Molino de Ideas).

(Coda: cualquier día, por chinchar, hago uno que distinga entre catalán, valenciano y mallorquín.)

Categories: r Tags: , ,

Más sobre la ley de Benford (II): la distribución de la parte fraccionaria

Viernes, 3 de mayo de 2013 Sin comentarios

Continuamos hoy nuestra serie sobre la llamada ley de Benford discutiendo la distribución de la parte fraccionaria de las muestras de una distribución.

La parte fraccionaria de un número es, para entendernos, lo que va detrás de la coma. Técnicamente, x - floor(x). ¿Le sorprendería a alguien la parte fraccionaria de una secuencia aleatoria de números no tenga una distribución uniforme sobre [0,1)?

Obviamente, si los números son enteros no. ¿Pero si siguen la distribución normal? Se puede probar, de hecho, que si la serie sigue una distribución de probabilidad que sea

  • regular, es decir, que no tenga picos extraños y, más en concreto, cuya función de densidad crezca hasta cierto punto y decrezca de él en adelante y
  • extendida, es decir, que cubra un rango amplio de valores (p.e., la recta real entera),

entonces la distribución de la parte fraccionaria de sus muestras serán aproximadamente uniformes. Y lo serán tanto más cuanto menor sea el máximo de la función de distribución. La referencia, el artículo Pourquoi la loi de Benford n’est pas mystérieuse de Nicolas Gauvrit y Jean-Paul Delahaye.

Esto se verifica fácilmente en ciertos casos. Por ejemplo,

x <- rnorm(100000)
x <- x - floor(x)

par(mfrow=c(1,2))
hist(x, col = "grey")
qqplot(x, runif(100000), main = "qqplot")

que produce

En la siguiente entrega analizaremos qué tiene que ver esto con la ley de Benford.

Categories: estadística, r Tags: , ,

data.table (I): cruces

Jueves, 2 de mayo de 2013 4 comentarios

Los protagonistas (tres tablas grandecitas):

dim(qjilm)
# [1] 3218575 5
dim(tf)
# [1] 6340091 7
dim(tfe)
#[1] 1493772 3

head(qjilm, 2)
#pos.es length.en length.es pos.en qjilm
#1 1 2 1 1 0.8890203
#2 1 2 1 2 0.1109797

head(tf, 2)
#frase es pos.es length.es en pos.en length.en
#1 996 ! 42 42 ! 43 44
#2 1231 ! 37 37 ! 37 38

head(tfe, 2)
#en es tfe
#1 ! ! 4.364360e-01
#2 ! !" 4.945229e-24

El objetivo (cruzarlas por los campos comunes):

res <- merge(merge(tf, tfe), qjilm)

El tiempo (usando merge):

res <- merge(merge(tf, tfe), qjilm)
#user system elapsed 
#442.991 2.496 446.832 

dim(res)
#[1] 6340091 9

Y con data.table:

library(data.table)

system.time({
  res.dt <- merge(data.table( tf,  key = c("en", "es")),
                  data.table( tfe, key = c("en", "es")) )

  res.dt <- merge( setkeyv(res.dt,   cols = c("pos.es", "pos.en", "length.es", "length.en")),
                   data.table(qjilm, key  = c("pos.es", "pos.en", "length.es", "length.en") )
              )
})
#user system elapsed 
#32.070 0.012 32.118 

dim(res.dt)
#[1] 6340091 9

Y, finalmente, suponiendo que los data.tables ya tienen asociado un índice de antemano:

tf.dt  <- data.table( tf,  key = c("en", "es"))
tfe.dt <- data.table( tfe, key = c("en", "es"))

system.time( res <- merge(tf.dt, tfe.dt) )
#user system elapsed 
#3.464 0.000 3.466 

dim(res)
#[1] 6340091 8

Resumen:

  • Había hecho unas pruebas con data.table previamente que no resultaron del todo satisfactorias.
  • Anoche, data.table me sacó de un apuro muy serio.
  • Ahora soy fan.
  • Gracias a data.table, el límite de tamaño de los conjuntos de datos con los que soy capaz de trabajar razonablemente con R ha crecido en todo un orden de magnitud: ya no me asusta que me hablen de millones.

¿Leemos “Analyzing Linguistic Data” juntos?

Miércoles, 24 de abril de 2013 1 comentario

Victor Peinado y yo estamos organizando un grupo de lectura. Junto con quienes se nos sumen, vamos a ir leyendo el libro Analyzing Linguistic Data: A practical introduction to Statistics“, que trata de:

  • R (instalación, gráficos, etc.)
  • Métodos estadísticos con R (modelos lineales, clústering,
  • clasificación, modelos mixtos)
  • Lingüística (que es el contexto en el que se aplica lo anterior).

La participación en este grupo está indicada para quienes tengan interés en las aplicaciones lingüísticas de la estadística (y de R, por supuesto). Y muy particularmente para esos lingüistas que se han encontrado con que su disciplina (o grandes partes de ella), cada vez más, está dejando de ser de letras.

Os invito a echarle un vistazo a la propuesta del grupo y el programa y a difundirlo entre quienes pudieran estar interesados.

Categories: estadística Tags: , ,

Más sobre la ley de Benford (I): una condición suficiente

Martes, 16 de abril de 2013 1 comentario

Las circunstancias —frente a las que soy dócil como el que más— me conducen a escribir de nuevo sobre la Ley de Benford. En concreto, voy a traer a la atención de mis lectores una condición suficiente para que se cumpla. Y de ella extraeremos conclusiones tal vez sorprendentes en sucesivas entradas de la serie que con esta inicio.

Dado un número (p.e., 1234), lo podemos descomponer en dos: una potencia de 10 y otro entre 0 y 10:

n <- 1234     # por ejemplo
suelo <- floor(log10(n))
parte.decimal <- log10(n) - suelo

10^suelo            # una potencia de 10
10^parte.decimal    # entre 0 y 10

Si lo que llamamos parte.decimal tiene una distribución uniforme en el intervalo (0,1), entonces la probabilidad de que un número comience por, por ejemplo, 3, será

P\left( 10^{\text{parte.decimal}} \in [3,4) \right),

o bien

P\left( \log_{10} 3 \le \text{parte.decimal} < \log_{10} 4 \right),

que no es otra cosa que log_{10} 4- log_{10} 3, el valor que corresponde a la definición estándar de la ley en cuestión.

Así que, en resumen:

Una condición suficiente para que se verifique la Ley de Benford para una serie de valores x_1, \dots, x_n es que la parte decimal de los valores \log_{10} x_i tenga una distribución uniforme sobre el intervalo (0,1).

(Nota: estoy obviando los signos).

Categories: estadística, r Tags: , ,

Mapa de los terremotos en la península ibérica

Lunes, 8 de abril de 2013 Sin comentarios

Me sorprendió hace un tiempo averiguar que en la península ibérica hubiese tantos terremotos (aunque mis amigos chilenos los llamarían de otra manera).

En esta entrada voy a mostrar el siguiente mapa de actividad sísmica durante los últimos años,

que he construido con el siguiente código en R:

library(ggmap)

url <- "http://comcat.cr.usgs.gov/earthquakes/feed/search.php?maxEventLatitude=45&minEventLatitude=35&minEventLongitude=-10&maxEventLongitude=5&minEventTime=953683200000&maxEventTime=1364688000000&minEventMagnitude=-1.0&maxEventMagnitude=10&minEventDepth=0.0&maxEventDepth=800.0&format=csv"
terremotos <- read.csv(url)

# obtengo un mapa
pen.iber <- get_map( location = c(-9.5, 36, 3.5, 44),
                     color = "color",
                     maptype = "roadmap")

# le añado puntos
ggmap(pen.iber) +
  geom_point(aes(x = Longitude, y = Latitude,
                 size = Magnitude),
                 data = terremotos, colour = 'red',
                 alpha = 0.2)
Categories: gráficos, r Tags: , ,

textConnection y ficheros anónimos: cuestión de rendimiento

Jueves, 4 de abril de 2013 Sin comentarios

La función textConnection de R es útil para leer el contenido de una variable como si fuese un fichero de texto. Verbigracia,

zz <- textConnection(LETTERS)
readLines(zz, 2)

Pero cuando uno hace

y lee con detenimiento, encuentra la siguiente nota:

As output text connections keep the character vector up to date line-by-line, they are relatively expensive to use, and it is often better to use an anonymous file() connection to collect output.

Vamos, que desaconseja usar dicha función por motivos de rendimiento cuando no vayan a usarse todas las sus características de las que file carece. Pero, ¿será cierto que el rendimiento es tan malo? Y de serlo, ¿cómo usar file?

Veámoslo:

quijote <- readLines( "http://www.gutenberg.org/cache/epub/2000/pg2000.txt",
                      encoding = "UTF-8" )
 
tc.io <- function(texto){
  zz <- textConnection(texto)
  readLines(zz)
}
 
system.time(tmp <- tc.io(quijote))
 
#user  system elapsed 
#13.92    0.00   13.92 
 
file.io <- function(texto){
  zz <- file()
  cat(texto, file = zz, sep = "\n")
  tmp <- readLines(zz)
  close(zz)
  tmp
}
 
 
system.time(tmp <- file.io(quijote))
 
#user  system elapsed 
#0.31    0.09    0.41 
Categories: r Tags: ,