Clústering

Seis asuntos sobre modelización estadística, incluyendo un problema que no parece del todo trivial

Sobre catboost

Todavía no he usado catboost en ningún proyecto serio, aunque tiene la pinta de ser la evolución más sofisticada de todos las variantes existentes del boosting. Ya escribí al respecto aquí y hoy traigo dos enlaces adicionales de José Luis Cañadas, un usuario muy entusiasta. Una sobre el tratamiento de las variables categóricas y otro sobre la regresión por cuantiles.

Ajuste bayesiano de un modelo con censura

Lo presenta el maestro Juan Orduz aquí que, como todos, no para mientes al hecho no totalmente evidente de que la verosimilitud de una densidad mixta (continua y discreta a un tiempo) es la que se postula que es (véase cómo arranca la sección Censored Gamma Model).

Consensus clustering

No hay nada tan corrosivo para la fe en el clústering que probar una y otra vez k-medias (por ejemplo) sobre los mismos datos y ver cómo los resultados cambian drásticamente de ejecución en ejecución.

Pero eso viene a ser, esencialmente, lo que hay detrás del consensus clústering (CC), una técnica que puede ser usada, entre otros fines, para determinar el número óptimo de grupos.

La idea fundamental de la cosa es que observaciones que merezcan ser agrupadas juntas lo serán muy frecuentemente aunque cambien ligeramente las condiciones iniciales (por ejemplo, se tome una submuestra de los datos o cambien las condiciones iniciales de k-medias, por ejemplo). Si uno altera esas condiciones iniciales repetidas veces puede contar la proporción de las veces que las observaciones i y j fueron emparejadas juntas y crear la correspondiente matriz (simétrica, para más señas) $latex C(i,j)$.

Documentar como el culo, no pensar en el usuario final, ser incapaz de ponerte en su situación, etc.

R

De vez en cuando pruebo paquetes promisorios. No es infrecuente que cosas que he intentado hace años, algún ejemplo más o menos sencillo que he publicado aquí, acabe convirtiéndose en la piedra angular de algo facturable. Incluso de algo facturable por mí.

geozoning podía haber sido uno de esos. La promesa del paquete es que puede ayudarte a segmentar regiones del espacio de acuerdo con alguna variable, una especie de clústering para información de tipo espacial.

Coordenadas polares por doquier

R

El otro día pasé por uno de esos sitios en los que exponen en las paredes obras de artistas medianos con el precio debajo. Me quedé mirando una muy… concéntrica porque me recordaba a lo que nos regala a menudo Antonio Chinchón. Pregunté de qué trataba la cosa y tuvieron la paciencia de explicármelo: al lado había una foto enorme y, se conoce, las cosas concéntricas eran una reordenación de los píxels de la primera. Una especie de tortilla de patata deconstruida a lo Adriá, pero con fotos.

Clústers de trayectorias con la distancia de Fréchet

R

Los viejos del lugar recordarán esto, donde agrupo trayectorias usando k-medias a pelo.

El paquete kmlShape usa la distancia de Fréchet para hacer algo parecido: buscar trayectorias geométricamente similares.

El código es

    library(kmlShape)
    library(tseries)
    library(zoo)
    library(XML)
    library(reshape)
    library(ggplot2)

    foo  <- function(
      simbolo, final = Sys.time(),
      profundidad = 30 * 24 * 3600) {
      precios <- get.hist.quote(
        instrument= simbolo,
        start = final - profundidad,
        end = final, quote=c("AdjClose"),
        provider="yahoo", origin="1970-01-01",
        compression="d", retclass="zoo")
      colnames(precios) <- simbolo
      return(precios)
    }

    # lista de símbolos del ibex

    tmp <- readHTMLTable("http://finance.yahoo.com/q/cp?s=%5EIBEX+Components")[[5]]
    tmp <- as.character(tmp$V1[-(1:6)])

    ibex <- do.call(merge,
      sapply(simbolos, foo, simplify = F))

    ibex.scaled <- data.frame(t(scale(ibex)))
    tmp <- cldsWide(ibex.scaled)

    res <- kmlShape(tmp, 4, toPlot = "none")

    tmp <- data.frame(
      id = rownames(ibex.scaled),
      cluster = res@clusters, ibex.scaled)

    tmp <- melt(tmp, id.vars = c("id", "cluster"))
    tmp$fecha <- as.Date(tmp$variable, "X%Y.%m.%d")

    ggplot(tmp, aes(x=fecha, y=value, group=id)) +
      geom_line() + facet_wrap(~cluster)

y el resultado,

Clases sociales y clústering

El clústering (o análisis de conglomerados, o como se le quiera llamar) es un atajo intelectual. En lugar de estudiar individuos (no necesariamente personas), estos se agrupan de manera más o menos cuestionable, se eligen representantes en cada uno de ellos, cuyas características se atribuyen a continuación a todos sus miembros.

No puedo evitar escribir párrafos como el anterior sin que me venga a la nariz ese olor a naftalina de cuando era crío y abría los armarios de mi abuela.

DBSCAN, ¿algo nuevo bajo el sol?

Ha sido en latitudes otras que las habituales que he aprendido y leído (mas no probado) sobre DBSCAN. Se conoce que es un nuevo (aunque ya tiene sus añitos: algo así como 20) método de clústering.

Por un lado, se agradecen las novedades.

Por el otro, tengo cierta aversión a las cosas que proceden de los congresos de Knowledge Discovery and Data Mining, que es donde fue publicado el algoritmo.

En esencia, funciona así: se fijan dos parámetros, e y n. Un punto es central si a distancia e o menor tiene, al menos, otros n puntos. Los clústers los conforman:

Voronois con distintas distancias

Especulando sobre la diferencia en la práctica entre distintas métricas ($latex l_1$, $latex l_2$, $latex l_\infty$, etc.), construi una serie de diagramas de Voronoi usado métricas arbitrarias.

En la Wikipedia se comparan gráficamente $latex l_1$, $latex l_2$ (o euclídea y Manhattan). Mi código,

library(data.table)
library(reshape2)
library(grid)

n <- 20
dim.image <- 1000
puntos <- data.frame(id = 1:n,
                      x0 = runif(n) * dim.image,
                      y0 = runif(n) * dim.image)
colores <- rainbow(n)

voronoi <- function(p){
  tmp <- data.table(expand.grid(
      x = 1:dim.image,
      y = 1:dim.image, id = 1:n), key = "id")
  tmp <- merge(tmp, puntos, by = "id")

  distancia <- function(a, b, c, d, p)
    (abs(a-c)^p + abs(b-d)^p)^(1/p)

  tmp$distancia <- distancia(tmp$x,
    tmp$y, tmp$x0, tmp$y0, p)
  tmp[, rank := rank(distancia, ties = "random"),
    by = c("x", "y")]

  rejilla <- tmp[tmp$rank == 1,]
  rejilla$x0 <- rejilla$y0 <-
    rejilla$distancia <- rejilla$rank <- NULL

  rejilla$color <- colores[rejilla$id]

  imagen <- as.matrix(dcast(rejilla, x ~ y, value.var = "color")[,-1])

  grid.raster(imagen)
}

permite usar más en función del parámetro p.

A cuento de mi clase práctica de modelos no supervisados

A cuento de la sesión práctica de modelos no supervisados que impartiré este sábado y que estoy preparando justo ahora, traigo a la atención de mis lectores una imagen que el asunto me sugiere:

dog2

La fuente también vale la pena. Aunque habla de otra cosa.

Nota: releyendo la entrada antes de publicarla definitivamente, me doy cuenta de que igual estoy siendo excesivamente sutil.