Cluster SOM per variabili nominali / circolari


11

Mi chiedo solo se qualcuno ha familiarità con il clustering di input nominali. Ho considerato SOM come una soluzione, ma a quanto pare funziona solo con funzioni numeriche. Esistono estensioni per le caratteristiche categoriche? In particolare, mi chiedevo "Giorni della settimana" come possibili funzionalità. Naturalmente è possibile convertirlo in una caratteristica numerica (es. Lun - dom corrispondente a nn 1-7), tuttavia la distanza euclidea tra sole e lun (1 e 7) non sarebbe uguale alla distanza da lun a mar (1 & 2 ). Eventuali suggerimenti o idee sarebbero molto apprezzati.


(+1) una domanda molto interessante
steffen il

2
Le variabili cicliche sono meglio pensate come elementi del cerchio unitario nel piano Complesso. Pertanto, sarebbe naturale mappare i giorni della settimana a (diciamo) i punti , ; cioè , , , ... (\ cos (12 \ pi / 7 ), \ sin (12 \ pi / 7)) . exp(2jπi/7)j=0,,6( cos ( 2 π / 7 ) , sin ( 2 π / 7 ) ) ( cos ( 12 π / 7 ) , sin ( 12 π / 7 ) )(cos(0),sin(0))(cos(2π/7),sin(2π/7))(cos(12π/7),sin(12π/7))
whuber

1
dovrei codificare la mia matrice di distanza specifica per le variabili cicliche? chiedo solo se esistessero già algoritmi esistenti per questo tipo di clustering. grazie
Michael,

@Michael: credo che vorrai specificare la tua metrica di distanza che è appropriata per la tua applicazione e che è definita su tutte le dimensioni nei tuoi dati, non solo sul DOW. Formalmente, lasciando che x, y denotino punti nello spazio dati, è necessario definire una funzione metrica d (x, y) con le consuete proprietà: d (x, x) = 0, d (x, y) = d (y , x) e d (x, z) <= d (x, y) + d (y, z). Dopo averlo fatto, la creazione di SOM è meccanica. La sfida creativa è definire d () in un modo che catturi il concetto di "somiglianza" appropriato per la tua applicazione.
Arthur Small,

Risposte:


7

Sfondo:

Il modo più logico per trasformare l'ora è in due variabili che oscillano avanti e indietro fuori sincrono. Immagina la posizione della fine della lancetta delle ore di un orologio di 24 ore. La xposizione oscilla avanti e indietro fuori sincrono con la yposizione. Per un orologio di 24 ore è possibile raggiungere questo obiettivo con x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Sono necessarie entrambe le variabili o si perde il movimento corretto nel tempo. Ciò è dovuto al fatto che la derivata di sin o cos cambia nel tempo, mentre la (x,y)posizione varia senza intoppi mentre viaggia attorno al cerchio unitario.

Infine, considera se vale la pena aggiungere una terza funzione per tracciare il tempo lineare, che può essere costruito come ore (o minuti o secondi) dall'inizio del primo record o un timestamp Unix o qualcosa di simile. Queste tre caratteristiche forniscono quindi proxy per la progressione ciclica e lineare del tempo, ad esempio è possibile estrarre fenomeni ciclici come i cicli del sonno nel movimento delle persone e anche una crescita lineare come la popolazione rispetto al tempo.

Esempio di realizzazione:

# 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

inserisci qui la descrizione dell'immagine

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

Ora proviamolo:

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

inserisci qui la descrizione dell'immagine

Riesci a malapena a vedere che ci sono alcuni dopo mezzanotte inclusi nel cluster verde prima di mezzanotte. Ora riduciamo il numero di cluster e mostriamo che prima e dopo la mezzanotte possono essere collegati in un singolo cluster in modo più dettagliato:

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

inserisci qui la descrizione dell'immagine

Scopri in che modo il cluster blu contiene i periodi precedenti e successivi alla mezzanotte raggruppati nello stesso cluster ...

Puoi farlo per tempo, o giorno della settimana, settimana del mese, giorno del mese, stagione o altro.


Utile (+1). Questa è un'applicazione in cui i grafici non quadrati sono davvero importanti. Non conosco il tuo software ma immagino che tu possa impostare le proporzioni su 1, lontano dal valore predefinito.
Nick Cox,

È vero @NickCox. Oppure puoi semplicemente eseguire la trasformazione lineare nella tua testa ;-)
user1745038 il

2

Le variabili comunemente nominali sono codificate fittizie quando utilizzate in SOM (ad esempio, una variabile con un 1 per lunedì 0 per non lunedì, un'altra per martedì, ecc.).

È possibile incorporare informazioni aggiuntive creando categorie combinate di giorni adiacenti. Ad esempio: lunedì e martedì, martedì e mercoledì, ecc. Tuttavia, se i tuoi dati si riferiscono al comportamento umano, è spesso più utile utilizzare il giorno della settimana e il fine settimana come categorie.


2

Per le variabili nominali, la codifica tipica in una rete neurale o in un contesto di ingegneria elettrica è chiamata "one-hot" - un vettore di tutti gli 0, con 1 1 nella posizione appropriata per il valore della variabile. Per i giorni della settimana, ad esempio, ci sono sette giorni, quindi i tuoi vettori one-hot sarebbero di lunghezza sette. Quindi lunedì sarebbe rappresentato come [1 0 0 0 0 0 0], martedì come [0 1 0 0 0 0 0], ecc.

Come ha suggerito Tim, questo approccio può essere generalizzato facilmente per comprendere vettori di funzioni booleane arbitrarie, in cui ogni posizione nel vettore corrisponde a una caratteristica di interesse per i tuoi dati e la posizione è impostata su 1 o 0 per indicare la presenza o l'assenza di quella caratteristica.

Una volta che hai vettori binari, la distanza di Hamming diventa una metrica naturale, anche se viene usata anche la distanza euclidea. Per i vettori binari a uno caldo, il SOM (o altro approssimatore di funzioni) interpolerà naturalmente tra 0 e 1 per ogni posizione vettoriale. In questo caso, questi vettori sono spesso trattati come i parametri di una distribuzione di Boltzmann o softmax nello spazio della variabile nominale; questo trattamento consente di utilizzare i vettori anche in una sorta di scenario di divergenza KL.

Le variabili cicliche sono molto più complicate. Come Arthur ha detto nei commenti, dovresti definire tu stesso una metrica della distanza che incorpora la natura ciclica della variabile.


1

Supponendo che il giorno della settimana (dow) vada da [0, 6], invece di proiettare i dati in un cerchio, un'altra opzione è usare:

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

Per capire perché, considera il dow come un orologio

  6  0
5      1
4      2
    3

la differenza tra 6 e 1 potrebbe essere 6 - 1 = 5 (andando in senso orario da 1 a 6) o 7 - (6 - 1) = 2. Prendere il minimo di entrambe le opzioni dovrebbe fare il trucco.

In generale puoi usare: min(abs(diff), range - abs(diff))


0

Ho codificato con successo i giorni della settimana (e i mesi dell'anno) come tupla di (cos, sin) come ha evidenziato il whuber nel suo commento. Rispetto alla distanza euclidea usata.

Questo è un esempio di codice in 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 distanza euclidea tra 0 e 6 è uguale a 0 e 1.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.