
? 作者:韩信子@ShowMeAI
? 数据分析实战系列:https://www.showmeai.tech/tutorials/40
? 机器学习实战系列:https://www.showmeai.tech/tutorials/41
? 本文地址:https://www.showmeai.tech/article-detail/334
? 声明:版权所有,转载请联系平台与作者并注明出处
? 收藏ShowMeAI查看更多精彩内容

很多公司的技术人员在做用户画像的工作,细分客户/客户分群是一个很有意义的工作,可以确保企业构建更个性化的消费者针对策略,同时优化产品和服务。
在机器学习的角度看,客户分群通常会采用无监督学习的算法完成。应用这些方法,我们会先收集整理客户的基本信息,例如地区、性别、年龄、偏好等,再对其进行分群。

在之前的文章 ?基于机器学习的用户价值数据挖掘与客户分群中,ShowMeAI 已经做了一些用户分群实操介绍,本篇内容中,ShowMeAI 将更深入地介绍聚类分群的方法,使用更丰富的建模方式,并剖析模型评估的方法模式。

我们先使用 pandas 加载 ?Mall_Customers数据,并做了一些最基本的数据清洗,把字段名称更改为清晰可理解的字符串格式。
? 实战数据集下载(百度网盘):公众号『ShowMeAI研究中心』回复『实战』,或者点击 这里 获取本文 [27]基于多种聚类算法的商城用户分群!绘制精准用户画像 『Mall_Customers数据集』
⭐ ShowMeAI官方GitHub:https://github.com/ShowMeAI-Hub
df= pd.read csv( "Mall Customers.csv")
df.rename (columns={"CustomerID": "id", "Age": "age", "Annual Income (k$)": "annual_income", "Spending Score (1-100)": "spending_score"}, inplace=True)
df.drop(columns=["id"], inplace=True)

本文数据操作处理与分析涉及的工具和技能,欢迎大家查阅 ShowMeAI 对应的教程和工具速查表,快学快用。
下面我们对数据做一些探索性数据分析,首先我们的特征字段可以分为数值型和类别型两种类型。后面我们单独对两类特征字段进行分析。
numcol = ["age", "annual_income", "spending_score"]
objcol = ['Gender']
我们对性别(女性和男性)做计数统计和绘图,代码如下:
sns.set_style("ticks")
my_pal = {"Male": "slateblue", "Female": "lightsalmon"}
ax = sns.countplot(data=df, x="Gender", palette=-my_pal)
ax.grid(True, axis='both' )
for p in ax.patches:
ax.annotate( '{:.Of}'. format(p.get _height()), (p.get _x()+0.25, p.get_height()+0.3))
percentage = "{:.If}%'. format(100 * p.get height )/lendf[ "Gender" ]))
ax.annotate(percentage, (p.get x()+0.25, p.get height ( )/2))
olt.title( "Gender Countolot")

后续的用户分群会使用到聚类算法,为了确保聚类算法可以正常工作,我们会查看连续值数据分布并检查异常值。如果不加这个步骤,严重倾斜的数据和异常值可能会导致很多问题。

如上图所示,除了annual_income特征有一个异常值之外,大多数数值特征已经很规整了。
sns.set_style("ticks", {'axes.grid' : False})
for idx, col in enumerate (numcol):
plt.figure()
f, ax = plt.subplots(nrows=2, sharex=True, gridspec_kw={"height_ratios": (0.2,0.85)}, figsize=(10,8));
plt.suptitle(f"{col.upper()}",y=0.93);
sns.boxplot(data=df,x=col,ax=ax[0],color="slateblue",boxprops=dict(alpha=.7),
linewidth=0.8, width=0.6, fliersize=10,
flierprops={ "marker" :"O", "markerfacecolor": "slateblue"},
medianprops={ "color": "black", "linewidth":2.5})
sns.histplot(data=df, ×=col, ax=ax[1],multiple="layer", fill=True, color= "slateblue", bins=40)
ax2 =ax[1].twinx()
sns.kdeplot(data=df, x=col, ax=ax2,
multiple="layer",
fill=True,
color="slateblue",
bw_adjust=0.9,
alpha=0.1,
linestyles="--")
ax[1].grid(False)
ax[0].set(xlabel="");
ax[1].set _xlabel(col, fontsize=14)
ax[1].grid(True)



我们再对两两的特征做联合分析,代码和绘制结果如下:
sns.set_style("ticks", {'axes.grid' : False})
def pairplot_hue(df, hue, **kwargs):
g = sns.pairplot(df, hue=hue, **kwargs)
g.fig.subplots_adjust(top=0.9)
g.fig.suptitle(hue)
return g
pairplot_hue(df[numcol+objcol], hue='Gender')

