• 目录Cluster Analysis聚类分析Introduction to Unsupervised Learning无监督学习简介ClusteringDistance or Similarity Function距离或相似度函数Clustering as an Optimization Problem聚类是一个优化问题Types of ...
目录Cluster Analysis聚类分析Introduction to Unsupervised Learning无监督学习简介ClusteringDistance or Similarity Function距离或相似度函数Clustering as an Optimization Problem聚类是一个优化问题Types of Clustering聚类的类型Partitioning MethodKMeansKMeans AlgorithmKMeansKMeans算法Limitations of KMeans.KMeans的局限性MeanshiftHierarchial Clustering层次集群Agglomerative method凝聚法Density Based Clustering - DBSCANMeasuring Performance of Clusters衡量聚类的性能completeness_scorehomogeneity_scoresilhoutte_scoreExample : Selecting the number of clusters with silhouette analysis on KMeans clusteringcalinski_harabaz_score
Cluster Analysis聚类分析

Introduction to Unsupervised Learning
Clustering
Similarity or Distance Calculation
Clustering as an Optimization Function
Types of Clustering Methods
Partitioning Clustering - KMeans & Meanshift
Hierarchial Clustering - Agglomerative
Density Based Clustering - DBSCAN
Measuring Performance of Clusters

1.无监督学习简介
2.聚类
3.相似度或距离计算
4.聚类作为优化函数
5.聚类方法的类型
6.分区聚类-KMeans和Meanshift
7.层次聚类-聚集
8.基于密度的群集-DBSCAN
9.衡量集群的绩效
10.比较所有聚类方法
Introduction to Unsupervised Learning无监督学习简介

Unsupervised Learning is a type of Machine learning to draw inferences from unlabelled datasets.
Model tries to find relationship between data.
Most common unsupervised learning method is clustering which is used for exploratory data analysis to find hidden patterns or grouping in data
无监督学习是一种机器学习，可以从未标记的数据集中得出推论。
模型试图查找数据之间的关系。
最常见的无监督学习方法是聚类，用于探索性数据分析以发现隐藏模式或数据分组

Clustering

A learning technique to group a set of objects in such a way that objects of same group are more similar to each other than from objects of other group.

Applications of clustering are as follows

Automatically organizing the data
Labeling data
Understanding hidden structure of data
News Cloustering for grouping similar news together
Customer Segmentation
Suggest social groups

一种将一组对象进行分组的学习技术，使得同一组的对象比来自其他组的对象彼此更相似。

集群的应用如下:

自动整理数据
标签数据
了解数据的隐藏结构
新闻汇总，将相似的新闻分组在一起
客户细分
建议社交团体

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import make_blobs#make_blobs 产生多类数据集，对每个类的中心和标准差有很好的控制


Generating natural cluster
生成自然簇

X,y = make_blobs(n_features=2, n_samples=1000, centers=3, cluster_std=1, random_state=3)
plt.scatter(X[:,0], X[:,1], s=5, alpha=.5)


Distance or Similarity Function距离或相似度函数

Data belonging to same cluster are similar & data belonging to different cluster are different.
We need mechanisms to measure similarity & differences between data.
This can be achieved using any of the below techniques.

Minkowiski breed of distance calculation:

Manhatten (p=1), Euclidian (p=2)

Cosine: Suited for text data

属于同一集群的数据相似，而属于不同集群的数据则不同。

我们需要一种机制来衡量数据之间的相似性和差异。

这可以使用以下任何一种技术来实现。

Minkowiski距离计算品种：

曼哈顿（p = 1），欧几里得（p = 2）

余弦：适用于文本数据

from sklearn.metrics.pairwise import euclidean_distances,cosine_distances,manhattan_distances
X = [[0, 1], [1, 1]]
print(euclidean_distances(X, X))
print(euclidean_distances(X, [[0,0]]))
print(euclidean_distances(X, [[0,0]]))
print(manhattan_distances(X,X))

[[0. 1.]
[1. 0.]]
[[1.        ]
[1.41421356]]
[[1.        ]
[1.41421356]]
[[0. 1.]
[1. 0.]]

Clustering as an Optimization Problem聚类是一个优化问题

Maximize inter-cluster distances

Minimize intra-cluster distances

最大化集群间距离

最小化集群内距离

Types of Clustering聚类的类型

Partitioning methods

Partitions n data into k partitions
Initially, random partitions are created & gradually data is moved across different partitions.
It uses distance between points to optimize clusters.
KMeans & Meanshift are examples of Partitioning methods

Hierarchical methods

These methods does hierarchical decomposition of datasets.
One approach is, assume each data as cluster & merge to create a bigger cluster

