Agrupación de SOM para variables nominales / circulares

11

Me pregunto si alguien está familiarizado con la agrupación de entradas nominales. He estado buscando en SOM como una solución, pero aparentemente solo funciona con características numéricas. ¿Hay alguna extensión para las características categóricas? Específicamente me preguntaba acerca de 'Días de la semana' como posibles características. Por supuesto, es posible convertirlo en una función numérica (es decir, lunes a domingo correspondiente a los números 1-7), sin embargo, la distancia euclidiana entre Sol y lunes (1 y 7) no sería la misma que la distancia de lunes a martes (1 y 2 ) Cualquier sugerencia o idea sería muy apreciada.

Miguel
fuente
(+1) una pregunta muy interesante
steffen
2
Las variables cíclicas se consideran mejor como elementos del círculo unitario en el plano complejo. Por lo tanto, sería natural asignar los días de la semana a (digamos) los puntos , ; es decir , , , ... . j = 0 , , 6 ( cos ( 0 ) , sin ( 0 ) ) ( cos ( 2 π / 7 ) , sin ( 2 π / 7 ) ) ( cos ( 12 π / 7 ) , pecado ( 12 π / 7 )exp(2jπi/7)j=0,,6(cos(0),sin(0))(cos(2π/7),sin(2π/7))(cos(12π/7),sin(12π/7))
whuber
1
¿tendría que codificar mi propia matriz de distancia y luego específica para variables cíclicas? Solo me preguntaba si ya existían algoritmos para este tipo de agrupación. gracias
Michael
@ Michael: Creo que querrá especificar su propia métrica de distancia que sea apropiada para su aplicación, y que esté definida en todas las dimensiones de sus datos, no solo el DOW. Formalmente, dejando que x, y denote puntos en su espacio de datos, necesita definir una función métrica d (x, y) con las propiedades habituales: d (x, x) = 0, d (x, y) = d (y , x) yd (x, z) <= d (x, y) + d (y, z). Una vez que hayas hecho eso, crear el SOM es mecánico. El desafío creativo es definir d () de una manera que capture la noción de "similitud" apropiada para su aplicación.
Arthur Small

Respuestas:

7

Antecedentes:

La forma más lógica de transformar la hora es en dos variables que se desvían de la sincronización. Imagine la posición de la manecilla de fin de hora de un reloj de 24 horas. La xposición oscila de un lado a otro fuera de sincronización con la yposición. Para un reloj de 24 horas se puede lograr esto con x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Necesita ambas variables o se pierde el movimiento adecuado a través del tiempo. Esto se debe al hecho de que la derivada de sin o cos cambia en el tiempo, mientras que la (x,y)posición varía suavemente a medida que viaja alrededor del círculo unitario.

Finalmente, considere si vale la pena agregar una tercera característica para rastrear el tiempo lineal, que se puede construir como horas (o minutos o segundos) desde el comienzo del primer registro o una marca de tiempo Unix o algo similar. Estas tres características proporcionan proxies para la progresión cíclica y lineal del tiempo, por ejemplo, puede extraer fenómenos cíclicos como los ciclos de sueño en el movimiento de las personas y también el crecimiento lineal como la población frente al tiempo.

Ejemplo de si se está logrando:

# Enable inline plotting
%matplotlib inline

#Import everything I need...

import numpy as np
import matplotlib as mp

import matplotlib.pyplot as plt
import pandas as pd

# Grab some random times from here: https://www.random.org/clock-times/
# put them into a csv.
from pandas import DataFrame, read_csv
df = read_csv('/Users/angus/Machine_Learning/ipython_notebooks/times.csv',delimiter=':')
df['hourfloat']=df.hour+df.minute/60.0
df['x']=np.sin(2.*np.pi*df.hourfloat/24.)
df['y']=np.cos(2.*np.pi*df.hourfloat/24.)

df

ingrese la descripción de la imagen aquí

def kmeansshow(k,X):

    from sklearn import cluster
    from matplotlib import pyplot
    import numpy as np

    kmeans = cluster.KMeans(n_clusters=k)
    kmeans.fit(X)

    labels = kmeans.labels_
    centroids = kmeans.cluster_centers_
    #print centroids

    for i in range(k):
        # select only data observations with cluster label == i
        ds = X[np.where(labels==i)]
        # plot the data observations
        pyplot.plot(ds[:,0],ds[:,1],'o')
        # plot the centroids
        lines = pyplot.plot(centroids[i,0],centroids[i,1],'kx')
        # make the centroid x's bigger
        pyplot.setp(lines,ms=15.0)
        pyplot.setp(lines,mew=2.0)
    pyplot.show()
    return centroids

Ahora probémoslo:

kmeansshow(6,df[['x', 'y']].values)

ingrese la descripción de la imagen aquí

Apenas puede ver que hay algunos momentos posteriores a la medianoche incluidos con el clúster verde anterior a la medianoche. Ahora reduzcamos el número de clústeres y demostremos que antes y después de la medianoche se pueden conectar en un solo clúster con más detalle:

kmeansshow(3,df[['x', 'y']].values)

ingrese la descripción de la imagen aquí

Vea cómo el clúster azul contiene horas anteriores y posteriores a la medianoche que se agrupan en el mismo clúster ...

Puede hacer esto por tiempo, día de la semana, semana del mes, día del mes, temporada o cualquier cosa.

usuario1745038
fuente
Útil (+1). Esta es una aplicación donde los gráficos que son cuadrados no oblongos es realmente importante. No conozco su software, pero imagino que puede establecer la relación de aspecto en 1, lejos del valor predeterminado.
Nick Cox
Eso es cierto @ NickCox. O simplemente puede realizar la transformación lineal en su cabeza ;-)
user1745038
2

