SOM clustering untuk variabel nominal / melingkar

11

Hanya ingin tahu apakah ada yang akrab dengan pengelompokan input nominal. Saya telah melihat SOM sebagai solusi tetapi ternyata itu hanya bekerja dengan fitur numerik. Apakah ada ekstensi untuk fitur kategorikal? Secara khusus saya bertanya-tanya tentang 'Days of the Week' sebagai fitur yang mungkin. Tentu saja dimungkinkan untuk mengubahnya menjadi fitur numerik (yaitu Sen - Matahari sesuai dengan nos 1-7) namun kemudian jarak Euclidean antara Matahari dan Sen (1 & 7) tidak akan sama dengan jarak dari Sen ke Sel (1 & 2) ). Setiap saran atau ide akan sangat dihargai.

Michael
sumber
(+1) pertanyaan yang sangat menarik
steffen
2
Variabel siklik paling baik dianggap sebagai elemen lingkaran satuan dalam bidang Kompleks. Dengan demikian, adalah wajar untuk memetakan hari dalam seminggu untuk (katakanlah) poin , ; yaitu , , , ... ( cos ( 12 π / 7 ) , dosa ( 12 π / 7 ) ) . j = 0 , , 6 ( cos ( 0 ) , sin ( 0 ) ) ( cos ( 2 π / 7 ) , sin ( 2 π / 7 ) )exp(2jπsaya/7)j=0,,6(cos(0),sin(0))(cos(2π/7),sin(2π/7))(cos(12π/7),dosa(12π/7))
whuber
1
saya harus kode jarak matriks saya sendiri kemudian spesifik untuk variabel siklik? hanya ingin tahu apakah sudah ada algoritma untuk jenis pengelompokan ini. thx
Michael
@Michael: Saya yakin Anda akan ingin menentukan metrik jarak Anda sendiri yang sesuai untuk aplikasi Anda, dan yang didefinisikan atas semua dimensi dalam data Anda, bukan hanya DOW. Secara formal, dengan membiarkan x, y menunjukkan titik dalam ruang data Anda, Anda perlu mendefinisikan fungsi metrik d (x, y) dengan properti biasa: d (x, x) = 0, d (x, y) = d (y , x), dan d (x, z) <= d (x, y) + d (y, z). Setelah Anda selesai melakukannya, membuat SOM adalah mekanis. Tantangan kreatifnya adalah mendefinisikan d () dengan cara yang menangkap gagasan "kesamaan" yang sesuai dengan aplikasi Anda.
Arthur Small

Jawaban:

7

Latar Belakang:

Cara paling logis untuk mengubah jam adalah menjadi dua variabel yang berayun bolak-balik. Bayangkan posisi ujung jarum jam dari 24 jam. The xayunan posisi bolak-balik tidak sinkron dengan yposisi. Untuk jam 24-jam Anda dapat melakukannya dengan x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Anda perlu kedua variabel atau gerakan yang tepat melalui waktu hilang. Hal ini disebabkan oleh fakta bahwa turunan dari dosa atau cos berubah dalam waktu sedangkan (x,y)posisi bervariasi dengan lancar saat bergerak di sekitar satuan lingkaran.

Akhirnya, pertimbangkan apakah perlu untuk menambahkan fitur ketiga untuk melacak waktu linier, yang dapat dibangun sebagai jam (atau menit atau detik) dari awal catatan pertama atau cap waktu Unix atau yang serupa. Ketiga fitur ini kemudian memberikan proksi untuk progresif siklus dan linear waktu, mis. Anda dapat mengeluarkan fenomena siklus seperti siklus tidur dalam pergerakan manusia dan juga pertumbuhan linear seperti populasi vs waktu.

Contoh jika dilakukan:

# 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

masukkan deskripsi gambar di sini

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

Sekarang mari kita coba:

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

masukkan deskripsi gambar di sini

Anda hanya bisa melihat bahwa ada beberapa setelah tengah malam yang termasuk dalam kluster hijau sebelum tengah malam. Sekarang mari kita kurangi jumlah cluster dan tunjukkan bahwa sebelum dan sesudah tengah malam dapat dihubungkan dalam satu cluster secara lebih detail:

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