Density-based methods

All above techniques are distance based & such methods can find only spherical clusters and not suited for clusters of other shapes.
Continue growing the cluster untill the density exceeds certain threashold.

分区方法

将n个数据分区为k个分区
最初，创建随机分区，然后逐渐在不同分区之间移动数据。
它使用点之间的距离来优化聚类。
KMeans和Meanshift是分区方法的示例

分层方法

这些方法对数据集进行分层分解。
一种方法是，将每个数据假定为群集并合并以创建更大的群集
另一种方法是从一个群集开始并继续拆分

基于密度的方法

所有上述技术都是基于距离的，并且此类方法只能找到球形簇，而不适合其他形状的簇。
继续生长群集，直到密度超过特定阈值。

Partitioning Method
KMeans

Minimizing creteria : within-cluster-sum-of-squares.

The centroids are chosen in such a way that it minimizes within cluster sum of squares.

The k-means algorithm divides a set of $N$ samples $X$ into $K$ disjoint clusters $C$, each described by the mean  of the samples in the cluster. $\mu$

KMeans Algorithm

Initialize k centroids.
Assign each data to the nearest centroid, these step will create clusters.
Recalculate centroid - which is mean of all data belonging to same cluster.
Repeat steps 2 & 3, till there is no data to reassign a different centroid.

Animation to explain algo - http://tech.nitoyon.com/en/blog/2013/11/07/k-means/
KMeans

最小化标准：集群内平方和。

*选择质心的方式应使其在簇的平方和之内最小。

k均值算法将一组$N $个样本$ X $划分为$ K $个不相交的聚类$ C$，每个聚类由聚类中样本的平均值来描述。 $\ mu$

KMeans算法
1.初始化k个质心。
2.将每个数据分配给最近的质心，这些步骤将创建聚类。
3.重新计算质心-这是属于同一群集的所有数据的平均值。
4.重复步骤2和3，直到没有数据可重新分配其他质心。
解释算法的动画-http://tech.nitoyon.com/zh/blog/2013/11/07/k-means/
from sklearn.datasets import make_blobs, make_moons
from sklearn.datasets import make_circles
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure(1)
x1,y1=make_circles(n_samples=1000,factor=0.5,noise=0.1)
# datasets.make_circles()专门用来生成圆圈形状的二维样本.factor表示里圈和外圈的距离之比.每圈共有n_samples/2个点，、

plt.subplot(121)
plt.title('make_circles function example')
plt.scatter(x1[:,0],x1[:,1],marker='o',c=y1)

plt.subplot(122)
x1,y1=make_moons(n_samples=1000,noise=0.1)
plt.title('make_moons function example')
plt.scatter(x1[:,0],x1[:,1],marker='o',c=y1)
plt.show()


X,y = make_blobs(n_features=2, n_samples=1000, cluster_std=.5)

plt.scatter(X[:,0], X[:,1],s=10)


from sklearn.cluster import KMeans, MeanShift

kmeans = KMeans(n_clusters=3)
kmeans.fit(X)
plt.scatter(X[:,0], X[:,1],s=10, c=kmeans.predict(X))


X, y = make_moons(n_samples=1000, noise=.09)
plt.scatter(X[:,0], X[:,1],s=10)


kmeans = KMeans(n_clusters=2)
kmeans.fit(X)
plt.scatter(X[:,0], X[:,1],s=10, c=kmeans.predict(X))


Limitations of KMeans.KMeans的局限性

Assumes that clusters are convex & behaves poorly for elongated clusters.
Probability for participation of data to multiple clusters.
KMeans tries to find local minima & this depends on init value.
假设簇是凸的，并且对于拉长的簇表现不佳。
数据参与多个集群的可能性。
KMeans尝试查找局部最小值，这取决于init值。

Meanshift

Centroid based clustering algorithm.
Mode can be understood as highest density of data points.
基于质心的聚类算法。
模式可以理解为最高数据点密度。

kmeans = KMeans(n_clusters=4)
centers = [[1, 1], [-.75, -1], [1, -1], [-3, 2]]
X, _ = make_blobs(n_samples=10000, centers=centers, cluster_std=0.6)
plt.scatter(X[:,0], X[:,1],s=10)


kmeans = KMeans(n_clusters=4)
ms = MeanShift()
kmeans.fit(X)
ms.fit(X)
plt.scatter(X[:,0], X[:,1],s=10, c=ms.predict(X))


plt.scatter(X[:,0], X[:,1],s=10, c=kmeans.predict(X))


Hierarchial Clustering层次集群

A method of clustering where you combine similar clusters to create a cluster or split a cluster into smaller clusters such they now they become better.
Two types of hierarchaial Clustering