Las variables nominales comunes se codifican de forma ficticia cuando se usan en SOM (por ejemplo, una variable con un 1 para el lunes 0 para no el lunes, otra para el martes, etc.).

Puede incorporar información adicional creando categorías combinadas de días adyacentes. Por ejemplo: lunes y martes, martes y miércoles, etc. Sin embargo, si sus datos se relacionan con el comportamiento humano, a menudo es más útil usar Weekday y Weekend como categorías.

Tim
fuente
2

Para las variables nominales, la codificación típica en una red neuronal o contexto de ingeniería eléctrica se denomina "one-hot" , un vector de todos los 0, con un 1 en la posición adecuada para el valor de la variable. Para los días de la semana, por ejemplo, hay siete días, por lo que sus vectores calientes serían de longitud siete. Entonces el lunes se representaría como [1 0 0 0 0 0 0], el martes como [0 1 0 0 0 0 0], etc.

Como Tim sugirió, este enfoque se puede generalizar fácilmente para abarcar vectores de características booleanas arbitrarias, donde cada posición en el vector corresponde a una característica de interés en sus datos, y la posición se establece en 1 o 0 para indicar la presencia o ausencia de ese característica.

Una vez que tiene vectores binarios, la distancia de Hamming se convierte en una métrica natural, aunque también se usa la distancia euclidiana. Para vectores binarios de un solo calor, el SOM (u otro aproximador de función) naturalmente interpolará entre 0 y 1 para cada posición del vector. En este caso, estos vectores a menudo se tratan como los parámetros de una distribución de Boltzmann o softmax en el espacio de la variable nominal; Este tratamiento también proporciona una forma de utilizar los vectores en algún tipo de escenario de divergencia KL.

Las variables cíclicas son mucho más complicadas. Como dijo Arthur en los comentarios, usted mismo necesitaría definir una métrica de distancia que incorpore la naturaleza cíclica de la variable.

lmjohns3
fuente
1

Suponiendo que el día de la semana (dow) pase de [0, 6], en lugar de proyectar datos en un círculo, otra opción es usar:

dist = min(abs(dow_diff), 7 - abs(dow_diff))

Para entender por qué, considera el dow como un reloj

  6  0
5      1
4      2
    3

diff entre 6 y 1 podría ser 6 - 1 = 5 (yendo en sentido horario de 1 a 6) o 7 - (6 - 1) = 2. Tomar el mínimo de ambas opciones debería ser el truco.

En general puedes usar: min(abs(diff), range - abs(diff))

ragha
fuente
0

He codificado con éxito los Días de la semana (y los Meses del año) como tupla de (cos, sin) como whuber destacó en su comentario. De la distancia euclidiana utilizada.

Este es un ejemplo de código en r:

circularVariable = function(n, r = 4){
 #Transform a circular variable (e.g. Month so the year or day of the week) into two new variables (tuple).
 #n = upper limit of the sequence. E.g. for days of the week this is 7.
 #r =  number of digits to round generated variables.
 #Return
 #
 coord = function(y){
   angle = ((2*pi)/n) *y
   cs = round(cos(angle),r)
   s = round(sin(angle),r)
   c(cs,s)
 }
 do.call("rbind", lapply((0:(n-1)), coord))
}

La distancia euclidiana entre 0 y 6 es igual a 0 y 1.

Mario F.
fuente