只是想知道是否有人熟悉标称输入的聚类。我一直在将SOM作为解决方案,但显然它仅适用于数字功能。分类功能是否有扩展?我特别想知道“星期几”是否可能是功能。当然可以将其转换为数值特征(例如,周一至周日对应于1-7号),但是,周日与周一之间的欧几里得距离(1&7)将与周一至周二(1&2)之间的欧氏距离不同)。任何建议或想法将不胜感激。
只是想知道是否有人熟悉标称输入的聚类。我一直在将SOM作为解决方案,但显然它仅适用于数字功能。分类功能是否有扩展?我特别想知道“星期几”是否可能是功能。当然可以将其转换为数值特征(例如,周一至周日对应于1-7号),但是,周日与周一之间的欧几里得距离(1&7)将与周一至周二(1&2)之间的欧氏距离不同)。任何建议或想法将不胜感激。
Answers:
背景:
转换小时数的最合乎逻辑的方法是将两个变量来回摆动,使其不同步。想象一下24小时制时针的末尾位置。该x
位置来回摆动不同步的y
位置。对于24小时制,您可以使用x=sin(2pi*hour/24)
,完成y=cos(2pi*hour/24)
。
您需要两个变量,否则会丢失正确的移动时间。这是由于正弦或余弦的导数随时间而变化,而(x,y)
位置随单位圆的移动而平滑变化的事实。
最后,考虑是否值得添加第三项功能来跟踪线性时间,该时间可以构造为从第一条记录开始起的小时(或分钟或秒),Unix时间戳或类似的东西。然后,这三个功能可提供时间的周期性和线性进展的代理,例如,您可以提取周期性的现象(如人们运动中的睡眠周期)以及线性增长(如人口与时间的增长)。
如果完成的示例:
# 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
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
现在让我们尝试一下:
kmeansshow(6,df[['x', 'y']].values)
您几乎看不到午夜之前的绿色群集中包含一些午夜之后的时间。现在,我们减少集群的数量,并显示午夜前后可以更详细地连接到单个集群中:
kmeansshow(3,df[['x', 'y']].values)
查看蓝色集群如何包含午夜前后的时间,这些时间聚集在同一集群中...
您可以按时间,一周中的某天,一个月中的某周,一个月中的某天,季节或任何其他方式执行此操作。
对于名义变量,在神经网络或电气工程环境中的典型编码称为“单热”(one-hot),即全0的矢量,其中1对应于变量值。例如,在一周中的某天,有7天,因此您的一热点向量长度为7。然后,星期一将表示为[1 0 0 0 0 0 0 0],星期二将表示为[0 1 0 0 0 0 0],依此类推。
正如Tim所暗示的,这种方法可以很容易地推广为包含任意布尔特征向量,其中向量中的每个位置都对应于数据中感兴趣的特征,并且该位置设置为1或0表示存在或不存在该特征特征。
一旦有了二元向量,尽管也使用了欧几里得距离,汉明距离便成为自然度量。对于单热二进制矢量,对于每个矢量位置,SOM(或其他函数逼近器)自然会在0和1之间进行插值。在这种情况下,这些向量通常被视为标称变量空间上的Boltzmann或softmax分布的参数;这种处理方式也提供了在某些KL散布情况下使用向量的方法。
循环变量要复杂得多。正如亚瑟(Arthur)在评论中所说,您需要自己定义一个距离量度,并结合变量的循环特性。
我已经成功地将星期几(和一年中的月份)编码为(cos,sin)的元组,在他的评论中强调了这一点。比使用欧几里得距离。
这是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))
}
0至6之间的欧几里得距离等于0和1。