Agglomerative method, a botton-up approach.
Divisive method, a top-down approach.

一种群集方法，您可以组合相似的群集以创建群集或将群集拆分为较小的群集，这样它们现在变得更好。
两种类型的层次聚类

凝聚法，自下而上的方法。
分裂方法，自上而下的方法。

Agglomerative method凝聚法

Combine clusters which have higher similarity.
Differences between methods arise due to different ways of defining distance (or similarity) between clusters. The following sections describe several agglomerative techniques in detail.

从为每个数据分配一个群集开始。
合并具有更高相似性的聚类。
方法之间的差异是由于定义聚类之间距离（或相似性）的方式不同而引起的。 以下各节详细介绍了几种凝聚技术。

单链接集群
完整的链接集群
平均链接聚类
平均组链接

X, y = make_moons(n_samples=1000, noise=.05)
plt.scatter(X[:,0], X[:,1],s=10)


from sklearn.cluster import AgglomerativeClustering
agc.fit(X)
plt.scatter(X[:,0], X[:,1],s=10,c=agc.labels_)


agc.labels_

array([1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1,
0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0,
1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0,
0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0,
0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1,
1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1,
0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1,
0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1,
1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0,
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0,
1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1,
0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0,
0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0,
1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 1, 1, 1, 1], dtype=int64)

Density Based Clustering - DBSCAN
centers = [[1, 1], [-1, -1], [1, -1]]
X, labels_true = make_blobs(n_samples=750, centers=centers, cluster_std=0.4,
random_state=0)
plt.scatter(X[:,0], X[:,1],s=10)


from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
X = StandardScaler().fit_transform(X)

db = DBSCAN(eps=0.3, min_samples=10).fit(X)
labels = db.labels_

plt.scatter(X[:,0], X[:,1],s=10,c=labels)


Measuring Performance of Clusters衡量聚类的性能

Two forms of evaluation
supervised, which uses a ground truth class values for each sample.

completeness_score
homogeneity_score

unsupervised, which measures the quality of model itself

silhoutte_score
calinski_harabaz_score

两种评估形式
有监督的，它对每个样本使用基本事实类别值。

completeness_score
homogenity_score

无人监督，它衡量模型本身的质量

silhoutte_score
calinski_harabaz_score

completeness_score

A clustering result satisfies completeness if all the data points that are members of a given class are elements of the same cluster.
Accuracy is 1.0 if data belonging to same class belongs to same cluster, even if multiple classes belongs to same cluster
如果属于给定类的所有数据点都是同一群集的元素，则群集结果将满足完整性要求
如果属于同一类别的数据属于同一群集，则即使多个类别属于同一群集，精度也为1.0

from sklearn.metrics.cluster import completeness_score

completeness_score( labels_true=[10,10,11,11],labels_pred=[1,1,0,0])

1.0


The acuracy is 1.0 because all the data belonging to same class belongs to same cluster

completeness_score( labels_true=[11,22,22,11],labels_pred=[1,0,1,1])

0.3836885465963443


The accuracy is .3 because class 1 - [11,22,11], class 2 - [22]

print(completeness_score([10, 10, 11, 11], [0, 0, 0, 0]))

1.0

homogeneity_score

A clustering result satisfies homogeneity if all of its clusters contain only data points which are members of a single class.

from sklearn.metrics.cluster import homogeneity_score

homogeneity_score([0, 0, 1, 1], [1, 1, 0, 0])

1.0

homogeneity_score([0, 0, 1, 1], [0, 1, 2, 3])

0.9999999999999999

homogeneity_score([0, 0, 0, 0], [1, 1, 0, 0])

1.0


Same class data is broken into two clusters

silhoutte_score

The Silhouette Coefficient is calculated using the mean intra-cluster distance (a) and the mean nearest-cluster distance (b) for each sample.
The Silhouette Coefficient for a sample is (b - a) / max(a, b). To clarify, b is the distance between a sample and the nearest cluster that the sample is not a part of.

Example : Selecting the number of clusters with silhouette analysis on KMeans clustering
from sklearn.datasets import make_blobs
X, Y = make_blobs(n_samples=500,
n_features=2,
centers=4,
cluster_std=1,
center_box=(-10.0, 10.0),
shuffle=True,
random_state=1)

plt.scatter(X[:,0],X[:,1],s=10)


range_n_clusters = [2, 3, 4, 5, 6]
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
for n_cluster in range_n_clusters:
kmeans = KMeans(n_clusters=n_cluster)
kmeans.fit(X)
labels = kmeans.predict(X)
print (n_cluster, silhouette_score(X,labels))