为了保证后续聚类算法的性能效果,数值特征在送入模型之前需要做缩放处理。我们直接使用 sklearn 中的 MinMaxScaler 缩放方法来完成这项工作,将数值型字段数据范围转换为 [0,1]。
scaler = MinMaxScaler()
df_scaled = df.copy()
for col in numcol:
df scaled[col] = pd.DataFrame(scaler.fit_transform(df_scaled[col].values.reshape(-1,1) ))
本篇内容涉及的聚类无监督学习算法,欢迎大家查看ShowMeAI的教程文章:
K-Means 算法是一种无监督学习算法,它通过迭代和聚合来根据数据分布确定数据属于哪个簇。
![]() |
BIRCH(Balanced Iterative Reducing and Clustering Using Hierarchies)翻译为中文就是『利用层次方法的平衡迭代规约和聚类』,全称非常复杂。简单来说,BIRCH 算法利用了一个树结构来帮助我们快速的聚类,这个特殊的树结构,就是我们后面要详细介绍的聚类特征树(CF-tree)。简单地说算法可以分为两步:
1)扫描数据库,建立一棵存放于内存的 CF-Tree,它可以被看作数据的多层压缩,试图保留数据的内在聚类结构;
2)采用某个选定的聚类算法,如 K-Means 或者凝聚算法,对 CF 树的叶节点进行聚类,把稀疏的簇当作离群点删除,而把更稠密的簇合并为更大的簇。
![]() |
虽然说聚类是一个无监督学习算法,但我们也有一些方法可以对其最终聚类效果进行评估,对我们的建模和聚合有一些指导作用。
◉ 轮廓分数(Silhouette score)
轮廓分数( Silhouette score)是一种常用的聚类评估方式。对于单个样本,设 a 是与它同类别中其他样本的平均距离,b 是与它距离最近不同类别中样本的平均距离,轮廓系数为:

对于一个数据集,它的轮廓系数是所有样本轮廓系数的平均值。轮廓系数取值范围是 [-1,1],同类别样本越距离相近且不同类别样本距离越远,分数越高。

◉ 卡林斯基哈拉巴斯得分(Calinski Harabasz score)
卡林斯基哈拉巴斯得分(Calinski Harabasz score)也称为方差比标准,由所有簇的簇间离散度(Between Group Sum of Squares, BGSS)之和与簇内离散度(Within Group Sum of Squares, WGSS)之和的比值计算得出。较高的 Calinski Harabasz 分数意味着更好的聚类(每个聚类中更密集)。以下给出计算过程:
第一步:计算簇间离散度(Between Group Sum of Squares, BGSS)

第二部:计算簇内离散度(Within Group Sum of Squares, WGSS)

第三步:计算卡林斯基哈拉巴斯得分(Calinski Harabasz score)

◉ 戴维斯布尔丹得分(Davies Bouldin score)
戴维斯布尔丹得分(Davies Bouldin score)表示每个集群与与其最相似的集群或每个集群的内部模式的平均相似度。最低可能或最接近零表示更好的聚类。
我们先应用 K-Means 聚类对数据进行建模,聚合得到不同的用户簇,代码如下:
k_range = range(2,10)
for x in k range:
model = KMeans(n_clusters=x, random_state=42)
X = df_scaled[[ "annual_ income", "spending_score"]]
model.fit(x)
评估 K-Means 算法的一种非常有效的方法是肘点法,它会可视化具有不同数量的簇的平方距离之和(失真分数)的加速变化(递减收益)的过程。
我们结合上述提到的3个得分,以及肘点法进行计算和绘图如下:


如上图所示,簇数 = 5 是适用于该数据集的适当簇数,因为它有着这些特性:
我们以5为聚类个数,对数据重新聚类,并分发聚类 id,然后再对数据进行分布分析绘图,不同的用户簇的数据分布如下(我们可以比较清晰看到不同用户群的分布差异)。

我们再使用 BIRCH 进行聚类,代码如下:
n = range(2,10)
for x in n:
model = Birch(n_clusters=x, threshold=0.17)
X = df_scaledI[ "annual income", "spending_score"]]
model.fit(X)
与 K-Means 聚类不同,BIRCH 聚类没有失真分数。其他3 个评分指标(Silhouette、CH、DBI)仍然相同。


BIRCH 的计算也给出了簇数等于5这样的一个结论。我们同样对数据进行分布分析绘图,不同的用户簇的数据分布如下(依旧可以比较清晰看到不同用户群的分布差异)。

两种算法都得出相似的结果(不完全相同)。
我们来对聚类后的结果做一些解释分析,如下:

如上图所示,从年龄的角度来看,不同的用户簇有各自的一些分布特点:

从收入维度来看:
综合年龄和年收入得出以下结果。

结果表明
从花费的角度来看分组的用户群:

结果表明
综合支出分和年收入来看。

结果表明:
我们对各个用户群进行平均汇总,并绘制图表如下:

我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。
我在新的Debian6VirtualBoxVM上安装RVM时遇到问题。我已经安装了所有需要的包并使用下载了安装脚本(curl-shttps://rvm.beginrescueend.com/install/rvm)>rvm,但以单个用户身份运行时bashrvm我收到以下错误消息:ERROR:Unabletocheckoutbranch.安装在这里停止,并且(据我所知)没有安装RVM的任何文件。如果我以root身份运行脚本(对于多用户安装),我会收到另一条消息:Successfullycheckedoutbranch''安装程序继续并指示成功,但未添加.rvm目录,甚至在修改我的.bas
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
有人知道如何将capybarapoltergeist的用户代理覆盖到移动用户代理以进行测试吗?我发现了一些有关为seleniumwebdriver配置它的信息:http://blog.plataformatec.com.br/2011/03/configuring-user-agents-with-capybara-selenium-webdriver/这在capybara闹鬼中怎么可能? 最佳答案 请参阅poltergeistgithub页面上的链接:https://github.com/teampoltergeist/polte
A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(
我想验证一个电子邮件地址是否是PayPal用户。是否有API调用来执行此操作?是否有执行此操作的ruby库?谢谢 最佳答案 GetVerifiedStatus来自PayPal'sAdaptiveAccounts平台会为您做这件事。PayPal没有任何codesamples或SDKs用于Ruby中的自适应帐户,但我确实找到了编写codeforGetVerifiedStatusinRuby的人.您需要更改该代码以检查他们拥有的帐户类型的唯一更改是更改if@xml['accountStatus']!=nilaccount_status