最近做一个有关二分类问题,我打算使用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,;具体情况可以自己改变。