2 0.7049787496083262
3 0.5882004012129721
4 0.6505186632729437
5 0.5745029081702377
6 0.4544511749648251


Optimal number of clusters seems to be 2

calinski_harabaz_score

The score is defined as ratio between the within-cluster dispersion and the between-cluster dispersion.
群集的最佳数量似乎是2
分数定义为群集内分散度和群集间分散度之间的比率。

from sklearn.metrics import calinski_harabaz_score

for n_cluster in range_n_clusters:
kmeans = KMeans(n_clusters=n_cluster)
kmeans.fit(X)
labels = kmeans.predict(X)
print (n_cluster, calinski_harabaz_score(X,labels))

2 1604.112286409658
3 1809.991966958033
4 2704.4858735121097
5 2281.933655783649
6 2016.011232059114



展开全文
• ## python 文本聚类

千次阅读 2017-10-23 21:34:56
在本教程中，我会利用 Python 来说明怎样聚类一系列的文档。我所演示的实例会识别出 top 100 电影的（来自 IMDB 列表）剧情简介的隐藏结构。关于这个例子的详细讨论在初始版本里。本教程包括：对所有剧情简介分词...


原地址：http://python.jobbole.com/85481/

在本教程中，我会利用 Python 来说明怎样聚类一系列的文档。我所演示的实例会识别出 top 100 电影的（来自 IMDB 列表）剧情简介的隐藏结构。关于这个例子的详细讨论在初始版本里。本教程包括：对所有剧情简介分词（tokenizing）和词干化（stemming）

利用 tf-idf 将语料库转换为向量空间（vector space）计算每个文档间的余弦距离（cosine distance）用以测量相似度利用 k-means 算法进行文档聚类利用多维尺度分析（multidimensional scaling）对语料库降维利用 matplotlib 和 mpld3 绘制输出的聚类对语料库进行Ward 聚类算法生成层次聚类（hierarchical clustering）绘制 Ward 树状图（Ward dendrogram）利用 隐含狄利克雷分布（LDA） 进行主题建模

整个项目在我的 github repo 都可以找到。其中‘cluster_analysis ‘工作簿是一个完整的版本；‘cluster_analysis_web’ 为了创建教程则经过了删减。欢迎下载代码并使用‘cluster_analysis’ 进行单步调试（step
through）。

如果你有任何问题，欢迎用推特来联系我 @brandonmrose。

在此之前，我先在前面导入所有需要用到的库

Python

12345678import numpy as npimport pandas as pdimport nltkimport reimport osimport codecsfrom sklearn import feature_extractionimport mpld3出于走查的目的，想象一下我有 2 个主要的列表：‘titles’：按照排名的影片名称‘synopses’：对应片名列表的剧情简介我在 github 上 po 出来的完整工作簿已经导入了上述列表，但是为了简洁起见，我会直接使用它们。其中最最重要的是 ‘synopses’ 列表了，‘titles’ 更多是作为了标记用的。Python

1

2

print
titles[:10]#前
10 个片名

Python

1['The Godfather', 'The Shawshank Redemption', "Schindler's List", 'Raging Bull', 'Casablanca', "One Flew Over the Cuckoo's Nest", 'Gone with the Wind', 'Citizen Kane', 'The Wizard of Oz', 'Titanic']停用词，词干化与分词本节我将会定义一些函数对剧情简介进行处理。首先，我载入 NLTK 的英文停用词列表。停用词是类似“a”，“the”，或者“in”这些无法传达重要意义的词。我相信除此之外还有更好的解释。Python

1

2

#
载入 nltk 的英文停用词作为“stopwords”变量

stopwords=nltk.corpus.stopwords.words('english')

Python

1print stopwords[:10]Python

1

['i','me','my','myself','we','our','ours','ourselves','you','your']

接下来我导入 NLTK 中的 Snowball 词干分析器（Stemmer）。词干化（Stemming）的过程就是将词打回原形。

Python

123# 载入 nltk 的 SnowballStemmer 作为“stemmer”变量from nltk.stem.snowball import SnowballStemmerstemmer = SnowballStemmer("english")以下我定义了两个函数：tokenize_and_stem：对每个词例（token）分词（tokenizes）（将剧情简介分割成单独的词或词例列表）并词干化tokenize_only: 分词即可我利用上述两个函数创建了一个重要的字典，以防我在后续算法中需要使用词干化后的词（stems）。出于展示的目的，后面我又会将这些词转换回它们原本的的形式。猜猜看会怎样，我实在想试试看！Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#
这里我定义了一个分词器（tokenizer）和词干分析器（stemmer），它们会输出给定文本词干化后的词集合

deftokenize_and_stem(text):

#
首先分句，接着分词，而标点也会作为词例存在

