Cluster SOM para variáveis ​​nominais / circulares


11

Basta saber se alguém está familiarizado com o agrupamento de entradas nominais. Eu estive olhando o SOM como uma solução, mas aparentemente ele só funciona com recursos numéricos. Existem extensões para recursos categóricos? Especificamente, eu estava pensando em "Dias da semana" como possíveis recursos. Obviamente, é possível convertê-lo em um recurso numérico (por exemplo, seg - dom correspondente aos n. 1-7), mas a distância euclidiana entre sol e seg (1 e 7) não seria a mesma que a distância de seg a ter (1 e 2). ) Todas as sugestões ou idéias serão muito apreciadas.


(+1) uma pergunta muito interessante
steffen

2
As variáveis ​​cíclicas são melhor consideradas como elementos do círculo unitário no plano Complex. Assim, seria natural mapear os dias da semana para (digamos) os pontos , j = 0 , ... , 6 ; ou seja , ( cos ( 0 ) , sin ( 0 ) ) , ( cos ( 2 π / 7 ) , sin ( 2 π / 7exp(2jπi/7)j=0,,6(cos(0),sin(0)) , ... ( cos ( 12 π / 7 ) , sin ( 12 π / 7 ) ) . (cos(2π/7),sin(2π/7))(cos(12π/7),sin(12π/7))
whuber

1
eu teria que codificar minha própria matriz de distância específica para variáveis ​​cíclicas? apenas imaginando se já havia algoritmos para esse tipo de cluster. thx
Michael

@ Michael: Eu acredito que você desejará especificar sua própria métrica de distância que seja apropriada para sua aplicação e que seja definida em todas as dimensões dos seus dados, não apenas na DOW. Formalmente, deixando x, y denotar pontos em seu espaço de dados, você precisa definir uma função métrica d (x, y) com as propriedades usuais: d (x, x) = 0, d (x, y) = d (y , x) ed (x, z) <= d (x, y) + d (y, z). Depois de fazer isso, criar o SOM é mecânico. O desafio criativo é definir d () de uma maneira que capture a noção de "similaridade" apropriada para sua aplicação.
Arthur pequeno

Respostas:


7

Fundo:

A maneira mais lógica de transformar a hora é em duas variáveis ​​que oscilam para frente e para trás fora de sincronia. Imagine a posição do ponteiro de fim de hora de um relógio de 24 horas. A xposição oscila para frente e para trás fora de sincronia com a yposição. Para um relógio de 24 horas você pode fazer isso com x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Você precisa de ambas as variáveis ​​ou o movimento adequado ao longo do tempo é perdido. Isso se deve ao fato de que a derivada do pecado ou cos muda no tempo, enquanto a (x,y)posição varia suavemente à medida que viaja ao redor do círculo unitário.

Por fim, considere se vale a pena adicionar um terceiro recurso para rastrear o tempo linear, que pode ser construído como horas (ou minutos ou segundos) desde o início do primeiro registro ou um carimbo de data / hora do Unix ou algo semelhante. Esses três recursos fornecem proxies para a progressão cíclica e linear do tempo, por exemplo, você pode retirar fenômenos cíclicos como ciclos de sono no movimento das pessoas e também crescimento linear como população versus tempo.

Exemplo de se está sendo realizado:

# 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

insira a descrição da imagem aqui

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

Agora vamos tentar:

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

insira a descrição da imagem aqui

Você mal consegue ver que há alguns horários após a meia-noite incluídos no cluster verde antes da meia-noite. Agora vamos reduzir o número de clusters e mostrar que antes e depois da meia-noite podem ser conectados em um único cluster com mais detalhes:

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

insira a descrição da imagem aqui

Veja como o cluster azul contém horários anteriores e posteriores à meia-noite agrupados no mesmo cluster ...

Você pode fazer isso por hora, dia da semana, semana do mês, dia do mês, estação do ano ou qualquer outra coisa.


Útil (+1). Esta é uma aplicação em que os gráficos sendo quadrados e não oblongos são realmente importantes. Não conheço o seu software, mas imagino que você possa definir a proporção para 1, longe do padrão.
Nick Cox

Isso é verdade @NickCox. Ou você pode apenas executar a transformação linear em sua cabeça ;-) #
user1745038 18 /

2

As variáveis ​​nominais comuns são codificadas quando usadas no SOM (por exemplo, uma variável com 1 na segunda-feira 0 para não segunda-feira, outra na terça-feira etc.).

Você pode incorporar informações adicionais criando categorias combinadas de dias adjacentes. Por exemplo: segunda e terça-feira, terça e quarta-feira, etc. No entanto, se seus dados estiverem relacionados ao comportamento humano, geralmente será mais útil usar o dia da semana e o fim de semana como categorias.


2

Para variáveis ​​nominais, a codificação típica em uma rede neural ou em um contexto de engenharia elétrica é chamada de "um quente" - um vetor de todos os 0s, com um 1 na posição apropriada para o valor da variável. Para os dias da semana, por exemplo, há sete dias, portanto seus vetores de um calor seriam de sete. Então, segunda-feira seria representada como [1 0 0 0 0 0 0], terça-feira como [0 1 0 0 0 0 0]] etc.

Como Tim sugeriu, essa abordagem pode ser generalizada facilmente para abranger vetores de recursos booleanos arbitrários, em que cada posição no vetor corresponde a um recurso de interesse em seus dados, e a posição é definida como 1 ou 0 para indicar a presença ou ausência dos mesmos. característica.

Depois de ter vetores binários, a distância de Hamming se torna uma métrica natural, embora a distância euclidiana também seja usada. Para vetores binários muito quentes, o SOM (ou outro aproximador de função) naturalmente interpola entre 0 e 1 para cada posição do vetor. Nesse caso, esses vetores são frequentemente tratados como parâmetros de uma distribuição Boltzmann ou softmax no espaço da variável nominal; esse tratamento também fornece uma maneira de usar os vetores em algum tipo de cenário de divergência de KL.

Variáveis ​​cíclicas são muito mais complicadas. Como Arthur disse nos comentários, você precisa definir uma métrica de distância que incorpore a natureza cíclica da variável.


1

Supondo que o dia da semana (dow) vá de [0, 6], em vez de projetar dados em um círculo, outra opção é usar:

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

Para entender o porquê, considere o dow como um relógio

  6  0
5      1
4      2
    3

diff entre 6 e 1 pode ser 6 - 1 = 5 (indo no sentido horário de 1 a 6) ou 7 - (6 - 1) = 2. Tomar o mínimo de ambas as opções deve ser suficiente.

Em geral, você pode usar: min(abs(diff), range - abs(diff))


0

Eu codifiquei com sucesso Dias da semana (e Meses do ano) como tupla de (cos, sin) como whuber destacado em seu comentário. Do que a distância euclidiana utilizada.

Este é um exemplo de código em 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))
}

A distância euclidiana entre 0 e 6 é igual a 0 e 1.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.