SOM-Clustering für nominale / zirkuläre Variablen

11

Ich frage mich nur, ob jemand mit dem Clustering von nominalen Eingaben vertraut ist. Ich habe SOM als Lösung betrachtet, aber anscheinend funktioniert es nur mit numerischen Funktionen. Gibt es Erweiterungen für kategoriale Funktionen? Insbesondere habe ich mich über "Wochentage" als mögliche Funktionen gewundert. Natürlich ist es möglich, es in ein numerisches Merkmal umzuwandeln (dh Mo - Sonne entsprechend Nr. 1-7), aber dann wäre der euklidische Abstand zwischen Sonne und Mo (1 & 7) nicht der gleiche wie der Abstand von Mo bis Di (1 & 2) ). Anregungen oder Ideen wäre sehr dankbar.

Michael
quelle
(+1) eine sehr interessante
frage
2
exp((2jπich/.7)j=0,,6((cos((0),Sünde((0))((cos((2π/.7),Sünde((2π/.7))((cos((12π/.7),Sünde((12π/.7))
1
müsste ich meine eigene Distanzmatrix codieren, die dann spezifisch für zyklische Variablen ist? Ich frage mich nur, ob es bereits Algorithmen für diese Art von Clustering gibt. thx
Michael
@Michael: Ich glaube, Sie möchten Ihre eigene Entfernungsmetrik angeben, die für Ihre Anwendung geeignet ist und die über alle Dimensionen in Ihren Daten definiert ist, nicht nur über den DOW. Wenn Sie x, y Punkte in Ihrem Datenraum bezeichnen lassen, müssen Sie eine metrische Funktion d (x, y) mit den üblichen Eigenschaften definieren: d (x, x) = 0, d (x, y) = d (y , x) und d (x, z) <= d (x, y) + d (y, z). Sobald Sie dies getan haben, ist das Erstellen des SOM mechanisch. Die kreative Herausforderung besteht darin, d () so zu definieren, dass der für Ihre Anwendung geeignete Begriff der "Ähnlichkeit" erfasst wird.
Arthur Small

Antworten:

7

Hintergrund:

Der logischste Weg, die Stunde umzuwandeln, besteht in zwei Variablen, die nicht synchron hin und her schwingen. Stellen Sie sich die Position des Stundenendzeigers einer 24-Stunden-Uhr vor. Die xPosition schwingt nicht synchron mit der yPosition hin und her . Für eine 24-Stunden-Uhr können Sie dies mit x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Sie benötigen beide Variablen oder die richtige Bewegung durch die Zeit geht verloren. Dies ist auf die Tatsache zurückzuführen, dass sich die Ableitung von sin oder cos zeitlich ändert, während sich die (x,y)Position gleichmäßig ändert, wenn sie sich um den Einheitskreis bewegt.

Überlegen Sie abschließend, ob es sich lohnt, ein drittes Feature zum Verfolgen der linearen Zeit hinzuzufügen, das als Stunden (oder Minuten oder Sekunden) ab dem Beginn des ersten Datensatzes oder als Unix-Zeitstempel oder ähnliches erstellt werden kann. Diese drei Merkmale bieten dann Proxys sowohl für den zyklischen als auch für den linearen Verlauf der Zeit, z. B. können Sie zyklische Phänomene wie Schlafzyklen in der Bewegung von Menschen und auch lineares Wachstum wie Bevölkerung gegen Zeit herausziehen.

Beispiel für die Durchführung:

# 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

Geben Sie hier die Bildbeschreibung ein

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

Probieren wir es jetzt aus:

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

Geben Sie hier die Bildbeschreibung ein

Sie können kaum sehen, dass einige Zeiten nach Mitternacht im grünen Cluster vor Mitternacht enthalten sind. Lassen Sie uns nun die Anzahl der Cluster reduzieren und zeigen, dass vor und nach Mitternacht eine Verbindung zu einem einzelnen Cluster hergestellt werden kann:

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

Geben Sie hier die Bildbeschreibung ein

Sehen Sie, wie der blaue Cluster Zeiten vor und nach Mitternacht enthält, die im selben Cluster zusammengefasst sind ...

Sie können dies für die Zeit oder den Wochentag oder die Woche des Monats oder den Tag des Monats oder die Jahreszeit oder irgendetwas tun.

user1745038
quelle
Hilfreich (+1). Dies ist eine Anwendung, bei der Diagramme, die quadratisch und nicht länglich sind, wirklich wichtig sind. Ich kenne Ihre Software nicht, aber ich kann mir vorstellen, dass Sie das Seitenverhältnis von der Standardeinstellung auf 1 setzen können.
Nick Cox
Das stimmt @NickCox. Oder Sie können einfach die lineare Transformation in Ihrem Kopf durchführen ;-)
user1745038
2

Üblicherweise werden nominelle Variablen bei Verwendung in SOM Dummy-codiert (z. B. eine Variable für mit einer 1 für Montag 0 für nicht Montag, eine andere für Dienstag usw.).

Sie können zusätzliche Informationen einbinden, indem Sie kombinierte Kategorien benachbarter Tage erstellen. Beispiel: Montag und Dienstag, Dienstag und Mittwoch usw. Wenn sich Ihre Daten jedoch auf menschliches Verhalten beziehen, ist es häufig sinnvoller, Wochentag und Wochenende als Kategorien zu verwenden.

Tim
quelle
2

Für nominale Variablen wird die typische Codierung in einem neuronalen Netzwerk oder in einem elektrotechnischen Kontext als "One-Hot" bezeichnet. - ein Vektor aller Nullen, wobei eine 1 an der entsprechenden Position für den Wert für die Variable steht. Für die Wochentage gibt es beispielsweise sieben Tage, sodass Ihre One-Hot-Vektoren die Länge sieben haben. Dann würde Montag als [1 0 0 0 0 0 0], Dienstag als [0 1 0 0 0 0 0] usw. dargestellt.

Wie Tim angedeutet hat, kann dieser Ansatz leicht verallgemeinert werden, um beliebige boolesche Merkmalsvektoren zu umfassen, wobei jede Position im Vektor einem Merkmal entspricht, das für Ihre Daten von Interesse ist, und die Position auf 1 oder 0 gesetzt wird, um das Vorhandensein oder Fehlen davon anzuzeigen Feature.

Sobald Sie binäre Vektoren haben, wird der Hamming-Abstand zu einer natürlichen Metrik, obwohl auch der euklidische Abstand verwendet wird. Bei One-Hot-Binärvektoren interpoliert der SOM (oder ein anderer Funktionsapproximator) natürlich für jede Vektorposition zwischen 0 und 1. In diesem Fall werden diese Vektoren häufig als Parameter einer Boltzmann- oder Softmax-Verteilung über den Raum der nominalen Variablen behandelt. Diese Behandlung bietet die Möglichkeit, die Vektoren auch in einer Art KL-Divergenzszenario zu verwenden.

Zyklische Variablen sind viel schwieriger. Wie Arthur in den Kommentaren sagte, müssten Sie selbst eine Entfernungsmetrik definieren, die die zyklische Natur der Variablen berücksichtigt.

lmjohns3
quelle
1

Angenommen, der Wochentag (dow) geht von [0, 6] aus, anstatt Daten auf einen Kreis zu projizieren, besteht eine andere Option darin, Folgendes zu verwenden:

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

Um zu verstehen warum, betrachten Sie den Dow als eine Uhr

  6  0
5      1
4      2
    3

Der Unterschied zwischen 6 und 1 kann 6 - 1 = 5 (im Uhrzeigersinn von 1 bis 6) oder 7 - (6 - 1) = 2 sein.

Im Allgemeinen können Sie verwenden: min(abs(diff), range - abs(diff))

Ragha
quelle
0

Ich habe Wochentage (und Monate des Jahres) erfolgreich als Tupel von (cos, sin) codiert, wie in seinem Kommentar hervorgehoben. Als benutzte euklidische Distanz.

Dies ist ein Beispiel für Code 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))
}

Der euklidische Abstand zwischen 0 und 6 ist gleich 0 und 1.

Mario F.
quelle