tokens=[wordforsentinnltk.sent_tokenize(text)forwordinnltk.word_tokenize(sent)]

filtered_tokens=[]

#
过滤所有不含字母的词例（例如：数字、纯标点）

fortokenintokens:

ifre.search('[a-zA-Z]',token):

filtered_tokens.append(token)

stems=[stemmer.stem(t)fortinfiltered_tokens]

returnstems

deftokenize_only(text):

#
首先分句，接着分词，而标点也会作为词例存在

tokens=[word.lower()forsentinnltk.sent_tokenize(text)forwordinnltk.word_tokenize(sent)]

filtered_tokens=[]

#
过滤所有不含字母的词例（例如：数字、纯标点）

fortokenintokens:

ifre.search('[a-zA-Z]',token):

filtered_tokens.append(token)

returnfiltered_tokens

接下来我会使用上述词干化/分词和分词函数遍历剧情简介列表以生成两个词汇表：经过词干化和仅仅经过分词后。

Python

12345678910# 非常不 pythonic，一点也不！# 扩充列表后变成了非常庞大的二维（flat）词汇表totalvocab_stemmed = []totalvocab_tokenized = []for i in synopses:    allwords_stemmed = tokenize_and_stem(i) #对每个电影的剧情简介进行分词和词干化    totalvocab_stemmed.extend(allwords_stemmed) # 扩充“totalvocab_stemmed”列表     allwords_tokenized = tokenize_only(i)    totalvocab_tokenized.extend(allwords_tokenized)利用上述两个列表，我创建了一个 pandas 的 DataFrame，以词干化后的词汇表作为索引，分词后的词为列。这么做便于观察词干化后的词转换回完整的词例。以下展示词干化后的词变回原词例是一对多（one to many）的过程：词干化后的“run”能够关联到“ran”，“runs”，“running”等等。在我看来这很棒——我非常愿意将我需要观察的词干化过后的词转换回第一个联想到的词例。Python

1

2

vocab_frame=pd.DataFrame({'words':totalvocab_tokenized},index=totalvocab_stemmed)

print'there
are '+str(vocab_frame.shape[0])+'
items in vocab_frame'

Python

1there are 312209 items in vocab_frame你会注意到有些重复的地方。我可以把它清理掉，不过鉴于 DataFrame 只有 312209 项，并不是很庞大，可以用 stem-index 来观察词干化后的词。Python

1

print

Python

123456wordsplot  plotedit  editedit  editedit  editon      onTf-idf 与文本相似度下面，我定义词频-逆向文件频率（tf-idf）的向量化参数，把剧情简介列表都转换成 tf-idf 矩阵。为了得到 TF-IDF 矩阵，首先计算词在文档中的出现频率，它会被转换成文档-词矩阵（dtm），也叫做词频（term frequency）矩阵。dtm 的例子如下图所示：接着使用 TF-IDF 权重：某些词在某个文档中出现频率高，在其他文中却不常出现，那么这些词具有更高的 TF-IDF 权重，因为这些词被认为在相关文档中携带更多信息。注意我下面定义的几个参数：max_df：这个给定特征可以应用在 tf-idf 矩阵中，用以描述单词在文档中的最高出现率。假设一个词（term）在 80% 的文档中都出现过了，那它也许（在剧情简介的语境里）只携带非常少信息。min_df：可以是一个整数（例如5）。意味着单词必须在 5 个以上的文档中出现才会被纳入考虑。在这里我设置为 0.2；即单词至少在 20% 的文档中出现 。因为我发现如果我设置更小的 min_df，最终会得到基于姓名的聚类（clustering）——举个例子，好几部电影的简介剧情中老出现“Michael”或者“Tom”这些名字，然而它们却不携带什么真实意义。ngram_range：这个参数将用来观察一元模型（unigrams），二元模型（ bigrams） 和三元模型（trigrams）。参考n元模型（n-grams）。Python

1

2

3

4

5

6

7

8

9

10

11

fromsklearn.feature_extraction.textimportTfidfVectorizer

#
定义向量化参数

tfidf_vectorizer=TfidfVectorizer(max_df=0.8,max_features=200000,

min_df=0.2,stop_words='english',

use_idf=True,tokenizer=tokenize_and_stem,ngram_range=(1,3))

%timetfidf_matrix=tfidf_vectorizer.fit_transform(synopses)#
向量化剧情简介文本

print(tfidf_matrix.shape)

Python

123CPU times: user 29.1 s, sys: 468 ms, total: 29.6 sWall time: 37.8 s(100, 563)“terms” 这个变量只是 tf-idf 矩阵中的特征（features）表，也是一个词汇表。Python

