K-means实现二分类问题

最近做一个有关二分类问题,我打算使用K-means算法实现baseline。

首先,我的数据文件形式是“.arff”格式的,在处理这种数据格式的时候,我是花了一些精力的,话不多说,代码如下:

import numpy as np

def readarff(filename):
    #dataMat=np.zeros(shape=(1000,4096))
    dataMat=[[0 for i in range(4096)] for j in range(591)]
    arff_file=open(filename)
    lines=arff_file.readlines()
    count=0
    for l in lines:
        content=[]
        if not (l.startswith("@")):
            content.append(l)
            for c in content:
                cs=c.split(',')
                cs.pop(0)
                cs.pop(0)
                cs.pop(0)
                flag=True
                while flag:
                    temp=cs[0].split(' ')
                    index=int(temp[1])
                    if not(index==4099):
                        dataMat[count][index-3]=(float)(temp[2])
                        #print count
                        #print index-4
                        cs.pop(0)
                    else:
                        flag=False
        count=count+1
    dataMat=np.matrix(dataMat)
    return dataMat


dataMat=readarff('data/temp1.arff')

我的数据文件中,前三个属性是不应该作为特征属性的,这就是出现了三个pop()的原因。

dataMat是一个数据矩阵,这个矩阵也是最后需要的一个返回值。关于这个矩阵,在初始化时,一定要使用:

dataMat=[[0 for i in range(4096)] for j in range(591)]

这种形式,而不要使用:

dataMat=[[0]*4096]*591

因为,如果使用下面这种形式的话,在给矩阵赋值的时候,例如:

dataMat[0][0]=0.123456

这样改变的不只是(0,0)这一个位置的值,而是会改变所有行的第0列的值。(具体知识点涉及到了list的浅拷贝问题,可以参照:https://www.cnblogs.com/btchenguang/archive/2012/01/30/2332479.html)

另外,由于我的数据格式是“ 1 0.123456789”,第二个数字才是我需要的,这也就是我为什么使用split对空格进行分割的原因了。

还有一个注意点:

就是我的强制类型转换那一步:

dataMat[count][index-3]=(float)(temp[2])

这里如果不进行强制类型转化,会发现在后面进行K-means算法时,数据之间的运算会出问题,这是因为,这个矩阵中数据类型是unicode型的。

 

下面就是K-means算法了:

#计算欧几里得距离
def
distEclud(vecA,vecB): return sqrt(sum(power(vecA-vecB,2))) # 计算两个向量之间的距离
#随机生成k个质心
def randCent(dataSet,k): #n=shape(dataSet)[1] n=dataSet.shape[1] centroids=mat(zeros((k,n))) for j in range(n): minJ=min(dataSet[:,j]) maxJ=max(dataSet[:,j]) rangeJ=float(maxJ-minJ) #这一步就是上面说的如果不进行强制类型转换会出现问题的位置 centroids[:,j]=minJ+rangeJ*random.rand(k,1) return centroids
#k-means算法:
def kMeans(dataSet,k,distMeans=distEclud,createCent=randCent): m=dataSet.shape[0] clusterAssment=mat(zeros((m,2))) #存放该样本属于哪类,以及距质心的距离 centroids=createCent(dataSet,k) clusterChanged=True while clusterChanged: clusterChanged=False; for i in range(m): minDist=inf;minIndex=-1; for j in range(k): distJI=distMeans(centroids[j,:],dataSet[i,:]) if distJI<minDist: minDist=distJI;minIndex=j if clusterAssment[i,0]!=minIndex:clusterChanged=True; clusterAssment[i,:]=minIndex,minDist**2 print(centroids) #在每一轮迭代后都输出一次质心的坐标
      #更新质心点的坐标
for cent in range(k): ptsInClust=dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] centroids[cent,:]=mean(ptsInClust,axis=0) return centroids,clusterAssment datMat=mat(readarff('data/temp1.arff')) myCentroids,clustAssing=kMeans(datMat,2) print(myCentroids) print(clustAssing)

K是类别的个数,这里我定为了2,;具体情况可以自己改变。