Grupowanie SOM dla zmiennych nominalnych / kołowych

11

Zastanawiam się tylko, czy ktoś jest zaznajomiony z grupowaniem nominalnych danych wejściowych. Patrzyłem na SOM jako rozwiązanie, ale najwyraźniej działa tylko z funkcjami numerycznymi. Czy są jakieś rozszerzenia dla funkcji jakościowych? W szczególności zastanawiałem się nad „Dniami tygodnia” jako możliwymi funkcjami. Oczywiście możliwe jest przekonwertowanie go na funkcję numeryczną (tj. Pn - Nd odpowiadające numerom 1-7), jednak odległość euklidesowa między Słońcem a Pn (1 i 7) nie byłaby taka sama jak odległość od Pn do Wt (1 i 2) ). Wszelkie sugestie i pomysły będą mile widziane.

Michael
źródło
(+1) bardzo interesujące pytanie
steffen
2
Zmienne cykliczne najlepiej traktować jako elementy koła jednostkowego w płaszczyźnie zespolonej. Zatem naturalnym byłoby odwzorowanie dni tygodnia na (powiedzmy) punkty , ; tj , , , ... . j = 0 , , 6 ( cos ( 0 ) , sin ( 0 ) ) ( cos ( 2 π / 7 ) , sin ( 2 π / 7 ) ) ( cos ( 12 π / 7 ) , sin ( 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
czy musiałbym kodować własną macierz odległości, a następnie specyficzną dla zmiennych cyklicznych? zastanawiam się tylko, czy istnieją już algorytmy dla tego typu grupowania. dzięki
Michael
@Michael: Wierzę, że będziesz chciał określić własną metrykę odległości, która jest odpowiednia dla twojej aplikacji i która jest zdefiniowana dla wszystkich wymiarów w twoich danych, nie tylko DOW. Formalnie, pozwalając x, y na oznaczenie punktów w przestrzeni danych, musisz zdefiniować funkcję metryczną d (x, y) o zwykłych właściwościach: d (x, x) = 0, d (x, y) = d (y , x) i d (x, z) <= d (x, y) + d (y, z). Gdy to zrobisz, tworzenie SOM jest mechaniczne. Twórczym wyzwaniem jest zdefiniowanie d () w sposób, który oddaje pojęcie „podobieństwa” odpowiednie dla twojej aplikacji.
Arthur Small

Odpowiedzi:

7

Tło:

Najbardziej logicznym sposobem na przekształcenie godziny są dwie zmienne, które kołyszą się w jedną i drugą synchronizację. Wyobraź sobie pozycję końca wskazówki godzinowej 24-godzinnego zegara. Te xwahania pozycji iz powrotem zsynchronizowany z ypozycji. Na zegarze 24-godzinnym można to osiągnąć z x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Potrzebujesz obu zmiennych lub utracisz właściwy ruch w czasie. Wynika to z faktu, że pochodna grzechu lub cos zmienia się w czasie, podczas gdy (x,y)pozycja zmienia się płynnie, gdy przemieszcza się po okręgu jednostki.

Na koniec zastanów się, czy warto dodać trzecią cechę do śledzenia czasu liniowego, którą można skonstruować jako godziny (lub minuty lub sekundy) od początku pierwszego rekordu lub uniksowy znacznik czasu lub coś podobnego. Te trzy cechy zapewniają następnie przybliżenie zarówno cyklicznego, jak i liniowego przebiegu czasu, np. Można wyciągać zjawiska cykliczne, takie jak cykle snu w ruchu ludzi, a także liniowy wzrost, taki jak populacja w funkcji czasu.

Przykład realizacji:

# 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

wprowadź opis zdjęcia tutaj

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

Teraz wypróbujmy:

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

wprowadź opis zdjęcia tutaj

Ledwo widać, że niektóre są po północy dołączone do zielonej gromady przed północą. Teraz zmniejszmy liczbę klastrów i pokażmy, że przed i po północy można połączyć bardziej szczegółowo w jednym klastrze:

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

wprowadź opis zdjęcia tutaj

Zobacz, jak niebieski klaster zawiera czasy sprzed i po północy, które są skupione w tym samym klastrze ...

Możesz to zrobić dla czasu, dnia tygodnia, tygodnia miesiąca, dnia miesiąca, sezonu lub cokolwiek innego.

użytkownik1745038
źródło
Pomocny (+1). Jest to jedna aplikacja, w której bardzo ważne są wykresy kwadratowe, a nie podłużne. Nie znam twojego oprogramowania, ale wyobrażam sobie, że możesz ustawić proporcje obrazu na 1, z dala od domyślnych.
Nick Cox,
To prawda @NickCox. Lub możesz po prostu przeprowadzić liniową transformację w głowie ;-)
user1745038
2

Zwykle zmienne nominalne są kodami obojętnymi, gdy są używane w SOM (np. Jedna zmienna z 1 dla poniedziałku 0 dla nie poniedziałku, inna dla wtorku itp.).

Możesz dołączyć dodatkowe informacje, tworząc połączone kategorie sąsiednich dni. Na przykład: poniedziałek i wtorek, wtorek i środa itp. Jeśli jednak dane dotyczą ludzkich zachowań, często bardziej użyteczne jest użycie kategorii Dzień tygodnia i Weekend.

Tim
źródło
2

W przypadku zmiennych nominalnych typowe kodowanie w sieci neuronowej lub w inżynierii elektrycznej nazywa się „jednym gorącym” - wektorem wszystkich zer, z jednym 1 w odpowiedniej pozycji dla wartości zmiennej. Na przykład w dni tygodnia jest siedem dni, więc twoje gorące wektory miałyby długość siedmiu. Wtedy poniedziałek będzie reprezentowany jako [1 0 0 0 0 0 0], wtorek jako [0 1 0 0 0 0 0] itd.

Jak wskazał Tim, podejście to można łatwo uogólnić, aby objąć dowolne wektory cech boolowskich, w których każda pozycja w wektorze odpowiada funkcji będącej przedmiotem zainteresowania twoich danych, a pozycja jest ustawiona na 1 lub 0, aby wskazać obecność lub jej brak. funkcja.

Kiedy masz już wektory binarne, odległość Hamminga staje się naturalną miarą, chociaż odległość euklidesowa jest również używana. W przypadku pojedynczych gorących wektorów binarnych SOM (lub inny aproksymator funkcji) naturalnie interpoluje od 0 do 1 dla każdej pozycji wektora. W tym przypadku wektory te są często traktowane jako parametry rozkładu Boltzmanna lub softmax w przestrzeni zmiennej nominalnej; takie traktowanie daje również możliwość wykorzystania wektorów w pewnym scenariuszu dywergencji KL.

Zmienne cykliczne są znacznie trudniejsze. Jak powiedział Arthur w komentarzach, musisz sam zdefiniować metrykę odległości, która uwzględnia cykliczny charakter zmiennej.

lmjohns3
źródło
1

Zakładając, że dzień tygodnia (dow) zaczyna się od [0, 6], zamiast wyświetlać dane w kręgu, inną opcją jest użycie:

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

Aby zrozumieć dlaczego, rozważ dow jako zegar

  6  0
5      1
4      2
    3

różnica między 6 a 1 może wynosić 6 - 1 = 5 (idąc od 1 do 6 zgodnie z ruchem wskazówek zegara) lub 7 - (6 - 1) = 2. Biorąc min z obu opcji, powinno wystarczyć.

Ogólnie możesz użyć: min(abs(diff), range - abs(diff))

ragha
źródło
0

Z powodzeniem zakodowałem Dni tygodnia (i Miesiące roku) jako krotkę (cos, sin), jak podkreślono w swoim komentarzu. Niż wykorzystana odległość euklidesowa.

To jest przykład kodu wr:

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))
}

Odległość euklidesowa między 0 a 6 jest równa 0 i 1.

Mario F.
źródło