1

terms=tfidf_vectorizer.get_feature_names()

dist 变量被定义为 1 – 每个文档的余弦相似度。余弦相似度用以和 tf-idf 相互参照评价。可以评价全文（剧情简介）中文档与文档间的相似度。被 1 减去是为了确保我稍后能在欧氏（euclidean）平面（二维平面）中绘制余弦距离。

注意 dist 可以用以评估任意两个或多个剧情简介间的相似度。

Python

12from sklearn.metrics.pairwise import cosine_similaritydist = 1 - cosine_similarity(tfidf_matrix)K-means 聚类下面开始好玩的部分。利用 tf-idf 矩阵，你可以跑一长串聚类算法来更好地理解剧情简介集里的隐藏结构。我首先用 k-means 算法。这个算法需要先设定聚类的数目（我设定为 5）。每个观测对象（observation）都会被分配到一个聚类，这也叫做聚类分配（cluster assignment）。这样做是为了使组内平方和最小。接下来，聚类过的对象通过计算来确定新的聚类质心（centroid）。然后，对象将被重新分配到聚类，在下一次迭代操作中质心也会被重新计算，直到算法收敛。跑了几次这个算法以后我发现得到全局最优解（global optimum）的几率要比局部最优解（local optimum）大。Python

1

2

3

4

5

6

7

8

9

10

fromsklearn.clusterimportKMeans

num_clusters=5

km=KMeans(n_clusters=num_clusters)

%timekm.fit(tfidf_matrix)

clusters=km.labels_.tolist()

Python

12CPU times: user 232 ms, sys: 6.64 ms, total: 239 msWall time: 305 ms利用 joblib.dump pickle 模型（model），一旦算法收敛，重载模型并分配聚类标签（labels）。Python

1

2

3

4

5

6

7

8

9

fromsklearn.externalsimportjoblib

#
注释语句用来存储你的模型

#
因为我已经从 pickle 载入过模型了

#joblib.dump(km,  'doc_cluster.pkl')

clusters=km.labels_.tolist()

下面，我创建了一个字典，包含片名，排名，简要剧情，聚类分配，还有电影类型（genre）（排名和类型是从 IMDB 上爬下来的）。

为了方便起见，我将这个字典转换成了 Pandas DataFrame。我是 Pandas 的脑残粉，我强烈建议你了解一下它惊艳的功能。这些我下面就会使用到，但不会深入。

Python

123films = { 'title': titles, 'rank': ranks, 'synopsis': synopses, 'cluster': clusters, 'genre': genres } frame = pd.DataFrame(films, index = [clusters] , columns = ['rank', 'title', 'cluster', 'genre'])Python

1

frame['cluster'].value_counts()#number
of films per cluster (clusters from 0 to 4)

Python

14 26Python

1

2

3

4

5

0    25

2    21

1    16

3    12

dtype:int64

Python

123grouped = frame['rank'].groupby(frame['cluster']) # 为了凝聚（aggregation），由聚类分类。 grouped.mean() # 每个聚类的平均排名（1 到 100）Python

1

2

3

4

5

6

7

cluster

0          47.200000

1          58.875000

2          49.380952

3          54.500000

4          43.730769

dtype:float64

clusters 4 和 clusters 0 的排名最低，说明它们包含的影片在 top 100 列表中相对没那么棒。

在这选取 n（我选 6 个） 个离聚类质心最近的词对聚类进行一些好玩的索引（indexing）和排列（sorting）。这样可以更直观观察聚类的主要主题。

Python