masukkan deskripsi gambar di sini

Lihat bagaimana gugus biru berisi waktu dari sebelum dan sesudah tengah malam yang dikelompokkan bersama di dalam kluster yang sama ...

Anda dapat melakukan ini untuk waktu, atau hari dalam seminggu, atau minggu dalam sebulan, atau hari dalam sebulan, atau musim, atau apa pun.

pengguna1745038
sumber
Bermanfaat (+1). Ini adalah salah satu aplikasi di mana grafik menjadi bujur sangkar tidak benar-benar penting. Saya tidak tahu perangkat lunak Anda, tetapi saya kira Anda dapat mengatur rasio aspek ke 1, jauh dari standar.
Nick Cox
Itu benar @NickCox. Atau Anda bisa melakukan transformasi linear di kepala Anda ;-)
user1745038
2

Variabel nominal yang umum adalah kode dummy ketika digunakan dalam SOM (misalnya, satu variabel untuk dengan 1 untuk Senin 0 untuk tidak Senin, yang lain untuk Selasa, dll).

Anda dapat memasukkan informasi tambahan dengan membuat kategori gabungan dari hari yang berdekatan. Misalnya: Senin & Selasa, Selasa & Rabu, dll. Namun, jika data Anda berkaitan dengan perilaku manusia, sering kali lebih bermanfaat untuk menggunakan Weekday dan Weekend sebagai kategori.

Tim
sumber
2

Untuk variabel nominal, pengkodean khas dalam jaringan saraf atau konteks teknik listrik disebut "satu panas" - vektor semua 0s, dengan satu 1 pada posisi yang sesuai untuk nilai untuk variabel. Untuk hari-hari dalam seminggu, misalnya, ada tujuh hari, jadi vektor satu-panas Anda akan panjangnya tujuh. Maka Senin akan direpresentasikan sebagai [1 ​​0 0 0 0 0 0], Selasa sebagai [0 1 0 0 0 0 0], dll.

Seperti yang disiratkan Tim, pendekatan ini dapat digeneralisasikan dengan mudah untuk mencakup vektor fitur boolean yang sewenang-wenang, di mana setiap posisi dalam vektor berhubungan dengan fitur yang diminati dalam data Anda, dan posisinya diatur ke 1 atau 0 untuk menunjukkan ada atau tidaknya itu. fitur.

Setelah Anda memiliki vektor biner, jarak Hamming menjadi metrik alami, meskipun jarak Euclidean juga digunakan. Untuk vektor biner satu-panas, SOM (atau aproksimasi fungsi lainnya) secara alami akan menginterpolasi antara 0 dan 1 untuk setiap posisi vektor. Dalam hal ini, vektor-vektor ini sering diperlakukan sebagai parameter distribusi Boltzmann atau softmax di atas ruang variabel nominal; perlakuan ini memberikan cara untuk menggunakan vektor dalam semacam skenario divergensi KL juga.

Variabel siklik jauh lebih rumit. Seperti yang dikatakan Arthur dalam komentar, Anda harus menentukan metrik jarak sendiri yang menggabungkan sifat siklik variabel.

lmjohns3
sumber
1

Dengan asumsi hari dalam seminggu (dow) berubah dari [0, 6], alih-alih memproyeksikan data ke lingkaran pilihan lain adalah dengan menggunakan:

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

Untuk memahami alasannya, pertimbangkan dow sebagai jam

  6  0
5      1
4      2
    3

perbedaan antara 6 dan 1 bisa 6 - 1 = 5 (pergi searah jarum jam dari 1 ke 6) atau 7 - (6 - 1) = 2. Mengambil min dari kedua opsi harus melakukan trik.

Secara umum Anda dapat menggunakan: min(abs(diff), range - abs(diff))

ragha
sumber
0

Saya telah berhasil menyandikan Hari dalam seminggu (dan Bulan dalam setahun) sebagai tupel (karena, dosa) seperti yang disorot oleh Whuber dalam komentarnya. Daripada digunakan jarak Euclidean.

Ini adalah contoh kode dalam 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))
}

Jarak Euclidean antara 0 dan 6 sama dengan 0 dan 1.

Mario F.
sumber