1234567891011121314151617181920from __future__ import print_function print("Top terms per cluster:")print()# 按离质心的距离排列聚类中心，由近到远order_centroids = km.cluster_centers_.argsort()[:, ::-1]  for i in range(num_clusters):    print("Cluster %d words:" % i, end='')     for ind in order_centroids[i, :6]: # 每个聚类选 6 个词        print(' %s' % vocab_frame.ix[terms[ind].split(' ')].values.tolist()[0][0].encode('utf-8', 'ignore'), end=',')    print() # 空行    print() # 空行     print("Cluster %d titles:" % i, end='')    for title in frame.ix[i]['title'].values.tolist():        print(' %s,' % title, end='')    print() # 空行    print() # 空行聚类中的前几项：聚类 0 中的单词: family, home, mother, war, house, dies,聚类 0 中的片名: Schindler’s List, One Flew Over the Cuckoo’s Nest, Gone with the Wind, The Wizard of Oz, Titanic, Forrest Gump, E.T. the Extra-Terrestrial, The Silence of the Lambs, Gandhi, A Streetcar Named Desire, The Best Years of Our Lives, My Fair Lady, Ben-Hur, Doctor Zhivago, The Pianist, The Exorcist, Out of Africa, Good Will Hunting, Terms of Endearment, Giant, The Grapes of Wrath, Close Encounters of the Third Kind, The Graduate, Stagecoach, Wuthering Heights,聚类 1 中的单词: police, car, killed, murders, driving, house,聚类 1 中的片名: Casablanca, Psycho, Sunset Blvd., Vertigo, Chinatown, Amadeus, High Noon, The French Connection, Fargo, Pulp Fiction, The Maltese Falcon, A Clockwork Orange, Double Indemnity, Rebel Without a Cause, The Third Man, North by Northwest,聚类 2 中的单词: father, new, york, new, brothers, apartments,聚类 2 中的片名: The Godfather, Raging Bull, Citizen Kane, The Godfather: Part II, On the Waterfront, 12 Angry Men, Rocky, To Kill a Mockingbird, Braveheart, The Good, the Bad and the Ugly, The Apartment, Goodfellas, City Lights, It Happened One Night, Midnight Cowboy, Mr. Smith Goes to Washington, Rain Man, Annie Hall, Network, Taxi Driver, Rear Window,聚类 3 中的单词: george, dance, singing, john, love, perform,聚类 3 中的片名: West Side Story, Singin’ in the Rain, It’s a Wonderful Life, Some Like It Hot, The Philadelphia Story, An American in Paris, The King’s Speech, A Place in the Sun, Tootsie, Nashville, American Graffiti, Yankee Doodle Dandy,聚类 4 中的单词: killed, soldiers, captain, men, army, command,聚类 4 中的片名: The Shawshank Redemption, Lawrence of Arabia, The Sound of Music, Star Wars, 2001: A Space Odyssey, The Bridge on the River Kwai, Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb, Apocalypse Now, The Lord of the Rings: The Return of the King, Gladiator, From Here to Eternity, Saving Private Ryan, Unforgiven, Raiders of the Lost Ark, Patton, Jaws, Butch Cassidy and the Sundance Kid, The Treasure of the Sierra Madre, Platoon, Dances with Wolves, The Deer Hunter, All Quiet on the Western Front, Shane, The Green Mile, The African Queen, Mutiny on the Bounty,多维尺度分析（Multidimensional scaling）利用下面多维尺度分析（MDS）的代码将距离矩阵转化为一个二维数组。我并不想假装我很了解MDS，不过这个算法很管用。另外可以用 特征降维（principal component analysis）来完成这个任务。Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

importos  #
为了使用 os.path.basename 函数

importmatplotlib.pyplotasplt

importmatplotlibasmpl

fromsklearn.manifoldimportMDS

MDS()

#
将二位平面中绘制的点转化成两个元素（components）

#
设置为“precomputed”是因为我们提供的是距离矩阵

#
我们可以将“random_state”具体化来达到重复绘图的目的

mds=MDS(n_components=2,dissimilarity="precomputed",random_state=1)

pos=mds.fit_transform(dist)  #
形如 (n_components, n_samples)

xs,ys=pos[:,0],pos[:,1]

可视化文档聚类

本节中，我会演示怎样利用 matplotlib 和 mpld3（将 matplotlib 封装成 D3.js）来实现文档聚类的可视化。

首先，我定义了一些字典，让聚类的编号和聚类绘色，聚类名称一一对应。其中聚类对应的名称是从离聚类质心最近的单词中挑选出来的。

Python

123456789# 用字典设置每个聚类的颜色cluster_colors = {0: '#1b9e77', 1: '#d95f02', 2: '#7570b3', 3: '#e7298a', 4: '#66a61e'} # 用字典设置每个聚类名称cluster_names = {0: 'Family, home, war',                  1: 'Police, killed, murders',                  2: 'Father, New York, brothers',                  3: 'Dance, singing, love',                  4: 'Killed, soldiers, captain'}下面我会用 matplotlib 来绘制彩色的带标签的观测对象（影片，片名）。关于 matplotlib 绘图我不想讨论太多，但我尽可能提供一些有用的注释。Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

#
在 ipython 中内联（inline）演示 matplotlib 绘图

%matplotlibinline

#
用 MDS 后的结果加上聚类编号和绘色创建 DataFrame

df=pd.DataFrame(dict(x=xs,y=ys,label=clusters,title=titles))

#
聚类归类

groups=df.groupby('label')

#
设置绘图

fig,ax=plt.subplots(figsize=(17,9))#
设置大小

ax.margins(0.05)#

#
对聚类进行迭代并分布在绘图上

#
我用到了 cluster_name 和 cluster_color 字典的“name”项，这样会返回相应的 color 和 label

forname,groupingroups:

ax.plot(group.x,group.y,marker='o',linestyle='',ms=12,

label=cluster_names[name],color=cluster_colors[name],

mec='none')

ax.set_aspect('auto')

ax.tick_params(

axis='x',          #
使用 x 坐标轴

which='both',      #
同时使用主刻度标签（major ticks）和次刻度标签（minor ticks）

bottom='off',      #
取消底部边缘（bottom edge）标签

top='off',        #
取消顶部边缘（top edge）标签

labelbottom='off')

ax.tick_params(

axis='y',        #
使用 y 坐标轴

which='both',      #
同时使用主刻度标签（major ticks）和次刻度标签（minor ticks）

left='off',      #
取消底部边缘（bottom edge）标签

top='off',        #
取消顶部边缘（top edge）标签

labelleft='off')

ax.legend(numpoints=1)  #
图例（legend）中每项只显示一个点

#
在坐标点为 x,y 处添加影片名作为标签（label）

foriinrange(len(df)):

ax.text(df.ix[i]['x'],df.ix[i]['y'],df.ix[i]['title'],size=8)

plt.show()#
展示绘图

#
以下注释语句可以保存需要的绘图

#plt.savefig('clusters_small_noaxes.png',
dpi=200)

Python

1plt.close()绘制的聚类分布图看起来不错，但是重叠在一起的标签真是亮瞎了眼。因为之前使用过 D3.js，所以我知道有个解决方案是基于浏览器和 javascript 交互的。所幸我最近偶然发现了 mpld3，是基于 matplotlib 的 D3 封装。Mpld3 主要可以让你使用 matplotlib 的语法实现网页交互。它非常容易上手，当你遇到感兴趣的内容，鼠标停驻的时候，利用高效的接口可以添加气泡提示。另外，它还提供了缩放和拖动这么炫的功能。以下的 javascript 片段主要自定义了缩放和拖动的位置。别太担心，实际上你用不到它，但是稍后导出到网页的时候有利于格式化。你唯一想要改变的应该是借助 x 和 y 的 attr 来改变工具栏的位置。Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

#
自定义工具栏（toolbar）位置

classTopToolbar(mpld3.plugins.PluginBase):

"""移动工具栏到分布图顶部的插件"""

JAVASCRIPT="""

mpld3.register_plugin("toptoolbar",
TopToolbar);

TopToolbar.prototype = Object.create(mpld3.Plugin.prototype);

TopToolbar.prototype.constructor
= TopToolbar;

function TopToolbar(fig, props){

mpld3.Plugin.call(this,
fig, props);

};

TopToolbar.prototype.draw
= function(){

//  还缺少工具栏 svg，因此一开始要绘制

this.fig.toolbar.draw();

// 接着把 y 的位置变为图顶部

this.fig.toolbar.toolbar.attr("x",
150);

this.fig.toolbar.toolbar.attr("y",
400);

// 再移除 draw 函数，防止被调用

this.fig.toolbar.draw =
function() {}

}

"""

def__init__(self):

self.dict_={"type":"toptoolbar"}

下面是对于交互式散点图的实际操作。我同样不会深入这个问题因为是直接从 mpld3 的例程移植过来的。虽然我用 pandas 对聚类进行了归类，但它们一一迭代后会分布在散点图上。和原生 D3 相比，用 mpld3 来做这项工作并且嵌入到 python 的工作簿中简单多了。如果你看了我网站上的其它内容，你就知道我有多么爱 D3 了。但以后一些基本的交互我可能还是会用 mpld3。

记住 mpld3 还可以自定义 CSS，像我设计的字体，坐标轴还有散点图左边的间距（margin）。

Python

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354# 用 MDS 后的结果加上聚类编号和绘色创建 DataFramedf = pd.DataFrame(dict(x=xs, y=ys, label=clusters, title=titles))  # 聚类归类groups = df.groupby('label') # 自定义 css 对字体格式化以及移除坐标轴标签css = """text.mpld3-text, div.mpld3-tooltip {  font-family:Arial, Helvetica, sans-serif;} g.mpld3-xaxis, g.mpld3-yaxis {display: none; } svg.mpld3-figure {margin-left: -200px;}""" # 绘图 fig, ax = plt.subplots(figsize=(14,6)) # 设置大小ax.margins(0.03) # 可选项，只添加 5% 的填充（padding）来自动缩放 # 对聚类进行迭代并分布在绘图上# 我用到了 cluster_name 和 cluster_color 字典的“name”项，这样会返回相应的 color 和 labelfor name, group in groups:    points = ax.plot(group.x, group.y, marker='o', linestyle='', ms=18,                      label=cluster_names[name], mec