[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"consumer-news-detail-985":3,"consumer-news-interaction-985":41,"consumer-news-related-985":44},{"detail":4,"item":36},{"card":5,"schemaVersion":23,"fields":24,"content":30},{"id":6,"kind":7,"targetType":8,"targetId":9,"subtype":7,"typeLabel":10,"title":11,"subtitle":12,"summary":13,"coverUrl":14,"badgeText":15,"href":16,"sourceName":12,"meta":17,"metrics":20,"tags":21,"resolved":22},"NEWS_ARTICLE:985","news","NEWS_ARTICLE",985,"资讯","机器学习项目：客户分群——K-Means 聚类从选参到业务画像的完整实战","博客园","商场用户分群： 一、前言 在商场运营中，面对成千上万的顾客，如果用同一套营销策略对待所有人，效果往往事倍功半。高收入的中年人和月光族的年轻人等等群体，消费习惯和偏好截然不同——这就需要用户分群（Customer Segmentation）：根据用户的年龄、收入、消费行为等特征，将相似的用户归为一类，","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F3775135\u002F202609\u002F3775135-20260908154332888-1132576367.png","","\u002Fnews\u002F985",[18,19],"2026","人工智能",{},[19],true,"consumer-content-detail-v1",{"sourceName":12,"authorName":25,"categoryName":19,"summary":13,"description":13,"publishTime":26,"updateTime":27,"sourceUrl":28,"language":29},"myLv","2026-09-08T16:01","2026-09-11T15:22:01","https:\u002F\u002Fwww.cnblogs.com\u002FmyInception\u002Fp\u002F22891264","中文",{"format":31,"policy":32,"normalized":22,"html":33,"text":34,"wordCount":35,"hasBody":22},"HTML","NEWS_CONTENT_V1","商场用户分群：\n\u003Ch2>一、前言\u003C\u002Fh2>\n\u003Cp>在商场运营中，面对成千上万的顾客，如果用同一套营销策略对待所有人，效果往往事倍功半。高收入的中年人和月光族的年轻人等等群体，消费习惯和偏好截然不同——这就需要\u003Cstrong>用户分群（Customer Segmentation）\u003C\u002Fstrong>：根据用户的年龄、收入、消费行为等特征，将相似的用户归为一类，然后针对每一类人群制定差异化的营销方案。\u003C\u002Fp>\n\u003Cp>本文将基于 Mall Customers 商场客户数据集，使用 K-Means 聚类算法完成一次完整的用户分群实战：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>聚类前的特征选择：为什么性别不参与聚类，而是留做事后画像\u003C\u002Fli>\n \u003Cli>数据标准化对 K-Means 欧氏距离的影响\u003C\u002Fli>\n \u003Cli>肘部法则（Elbow Method）与轮廓系数（Silhouette Score）两种选 k 方法\u003C\u002Fli>\n \u003Cli>\u003Cstrong>指标最优 != 业务可解释最优\u003C\u002Fstrong>：k=5 与 k=6 的权衡思考\u003C\u002Fli>\n \u003Cli>聚类中心反标准化，还原真实业务数值\u003C\u002Fli>\n \u003Cli>五个用户群体的详细画像与性别构成分析\u003C\u002Fli>\n \u003Cli>二维散点图可视化分群结果\u003C\u002Fli>\n \u003Cli>针对每类人群的差异化营销策略\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>数据集包含 200 位商场会员的信息，特征包括性别、年龄、年收入和消费得分。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>二、数据探索\u003C\u002Fh2>\n\u003Ch3>2.1 导入数据与基本信息\u003C\u002Fh3>\n\u003Cpre>\u003Ccode>import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# 设置 pandas 显示优化\npd.set_option('display.max_columns', None)\npd.set_option('display.width', 2000)\npd.set_option('display.max_colwidth', 35)\n\n# 设置显示中文\nplt.rcParams['font.sans-serif'] = ['SimHei']\nplt.rcParams['axes.unicode_minus'] = False\n\n# 读取数据\ndf = pd.read_csv(\"7.Mall_Customers.csv\")\n\n# 初步观察数据情况\nprint(df.columns.tolist())\nprint(f\"数据形状：{df.shape}\")\nprint(f\"\\n前五行数据：\\n{df.head()}\")\nprint(f\"\\n数据类型与缺失值情况：\")\nprint(df.info())\nprint(df.describe())\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>['CustomerID', 'Gender', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)']\n数据形状：(200, 5)\n\n前五行数据：\n   CustomerID  Gender  Age  Annual Income (k$)  Spending Score (1-100)\n0           1    Male   19                  15                      39\n1           2    Male   21                  15                      81\n2           3  Female   20                  16                       6\n3           4  Female   23                  16                      77\n4           5  Female   31                  17                      40\n\n数据类型与缺失值情况：\n&lt;class 'pandas.core.frame.DataFrame'&gt;\nRangeIndex: 200 entries, 0 to 199\nData columns (total 5 columns):\n #   Column                  Non-Null Count  Dtype\n---  ------                  --------------  -----\n 0   CustomerID              200 non-null    int64\n 1   Gender                  200 non-null    object\n 2   Age                     200 non-null    int64\n 3   Annual Income (k$)      200 non-null    int64\n 4   Spending Score (1-100)  200 non-null    int64\ndtypes: int64(4), object(1)\nmemory usage: 7.9+ KB\nNone\n       CustomerID         Age  Annual Income (k$)  Spending Score (1-100)\ncount  200.000000  200.000000          200.000000              200.000000\nmean   100.500000   38.850000           60.560000               50.200000\nstd     57.879185   13.969007           26.264721               25.823522\nmin      1.000000   18.000000           15.000000                1.000000\n25%     50.750000   28.750000           41.500000               34.750000\n50%    100.500000   36.000000           61.500000               50.000000\n75%    150.250000   49.000000           78.000000               73.000000\nmax    200.000000   70.000000          137.000000               99.000000\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>数据共 200 条、5 个特征，无缺失值。其中 \u003Ccode>CustomerID\u003C\u002Fcode> 是唯一标识，没有实际业务意义，需要去除。年龄（1870）、年收入（15137k$）、消费得分（1~99）三个数值特征的量纲差异很大，K-Means 基于欧氏距离，要做标准化处理。\u003C\u002Fp>\n\u003Ch3>2.2 特征选择：性别要不要加入聚类？\u003C\u002Fh3>\n\u003Cp>这是一个值得讨论的问题：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>利\u003C\u002Fstrong>：性别可能带来消费行为差异，理论上可以提供额外信息。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>弊\u003C\u002Fstrong>： \n  \u003Col>\n   \u003Cli>\u003Ccode>Gender\u003C\u002Fcode> 是分类特征（男\u002F女），K-Means 依靠欧氏距离，直接塞字符串不行，必须做独热编码；\u003C\u002Fli>\n   \u003Cli>这个数据集的业务经典做法是\u003Cstrong>不把性别放进聚类特征\u003C\u002Fstrong>。原因：年龄、年收入、消费得分是连续的行为核心指标；性别属于人口属性标签，可以在聚类完成之后，\u003Cstrong>事后用来做画像统计，而不是参与距离计算\u003C\u002Fstrong>。\u003C\u002Fli>\n  \u003C\u002Fol>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>因此最终选择 \u003Ccode>Age\u003C\u002Fcode>、\u003Ccode>Annual Income (k$)\u003C\u002Fcode>、\u003Ccode>Spending Score (1-100)\u003C\u002Fcode> 三个连续特征作为聚类输入。\u003C\u002Fp>\n\u003Ch3>2.3 性别分布\u003C\u002Fh3>\n\u003Cp>先看一下整体性别分布，留作后续画像对比。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 查看性别分布 \"\"\"\ndf.value_counts(\"Gender\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>Gender\nFemale    112\nMale       88\nName: count, dtype: int64\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>女性（56%）略多于男性（44%），整体相差不大。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>三、数据标准化\u003C\u002Fh2>\n\u003Cp>K-Means 的核心是欧氏距离，如果不做标准化，年收入（几十到上百）的数值会完全\"淹没\"年龄和消费得分的差异，导致聚类结果被收入主导。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 标准化 \"\"\"\nfrom sklearn.preprocessing import StandardScaler\n\nfeature_cols = [\"Age\", \"Annual Income (k$)\", \"Spending Score (1-100)\"]\nX_raw = df[feature_cols].copy()\n\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X_raw)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>标准化后三个特征均值为 0、标准差为 1，在距离计算中权重平等。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>四、确定最佳聚类数 k\u003C\u002Fh2>\n\u003Cp>K-Means 需要预先指定聚类数量 k，这里用两种方法来辅助决策。\u003C\u002Fp>\n\u003Ch3>4.1 肘部法则\u003C\u002Fh3>\n\u003Cp>肘部法则的核心思想：随着 k 增大，簇内平方和（inertia，即惯性）会持续下降，但下降速度会在某个 k 值后明显变缓，形成一个\"肘部\"拐点。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 肘部法则，遍历 k, 计算 inertia 惯性 \"\"\"\nfrom sklearn.cluster import KMeans\n\ninertia_list = []\nk_range = range(2, 11)\nfor k in k_range:\n    kmeans = KMeans(n_clusters=k, random_state=42, n_init=\"auto\")\n    kmeans.fit(X_scaled)\n    inertia_list.append(kmeans.inertia_)\n\n# 绘制肘部图\nplt.figure(figsize=(8, 5))\nplt.plot(k_range, inertia_list, marker=\"o\")\nplt.xlabel(\"聚类数量 k\")\nplt.ylabel(\"惯性 inertia(簇内平方和)\")\nplt.title(\"K-Means肘部法则\")\nplt.grid(1)\nplt.show()\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fi1.wp.com\u002Fimg2024.cnblogs.com\u002Fblog\u002F3775135\u002F202609\u002F3775135-20260908154332888-1132576367.png?w=720&amp;quality=65&amp;strip=all\" alt=\"7\">\u003C\u002Fp>\n\u003Cp>从肘部图来看，k=4 或 k=5 附近出现了明显的拐点，但肘部法则的判断有一定主观性，需要结合轮廓系数进一步确认。\u003C\u002Fp>\n\u003Ch3>4.2 轮廓系数\u003C\u002Fh3>\n\u003Cp>轮廓系数衡量的是：一个样本与自己所在簇的相似度（凝聚度），与其他簇的相似度（分离度）之间的平衡。取值范围 [-1, 1]，越接近 1 说明聚类效果越好。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 计算每个 k 对应的轮廓系数 \"\"\"\nfrom sklearn.metrics import silhouette_score\n\nsil_score_list = []\nfor k in k_range:\n    kmeans = KMeans(n_clusters=k, random_state=42, n_init=\"auto\")\n    labels = kmeans.fit_predict(X_scaled)\n    sil = silhouette_score(X_scaled, labels)\n    sil_score_list.append(sil)\n\nplt.figure(figsize=(8, 5))\nplt.plot(k_range, sil_score_list, marker=\"o\", color=\"orange\")\nplt.xlabel(\"聚类数量 k\")\nplt.ylabel(\"轮廓系数 silhouette score\")\nplt.title(\"不同 k 的轮廓系数\")\nplt.grid(1)\nplt.show()\n\nfor k, score in zip(k_range, sil_score_list):\n    print(f\"k = {k}, 轮廓系数 = {score:.4f}\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fi1.wp.com\u002Fimg2024.cnblogs.com\u002Fblog\u002F3775135\u002F202609\u002F3775135-20260908154523373-655912177.png?w=720&amp;quality=65&amp;strip=all\" alt=\"7\">\u003C\u002Fp>\n\u003Cpre>\u003Ccode>k = 2, 轮廓系数 = 0.3355\nk = 3, 轮廓系数 = 0.3579\nk = 4, 轮廓系数 = 0.4040\nk = 5, 轮廓系数 = 0.4085\nk = 6, 轮廓系数 = 0.4311\nk = 7, 轮廓系数 = 0.4101\nk = 8, 轮廓系数 = 0.3674\nk = 9, 轮廓系数 = 0.3744\nk = 10, 轮廓系数 = 0.3619\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>4.3 k=5 vs k=6：指标最优 ≠ 业务可解释最优\u003C\u002Fh3>\n\u003Cp>从轮廓系数看，\u003Cstrong>k=6 数值最高（0.4311）\u003C\u002Fstrong>，但这个数据集业界经典的业务分群是 \u003Cstrong>k=5\u003C\u002Fstrong>。这里体现了无监督学习的关键点：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>k=6\u003C\u002Fstrong>：数学指标最好，但有可能其中某一类人群样本量很小，业务含义模糊；\u003C\u002Fli>\n \u003Cli>\u003Cstrong>k=5\u003C\u002Fstrong>：轮廓系数 0.4085 略低于 6，但 5 个客户群体每一类都有清晰的商业画像，商场运营可以直接落地做营销。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>综合考虑业务可解释性和营销落地成本，最终选择 \u003Cstrong>k=5\u003C\u002Fstrong> 作为聚类方案（后面会有 k=6 的分类情况说明为什么不用 6）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>五、k=5 K-Means 聚类\u003C\u002Fh2>\n\u003Ch3>5.1 训练最终模型并打标签\u003C\u002Fh3>\n\u003Cpre>\u003Ccode>\"\"\" 训练 k=5 最终 K-Means 模型，给原始 df 打上簇标签 \"\"\"\nkmeans_final = KMeans(n_clusters=5, random_state=42, n_init=\"auto\")\ncluster_labels = kmeans_final.fit_predict(X_scaled)\n\ndf[\"cluster\"] = cluster_labels  # 把聚类标签追加回原始 DataFrame, 方便后续画像分析\nprint(\"每个簇样本数量:\")\nprint(df[\"cluster\"].value_counts().sort_index())\n\n# 获取聚类中心点（标准化后的）\ncenters_scaled = kmeans_final.cluster_centers_\n\n# 把中心点 反标准化，还原回真实业务数值（年龄、年收入、消费分数）\ncenters_origin = scaler.inverse_transform(centers_scaled)\ncenters_df = pd.DataFrame(centers_origin, columns=feature_cols)\nprint(\"\\n各簇中心真实业务数值:\")\nprint(centers_df.round(2))\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>每个簇样本数量:\ncluster\n0    58\n1    40\n2    26\n3    45\n4    31\nName: count, dtype: int64\n\n各簇中心真实业务数值:\n     Age  Annual Income (k$)  Spending Score (1-100)\n0  55.28               47.62                   41.71\n1  32.88               86.10                   81.53\n2  25.77               26.12                   74.85\n3  26.73               54.31                   40.91\n4  44.39               89.77                   18.48\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>从簇中心可以看出一个关键洞察：\u003Cstrong>消费行为不完全由收入和年龄决定，消费意愿是一个独立维度\u003C\u002Fstrong>。比如簇 2 收入最低（26k\u003Cspan>\\(）但消费得分高达 74.85，而簇 4 收入最高（89.77k\\)\u003C\u002Fspan>）消费得分却只有 18.48——这正是聚类挖掘出来的有价值信息。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>六、用户画像分析\u003C\u002Fh2>\n\u003Ch3>6.1 各簇详细统计\u003C\u002Fh3>\n\u003Cp>遍历每个簇，查看年龄、消费得分、年收入的详细分布。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 遍历每个簇，查看【年龄-消费得分】统计 \"\"\"\nfor c_id in sorted(df[\"cluster\"].unique()):\n    sub = df[df[\"cluster\"] == c_id]\n    print(f\"\\n======== 簇 {c_id} 样本量：{len(sub)} ========\")\n    print(f\"年龄均值: {sub['Age'].mean():.2f}, min:{sub['Age'].min()}, max:{sub['Age'].max()}\")\n    print(f\"消费得分均值: {sub['Spending Score (1-100)'].mean():.2f}, min:{sub['Spending Score (1-100)'].min()}, max:{sub['Spending Score (1-100)'].max()}\")\n    print(f\"年收入均值: {sub['Annual Income (k$)'].mean():.2f}\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>======== 簇 0 样本量：58 ========\n年龄均值: 55.28, min:40, max:70\n消费得分均值: 41.71, min:3, max:60\n年收入均值: 47.62\n\n======== 簇 1 样本量：40 ========\n年龄均值: 32.88, min:27, max:40\n消费得分均值: 81.53, min:58, max:97\n年收入均值: 86.10\n\n======== 簇 2 样本量：26 ========\n年龄均值: 25.77, min:18, max:35\n消费得分均值: 74.85, min:39, max:99\n年收入均值: 26.12\n\n======== 簇 3 样本量：45 ========\n年龄均值: 26.73, min:18, max:40\n消费得分均值: 40.91, min:5, max:60\n年收入均值: 54.31\n\n======== 簇 4 样本量：31 ========\n年龄均值: 44.39, min:32, max:59\n消费得分均值: 18.48, min:1, max:39\n年收入均值: 89.77\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>6.2 各簇性别分布\u003C\u002Fh3>\n\u003Cp>聚类时没有使用性别特征，现在事后统计每个簇的男女比例，丰富画像维度。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 统计每个簇的男女比例 \"\"\"\nprint(\"每个簇性别分布\")\ngender_cluster = pd.crosstab(df[\"cluster\"], df[\"Gender\"], normalize=\"index\")\ngender_cluster.round(3)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>每个簇性别分布\nGender   Female   Male\ncluster\n0         0.569  0.431\n1         0.550  0.450\n2         0.577  0.423\n3         0.600  0.400\n4         0.484  0.516\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>前四个簇都是女性略多，而簇 4（高收入节俭人群）男性比例（51.6%）略高于女性，这也是一个有趣的业务发现。\u003C\u002Fp>\n\u003Ch3>6.3 二维散点图可视化\u003C\u002Fh3>\n\u003Cp>分别绘制\"年龄-消费得分\"和\"年收入-消费得分\"两个维度的散点图，直观展示分群效果。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 绘制二维散点图，区分人群 \"\"\"\nplt.figure(figsize=(16, 6))\n\n# 图1：年龄-消费得分\nplt.subplot(1, 2, 1)\nscatter1 = plt.scatter(\n    df[\"Age\"],                       # x\n    df[\"Spending Score (1-100)\"],    # y\n    c=df[\"cluster\"],                 # 按聚类簇编号给点上色\n    cmap=\"tab10\",                    # 配色方案，tab10 自带 10 种\n    s=60,                            # 散点大小\n    alpha=0.7                        # 透明度\n)\nplt.xlabel(\"Age\")\nplt.ylabel(\"Spending Score\")\nplt.title(\"年龄-消费得分分布\")\nplt.legend(*scatter1.legend_elements(), title=\"簇编号\")\nplt.grid(alpha=0.3)\n\n# 图2：年收入-消费得分\nplt.subplot(1, 2, 2)\nscatter2 = plt.scatter(\n    df[\"Annual Income (k$)\"],\n    df[\"Spending Score (1-100)\"],\n    c=df[\"cluster\"],\n    cmap=\"tab10\",\n    s=60,\n    alpha=0.7\n)\nplt.xlabel(\"Annual Income(k$)\")\nplt.ylabel(\"Spending Score\")\nplt.title(\"年收入-消费得分分布\")\nplt.legend(*scatter2.legend_elements(), title=\"簇编号\")\nplt.grid(alpha=0.3)\n\nplt.tight_layout()\nplt.show()\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fi1.wp.com\u002Fimg2024.cnblogs.com\u002Fblog\u002F3775135\u002F202609\u002F3775135-20260908154807837-128834176.png?w=720&amp;quality=65&amp;strip=all\" alt=\"7.客户分群\">\u003C\u002Fp>\n\u003Cp>从\"年收入-消费得分\"散点图可以非常清晰地看到五个簇的分布：左上角（高收入低消费）、右上角（高收入高消费）、左下角（低收入低消费）、右下角（低收入高消费）、以及中间区域，分群边界明确。\u003C\u002Fp>\n\u003Ch3>6.4 五群画像总结\u003C\u002Fh3>\n\u003Cp>将以上所有统计信息汇总成一张完整的画像表：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>簇号\u003C\u002Fth>\n   \u003Cth>样本数\u003C\u002Fth>\n   \u003Cth>平均年龄\u003C\u002Fth>\n   \u003Cth>平均年收入(k$)\u003C\u002Fth>\n   \u003Cth>平均消费得分\u003C\u002Fth>\n   \u003Cth>性别构成(女:男)\u003C\u002Fth>\n   \u003Cth>人群画像总结\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>0\u003C\u002Ftd>\n   \u003Ctd>58\u003C\u002Ftd>\n   \u003Ctd>55.28\u003C\u002Ftd>\n   \u003Ctd>47.62\u003C\u002Ftd>\n   \u003Ctd>41.71\u003C\u002Ftd>\n   \u003Ctd>56.9% : 43.1%\u003C\u002Ftd>\n   \u003Ctd>\u003Cstrong>中老年保守群体\u003C\u002Fstrong>，年龄集中 40-70 岁，中等收入，消费克制，不会疯狂消费。\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>1\u003C\u002Ftd>\n   \u003Ctd>40\u003C\u002Ftd>\n   \u003Ctd>32.88\u003C\u002Ftd>\n   \u003Ctd>86.10\u003C\u002Ftd>\n   \u003Ctd>81.53\u003C\u002Ftd>\n   \u003Ctd>55.0% : 45.0%\u003C\u002Ftd>\n   \u003Ctd>\u003Cstrong>核心高价值客户\u003C\u002Fstrong>，中青年，高收入、高消费意愿，消费得分普遍 58-97，是商场最重要客群。\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>2\u003C\u002Ftd>\n   \u003Ctd>26\u003C\u002Ftd>\n   \u003Ctd>25.77\u003C\u002Ftd>\n   \u003Ctd>26.12\u003C\u002Ftd>\n   \u003Ctd>74.85\u003C\u002Ftd>\n   \u003Ctd>57.7% : 42.3%\u003C\u002Ftd>\n   \u003Ctd>\u003Cstrong>低收入高消费年轻人（月光族）\u003C\u002Fstrong>，年龄 18-35，收入不高，但愿意花钱享乐，消费欲望很强。\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>3\u003C\u002Ftd>\n   \u003Ctd>45\u003C\u002Ftd>\n   \u003Ctd>26.73\u003C\u002Ftd>\n   \u003Ctd>54.31\u003C\u002Ftd>\n   \u003Ctd>40.91\u003C\u002Ftd>\n   \u003Ctd>60.0% : 40.0%\u003C\u002Ftd>\n   \u003Ctd>\u003Cstrong>普通青年群体\u003C\u002Fstrong>，年轻、中等收入，消费理性克制，不会冲动大额消费。\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>4\u003C\u002Ftd>\n   \u003Ctd>31\u003C\u002Ftd>\n   \u003Ctd>44.39\u003C\u002Ftd>\n   \u003Ctd>89.77\u003C\u002Ftd>\n   \u003Ctd>18.48\u003C\u002Ftd>\n   \u003Ctd>48.4% : 51.6%\u003C\u002Ftd>\n   \u003Ctd>\u003Cstrong>高收入节俭人群\u003C\u002Fstrong>，中年，收入全场最高，但消费得分极低，有钱但是不愿意在商场消费。\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Chr>\n\u003Ch2>七、业务营销策略\u003C\u002Fh2>\n\u003Cp>针对五个不同群体，制定差异化的营销方案：\u003C\u002Fp>\n\u003Ch3>簇 0：中老年保守群体\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>策略\u003C\u002Fstrong>：性价比高、实用型商品，促销折扣，避免过度推销。\u003C\u002Fli>\n \u003Cli>中老年客户消费理性，更看重实用性和性价比，过度营销反而会引起反感。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>簇 1：高价值核心客户\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>策略\u003C\u002Fstrong>：VIP 会员、专属折扣、高端新品优先推送，维护留存，防止流失。\u003C\u002Fli>\n \u003Cli>这部分客户收入高、消费意愿强，是商场利润的核心来源，投入最多资源维护。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>簇 2：年轻月光低收入高消费\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>策略\u003C\u002Fstrong>：主推潮流、平价爆款，分期活动，刺激消费。\u003C\u002Fli>\n \u003Cli>虽然收入不高，但消费意愿强，对价格敏感但愿意为喜欢的东西花钱，适合用促销和分期撬动。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>簇 3：普通青年理性消费\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>策略\u003C\u002Fstrong>：推送刚需日用品，做常规满减活动。\u003C\u002Fli>\n \u003Cli>这部分群体消费克制但基数不小，适合用常规的满减、日用品促销维持稳定消费。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>簇 4：高收入极度节俭\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>策略\u003C\u002Fstrong>：挖掘潜在需求，推送高端刚需产品。\u003C\u002Fli>\n \u003Cli>普通折扣很难打动这部分人，他们有钱但消费意愿低，需要精准匹配其真实需求，而不是简单打折。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2>八、k=6 补充对比\u003C\u002Fh2>\n\u003Cp>为了验证之前的判断，跑一下 k=6 的情况，看看多出来的一簇到底是什么。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\"\"\" 跑一下 k=6 时的情况 \"\"\"\nkmeans6 = KMeans(n_clusters=6, random_state=42, n_init=\"auto\")\ndf[\"cluster_6\"] = kmeans6.fit_predict(X_scaled)\nprint(\"k=6 每个簇样本数量：\")\nprint(df[\"cluster_6\"].value_counts().sort_index())\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>k=6 每个簇样本数量：\ncluster_6\n0    45\n1    39\n2    25\n3    40\n4    30\n5    21\nName: count, dtype: int64\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode># 获取k=6聚类中心，还原原始业务数值\ncenters6_scaled = kmeans6.cluster_centers_\ncenters6_origin = scaler.inverse_transform(centers6_scaled)\ncenters6_df = pd.DataFrame(centers6_origin, columns=feature_cols)\nprint(\"k=6 各簇中心:\")\nprint(centers6_df.round(2))\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode>k=6 各簇中心:\n     Age  Annual Income (k$)  Spending Score (1-100)\n0  56.33               54.27                   49.07\n1  32.69               86.54                   82.13\n2  25.56               26.48                   76.24\n3  26.12               59.42                   44.45\n4  44.00               90.13                   17.93\n5  45.52               26.29                   19.38\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>k=6 多出来的簇 5 是\"中年低收入低消费\"人群（平均年龄 45.5 岁，年收入 26.29k$，消费得分 19.38）。在 k=5 时，这部分人被合并到了簇 0（中老年保守群体）中；k=6 时把他们单独拆分了出来。\u003C\u002Fp>\n\u003Ch3>业务层面权衡\u003C\u002Fh3>\n\u003Col>\n \u003Cli>k=6 数学上轮廓系数更高，新增簇 5 在统计上是独立一簇。\u003C\u002Fli>\n \u003Cli>但从业务实操角度：商场做营销，维护 6 套营销策略的成本比 5 套更高。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>k=6 轮廓系数指标最优，可以拆分出中年低收入低消费客群；但 k=5 分组更少，每一类画像简洁清晰，业务落地成本更低。综合选择 \u003Cstrong>k=5 作为最终聚类方案\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2>九、项目总结\u003C\u002Fh2>\n\u003Ch3>项目目标\u003C\u002Fh3>\n\u003Cp>基于商场客户数据集，使用 K‑Means 无监督聚类完成客户分群，挖掘不同人群画像，为商场营销提供依据。特征：\u003Ccode>Age\u003C\u002Fcode>年龄、\u003Ccode>Annual Income (k$)\u003C\u002Fcode>年收入、\u003Ccode>Spending Score (1‑100)\u003C\u002Fcode>消费得分；性别不参与聚类，用于事后画像统计。\u003C\u002Fp>\n\u003Ch3>数据预处理\u003C\u002Fh3>\n\u003Col>\n \u003Cli>无缺失值；\u003Ccode>CustomerID\u003C\u002Fcode>为标识符，丢弃不参与聚类；Gender 分类特征不进入距离计算。\u003C\u002Fli>\n \u003Cli>K‑Means 对特征量纲敏感，使用\u003Ccode>StandardScaler\u003C\u002Fcode>标准化全部连续特征。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>确定聚类数量 k\u003C\u002Fh3>\n\u003Col>\n \u003Cli>\u003Cstrong>肘部法则\u003C\u002Fstrong>：观察惯性（簇内平方和），拐点区间 k=4‑6。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>轮廓系数\u003C\u002Fstrong>：取值 [-1,1]，数值越高聚类质量越好；k=6 轮廓系数 0.4311 最高，k=5 为 0.4085 略低。\u003C\u002Fli>\n \u003Cli>对比实验 k=5 与 k=6： \n  \u003Cul>\n   \u003Cli>k=6：指标最优，样本无极端小簇，拆分出中年低收入低消费人群；但需要维护 6 套业务策略。\u003C\u002Fli>\n   \u003Cli>k=5：指标略低，5 类客户画像清晰易懂，营销落地简单，本项目最终选择 k=5。\u003C\u002Fli>\n  \u003C\u002Ful>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cblockquote>\n \u003Cp>得出：无监督聚类不能单纯依靠指标选择 k，必须结合业务可解释性。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3>k=5 客户画像 &amp; 营销策略\u003C\u002Fh3>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>簇号\u003C\u002Fth>\n   \u003Cth>人群画像\u003C\u002Fth>\n   \u003Cth>核心特征\u003C\u002Fth>\n   \u003Cth>营销建议\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>0\u003C\u002Ftd>\n   \u003Ctd>中老年保守群体\u003C\u002Ftd>\n   \u003Ctd>年龄偏大，中等收入，消费克制\u003C\u002Ftd>\n   \u003Ctd>主推实用高性价比商品，折扣促销\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>1\u003C\u002Ftd>\n   \u003Ctd>核心高价值客户\u003C\u002Ftd>\n   \u003Ctd>中青年，高收入、高消费得分\u003C\u002Ftd>\n   \u003Ctd>VIP 服务，高端新品优先，重点留存\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>2\u003C\u002Ftd>\n   \u003Ctd>月光年轻群体\u003C\u002Ftd>\n   \u003Ctd>年龄小，收入低，但消费意愿极强\u003C\u002Ftd>\n   \u003Ctd>潮流平价商品，分期活动，刺激冲动消费\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>3\u003C\u002Ftd>\n   \u003Ctd>普通理性青年\u003C\u002Ftd>\n   \u003Ctd>年轻中等收入，消费克制\u003C\u002Ftd>\n   \u003Ctd>刚需日用品，常规满减活动\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>4\u003C\u002Ftd>\n   \u003Ctd>高收入节俭人群\u003C\u002Ftd>\n   \u003Ctd>收入很高，但消费意愿极低\u003C\u002Ftd>\n   \u003Ctd>不适合普通折扣，挖掘高端刚需精准推送\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2>十、结语\u003C\u002Fh2>\n\u003Cp>由于我做的时候使用的是 Jupyter Notebook，所以代码都是一段一段的看起来可能不方便，还请见谅！此外如果聪明的你发现了代码和表述有错误或者有更好的提议，可以在评论区写下你的建议，谢谢(●'◡'●)！数据下载：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FShu-Jvan\u002Fmy-notes\u002Fblob\u002Fmain\u002FMachine%20Learning\u002F7.%E7%94%A8%E6%88%B7%E5%88%86%E7%BE%A4\u002F7.Mall_Customers.csv\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Mall_Customers\u003C\u002Fa>\u003C\u002Fli>\n \u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FShu-Jvan\u002Fmy-notes\u002Fblob\u002Fmain\u002FMachine%20Learning\u002F7.%E7%94%A8%E6%88%B7%E5%88%86%E7%BE%A4\u002F7.%E7%94%A8%E6%88%B7%E5%88%86%E7%BE%A4.ipynb\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">客户分群.ipynb\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>","商场用户分群： 一、前言 在商场运营中，面对成千上万的顾客，如果用同一套营销策略对待所有人，效果往往事倍功半。高收入的中年人和月光族的年轻人等等群体，消费习惯和偏好截然不同——这就需要用户分群（Customer Segmentation）：根据用户的年龄、收入、消费行为等特征，将相似的用户归为一类，然后针对每一类人群制定差异化的营销方案。 本文将基于 Mall Customers 商场客户数据集，使用 K-Means 聚类算法完成一次完整的用户分群实战： 聚类前的特征选择：为什么性别不参与聚类，而是留做事后画像 数据标准化对 K-Means 欧氏距离的影响 肘部法则（Elbow Method）与轮廓系数（Silhouette Score）两种选 k 方法 指标最优 != 业务可解释最优：k=5 与 k=6 的权衡思考 聚类中心反标准化，还原真实业务数值 五个用户群体的详细画像与性别构成分析 二维散点图可视化分群结果 针对每类人群的差异化营销策略 数据集包含 200 位商场会员的信息，特征包括性别、年龄、年收入和消费得分。 二、数据探索 2.1 导入数据与基本信息 import numpy as np import pandas as pd import matplotlib.pyplot as plt # 设置 pandas 显示优化 pd.set_option('display.max_columns', None) pd.set_option('display.width', 2000) pd.set_option('display.max_colwidth', 35) # 设置显示中文 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 读取数据 df = pd.read_csv(\"7.Mall_Customers.csv\") # 初步观察数据情况 print(df.columns.tolist()) print(f\"数据形状：{df.shape}\") print(f\"\\n前五行数据：\\n{df.head()}\") print(f\"\\n数据类型与缺失值情况：\") print(df.info()) print(df.describe()) ['CustomerID', 'Gender', 'Age', 'Annual Income (k$)', 'Spending Score (1-100)'] 数据形状：(200, 5) 前五行数据： CustomerID Gender Age Annual Income (k$) Spending Score (1-100) 0 1 Male 19 15 39 1 2 Male 21 15 81 2 3 Female 20 16 6 3 4 Female 23 16 77 4 5 Female 31 17 40 数据类型与缺失值情况： \u003Cclass 'pandas.core.frame.DataFrame'> RangeIndex: 200 entries, 0 to 199 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CustomerID 200 non-null int64 1 Gender 200 non-null object 2 Age 200 non-null int64 3 Annual Income (k$) 200 non-null int64 4 Spending Score (1-100) 200 non-null int64 dtypes: int64(4), object(1) memory usage: 7.9+ KB None CustomerID Age Annual Income (k$) Spending Score (1-100) count 200.000000 200.000000 200.000000 200.000000 mean 100.500000 38.850000 60.560000 50.200000 std 57.879185 13.969007 26.264721 25.823522 min 1.000000 18.000000 15.000000 1.000000 25% 50.750000 28.750000 41.500000 34.750000 50% 100.500000 36.000000 61.500000 50.000000 75% 150.250000 49.000000 78.000000 73.000000 max 200.000000 70.000000 137.000000 99.000000 数据共 200 条、5 个特征，无缺失值。其中 CustomerID 是唯一标识，没有实际业务意义，需要去除。年龄（1870）、年收入（15137k$）、消费得分（1~99）三个数值特征的量纲差异很大，K-Means 基于欧氏距离，要做标准化处理。 2.2 特征选择：性别要不要加入聚类？ 这是一个值得讨论的问题： 利：性别可能带来消费行为差异，理论上可以提供额外信息。 弊： Gender 是分类特征（男\u002F女），K-Means 依靠欧氏距离，直接塞字符串不行，必须做独热编码； 这个数据集的业务经典做法是不把性别放进聚类特征。原因：年龄、年收入、消费得分是连续的行为核心指标；性别属于人口属性标签，可以在聚类完成之后，事后用来做画像统计，而不是参与距离计算。 因此最终选择 Age、Annual Income (k$)、Spending Score (1-100) 三个连续特征作为聚类输入。 2.3 性别分布 先看一下整体性别分布，留作后续画像对比。 \"\"\" 查看性别分布 \"\"\" df.value_counts(\"Gender\") Gender Female 112 Male 88 Name: count, dtype: int64 女性（56%）略多于男性（44%），整体相差不大。 三、数据标准化 K-Means 的核心是欧氏距离，如果不做标准化，年收入（几十到上百）的数值会完全\"淹没\"年龄和消费得分的差异，导致聚类结果被收入主导。 \"\"\" 标准化 \"\"\" from sklearn.preprocessing import StandardScaler feature_cols = [\"Age\", \"Annual Income (k$)\", \"Spending Score (1-100)\"] X_raw = df[feature_cols].copy() scaler = StandardScaler() X_scaled = scaler.fit_transform(X_raw) 标准化后三个特征均值为 0、标准差为 1，在距离计算中权重平等。 四、确定最佳聚类数 k K-Means 需要预先指定聚类数量 k，这里用两种方法来辅助决策。 4.1 肘部法则 肘部法则的核心思想：随着 k 增大，簇内平方和（inertia，即惯性）会持续下降，但下降速度会在某个 k 值后明显变缓，形成一个\"肘部\"拐点。 \"\"\" 肘部法则，遍历 k, 计算 inertia 惯性 \"\"\" from sklearn.cluster import KMeans inertia_list = [] k_range = range(2, 11) for k in k_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=\"auto\") kmeans.fit(X_scaled) inertia_list.append(kmeans.inertia_) # 绘制肘部图 plt.figure(figsize=(8, 5)) plt.plot(k_range, inertia_list, marker=\"o\") plt.xlabel(\"聚类数量 k\") plt.ylabel(\"惯性 inertia(簇内平方和)\") plt.title(\"K-Means肘部法则\") plt.grid(1) plt.show() 从肘部图来看，k=4 或 k=5 附近出现了明显的拐点，但肘部法则的判断有一定主观性，需要结合轮廓系数进一步确认。 4.2 轮廓系数 轮廓系数衡量的是：一个样本与自己所在簇的相似度（凝聚度），与其他簇的相似度（分离度）之间的平衡。取值范围 [-1, 1]，越接近 1 说明聚类效果越好。 \"\"\" 计算每个 k 对应的轮廓系数 \"\"\" from sklearn.metrics import silhouette_score sil_score_list = [] for k in k_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=\"auto\") labels = kmeans.fit_predict(X_scaled) sil = silhouette_score(X_scaled, labels) sil_score_list.append(sil) plt.figure(figsize=(8, 5)) plt.plot(k_range, sil_score_list, marker=\"o\", color=\"orange\") plt.xlabel(\"聚类数量 k\") plt.ylabel(\"轮廓系数 silhouette score\") plt.title(\"不同 k 的轮廓系数\") plt.grid(1) plt.show() for k, score in zip(k_range, sil_score_list): print(f\"k = {k}, 轮廓系数 = {score:.4f}\") k = 2, 轮廓系数 = 0.3355 k = 3, 轮廓系数 = 0.3579 k = 4, 轮廓系数 = 0.4040 k = 5, 轮廓系数 = 0.4085 k = 6, 轮廓系数 = 0.4311 k = 7, 轮廓系数 = 0.4101 k = 8, 轮廓系数 = 0.3674 k = 9, 轮廓系数 = 0.3744 k = 10, 轮廓系数 = 0.3619 4.3 k=5 vs k=6：指标最优 ≠ 业务可解释最优 从轮廓系数看，k=6 数值最高（0.4311），但这个数据集业界经典的业务分群是 k=5。这里体现了无监督学习的关键点： k=6：数学指标最好，但有可能其中某一类人群样本量很小，业务含义模糊； k=5：轮廓系数 0.4085 略低于 6，但 5 个客户群体每一类都有清晰的商业画像，商场运营可以直接落地做营销。 综合考虑业务可解释性和营销落地成本，最终选择 k=5 作为聚类方案（后面会有 k=6 的分类情况说明为什么不用 6）。 五、k=5 K-Means 聚类 5.1 训练最终模型并打标签 \"\"\" 训练 k=5 最终 K-Means 模型，给原始 df 打上簇标签 \"\"\" kmeans_final = KMeans(n_clusters=5, random_state=42, n_init=\"auto\") cluster_labels = kmeans_final.fit_predict(X_scaled) df[\"cluster\"] = cluster_labels # 把聚类标签追加回原始 DataFrame, 方便后续画像分析 print(\"每个簇样本数量:\") print(df[\"cluster\"].value_counts().sort_index()) # 获取聚类中心点（标准化后的） centers_scaled = kmeans_final.cluster_centers_ # 把中心点 反标准化，还原回真实业务数值（年龄、年收入、消费分数） centers_origin = scaler.inverse_transform(centers_scaled) centers_df = pd.DataFrame(centers_origin, columns=feature_cols) print(\"\\n各簇中心真实业务数值:\") print(centers_df.round(2)) 每个簇样本数量: cluster 0 58 1 40 2 26 3 45 4 31 Name: count, dtype: int64 各簇中心真实业务数值: Age Annual Income (k$) Spending Score (1-100) 0 55.28 47.62 41.71 1 32.88 86.10 81.53 2 25.77 26.12 74.85 3 26.73 54.31 40.91 4 44.39 89.77 18.48 从簇中心可以看出一个关键洞察：消费行为不完全由收入和年龄决定，消费意愿是一个独立维度。比如簇 2 收入最低（26k\\(）但消费得分高达 74.85，而簇 4 收入最高（89.77k\\)）消费得分却只有 18.48——这正是聚类挖掘出来的有价值信息。 六、用户画像分析 6.1 各簇详细统计 遍历每个簇，查看年龄、消费得分、年收入的详细分布。 \"\"\" 遍历每个簇，查看【年龄-消费得分】统计 \"\"\" for c_id in sorted(df[\"cluster\"].unique()): sub = df[df[\"cluster\"] == c_id] print(f\"\\n======== 簇 {c_id} 样本量：{len(sub)} ========\") print(f\"年龄均值: {sub['Age'].mean():.2f}, min:{sub['Age'].min()}, max:{sub['Age'].max()}\") print(f\"消费得分均值: {sub['Spending Score (1-100)'].mean():.2f}, min:{sub['Spending Score (1-100)'].min()}, max:{sub['Spending Score (1-100)'].max()}\") print(f\"年收入均值: {sub['Annual Income (k$)'].mean():.2f}\") ======== 簇 0 样本量：58 ======== 年龄均值: 55.28, min:40, max:70 消费得分均值: 41.71, min:3, max:60 年收入均值: 47.62 ======== 簇 1 样本量：40 ======== 年龄均值: 32.88, min:27, max:40 消费得分均值: 81.53, min:58, max:97 年收入均值: 86.10 ======== 簇 2 样本量：26 ======== 年龄均值: 25.77, min:18, max:35 消费得分均值: 74.85, min:39, max:99 年收入均值: 26.12 ======== 簇 3 样本量：45 ======== 年龄均值: 26.73, min:18, max:40 消费得分均值: 40.91, min:5, max:60 年收入均值: 54.31 ======== 簇 4 样本量：31 ======== 年龄均值: 44.39, min:32, max:59 消费得分均值: 18.48, min:1, max:39 年收入均值: 89.77 6.2 各簇性别分布 聚类时没有使用性别特征，现在事后统计每个簇的男女比例，丰富画像维度。 \"\"\" 统计每个簇的男女比例 \"\"\" print(\"每个簇性别分布\") gender_cluster = pd.crosstab(df[\"cluster\"], df[\"Gender\"], normalize=\"index\") gender_cluster.round(3) 每个簇性别分布 Gender Female Male cluster 0 0.569 0.431 1 0.550 0.450 2 0.577 0.423 3 0.600 0.400 4 0.484 0.516 前四个簇都是女性略多，而簇 4（高收入节俭人群）男性比例（51.6%）略高于女性，这也是一个有趣的业务发现。 6.3 二维散点图可视化 分别绘制\"年龄-消费得分\"和\"年收入-消费得分\"两个维度的散点图，直观展示分群效果。 \"\"\" 绘制二维散点图，区分人群 \"\"\" plt.figure(figsize=(16, 6)) # 图1：年龄-消费得分 plt.subplot(1, 2, 1) scatter1 = plt.scatter( df[\"Age\"], # x df[\"Spending Score (1-100)\"], # y c=df[\"cluster\"], # 按聚类簇编号给点上色 cmap=\"tab10\", # 配色方案，tab10 自带 10 种 s=60, # 散点大小 alpha=0.7 # 透明度 ) plt.xlabel(\"Age\") plt.ylabel(\"Spending Score\") plt.title(\"年龄-消费得分分布\") plt.legend(*scatter1.legend_elements(), title=\"簇编号\") plt.grid(alpha=0.3) # 图2：年收入-消费得分 plt.subplot(1, 2, 2) scatter2 = plt.scatter( df[\"Annual Income (k$)\"], df[\"Spending Score (1-100)\"], c=df[\"cluster\"], cmap=\"tab10\", s=60, alpha=0.7 ) plt.xlabel(\"Annual Income(k$)\") plt.ylabel(\"Spending Score\") plt.title(\"年收入-消费得分分布\") plt.legend(*scatter2.legend_elements(), title=\"簇编号\") plt.grid(alpha=0.3) plt.tight_layout() plt.show() 从\"年收入-消费得分\"散点图可以非常清晰地看到五个簇的分布：左上角（高收入低消费）、右上角（高收入高消费）、左下角（低收入低消费）、右下角（低收入高消费）、以及中间区域，分群边界明确。 6.4 五群画像总结 将以上所有统计信息汇总成一张完整的画像表： 簇号 样本数 平均年龄 平均年收入(k$) 平均消费得分 性别构成(女:男) 人群画像总结 0 58 55.28 47.62 41.71 56.9% : 43.1% 中老年保守群体，年龄集中 40-70 岁，中等收入，消费克制，不会疯狂消费。 1 40 32.88 86.10 81.53 55.0% : 45.0% 核心高价值客户，中青年，高收入、高消费意愿，消费得分普遍 58-97，是商场最重要客群。 2 26 25.77 26.12 74.85 57.7% : 42.3% 低收入高消费年轻人（月光族），年龄 18-35，收入不高，但愿意花钱享乐，消费欲望很强。 3 45 26.73 54.31 40.91 60.0% : 40.0% 普通青年群体，年轻、中等收入，消费理性克制，不会冲动大额消费。 4 31 44.39 89.77 18.48 48.4% : 51.6% 高收入节俭人群，中年，收入全场最高，但消费得分极低，有钱但是不愿意在商场消费。 七、业务营销策略 针对五个不同群体，制定差异化的营销方案： 簇 0：中老年保守群体 策略：性价比高、实用型商品，促销折扣，避免过度推销。 中老年客户消费理性，更看重实用性和性价比，过度营销反而会引起反感。 簇 1：高价值核心客户 策略：VIP 会员、专属折扣、高端新品优先推送，维护留存，防止流失。 这部分客户收入高、消费意愿强，是商场利润的核心来源，投入最多资源维护。 簇 2：年轻月光低收入高消费 策略：主推潮流、平价爆款，分期活动，刺激消费。 虽然收入不高，但消费意愿强，对价格敏感但愿意为喜欢的东西花钱，适合用促销和分期撬动。 簇 3：普通青年理性消费 策略：推送刚需日用品，做常规满减活动。 这部分群体消费克制但基数不小，适合用常规的满减、日用品促销维持稳定消费。 簇 4：高收入极度节俭 策略：挖掘潜在需求，推送高端刚需产品。 普通折扣很难打动这部分人，他们有钱但消费意愿低，需要精准匹配其真实需求，而不是简单打折。 八、k=6 补充对比 为了验证之前的判断，跑一下 k=6 的情况，看看多出来的一簇到底是什么。 \"\"\" 跑一下 k=6 时的情况 \"\"\" kmeans6 = KMeans(n_clusters=6, random_state=42, n_init=\"auto\") df[\"cluster_6\"] = kmeans6.fit_predict(X_scaled) print(\"k=6 每个簇样本数量：\") print(df[\"cluster_6\"].value_counts().sort_index()) k=6 每个簇样本数量： cluster_6 0 45 1 39 2 25 3 40 4 30 5 21 Name: count, dtype: int64 # 获取k=6聚类中心，还原原始业务数值 centers6_scaled = kmeans6.cluster_centers_ centers6_origin = scaler.inverse_transform(centers6_scaled) centers6_df = pd.DataFrame(centers6_origin, columns=feature_cols) print(\"k=6 各簇中心:\") print(centers6_df.round(2)) k=6 各簇中心: Age Annual Income (k$) Spending Score (1-100) 0 56.33 54.27 49.07 1 32.69 86.54 82.13 2 25.56 26.48 76.24 3 26.12 59.42 44.45 4 44.00 90.13 17.93 5 45.52 26.29 19.38 k=6 多出来的簇 5 是\"中年低收入低消费\"人群（平均年龄 45.5 岁，年收入 26.29k$，消费得分 19.38）。在 k=5 时，这部分人被合并到了簇 0（中老年保守群体）中；k=6 时把他们单独拆分了出来。 业务层面权衡 k=6 数学上轮廓系数更高，新增簇 5 在统计上是独立一簇。 但从业务实操角度：商场做营销，维护 6 套营销策略的成本比 5 套更高。 k=6 轮廓系数指标最优，可以拆分出中年低收入低消费客群；但 k=5 分组更少，每一类画像简洁清晰，业务落地成本更低。综合选择 k=5 作为最终聚类方案。 九、项目总结 项目目标 基于商场客户数据集，使用 K‑Means 无监督聚类完成客户分群，挖掘不同人群画像，为商场营销提供依据。特征：Age年龄、Annual Income (k$)年收入、Spending Score (1‑100)消费得分；性别不参与聚类，用于事后画像统计。 数据预处理 无缺失值；CustomerID为标识符，丢弃不参与聚类；Gender 分类特征不进入距离计算。 K‑Means 对特征量纲敏感，使用StandardScaler标准化全部连续特征。 确定聚类数量 k 肘部法则：观察惯性（簇内平方和），拐点区间 k=4‑6。 轮廓系数：取值 [-1,1]，数值越高聚类质量越好；k=6 轮廓系数 0.4311 最高，k=5 为 0.4085 略低。 对比实验 k=5 与 k=6： k=6：指标最优，样本无极端小簇，拆分出中年低收入低消费人群；但需要维护 6 套业务策略。 k=5：指标略低，5 类客户画像清晰易懂，营销落地简单，本项目最终选择 k=5。 得出：无监督聚类不能单纯依靠指标选择 k，必须结合业务可解释性。 k=5 客户画像 & 营销策略 簇号 人群画像 核心特征 营销建议 0 中老年保守群体 年龄偏大，中等收入，消费克制 主推实用高性价比商品，折扣促销 1 核心高价值客户 中青年，高收入、高消费得分 VIP 服务，高端新品优先，重点留存 2 月光年轻群体 年龄小，收入低，但消费意愿极强 潮流平价商品，分期活动，刺激冲动消费 3 普通理性青年 年轻中等收入，消费克制 刚需日用品，常规满减活动 4 高收入节俭人群 收入很高，但消费意愿极低 不适合普通折扣，挖掘高端刚需精准推送 十、结语 由于我做的时候使用的是 Jupyter Notebook，所以代码都是一段一段的看起来可能不方便，还请见谅！此外如果聪明的你发现了代码和表述有错误或者有更好的提议，可以在评论区写下你的建议，谢谢(●'◡'●)！数据下载： Mall_Customers 客户分群.ipynb",9473,{"id":6,"kind":7,"title":11,"summary":13,"image":14,"href":16,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":40},"2026 · 人工智能","#2563eb","16 \u002F 10",[19],{"targetType":8,"targetId":9,"likedByMe":42,"likeCount":43,"commentCount":43,"contentLikeCount":43,"contentCommentCount":43,"sourceLikeCount":43,"sourceCommentCount":43},false,0,[45,52,59,66,73,80,86,93],{"id":46,"kind":7,"title":47,"summary":48,"image":49,"href":50,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":51},"NEWS_ARTICLE:930","基于 vLLM+Nginx 构建负载均衡推理集群","企业内部私有环境部署大模型推理集群时，很容易遇到流量调度混乱、节点负载失衡、会话上下文丢失、接口缺少鉴权防护等一系列问题，单 vLLM 推理节点难以支撑并发请求。本文基于 Ubuntu 22.04 系统环境，搭建 Nginx + vLLM-Semantic-Router + vLLM-Router","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1379525\u002F202609\u002F1379525-20260910165534385-2022408098.png","\u002Fnews\u002F930",[19],{"id":53,"kind":7,"title":54,"summary":55,"image":56,"href":57,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":58},"NEWS_ARTICLE:932","2026年AI编程工具大全，33个主流工具一次看懂","事情是这样的，前两天看到一张图，是某个社区官网的「支持的工具」清单，我数了数，整整31个AI编程工具。 两年前这份清单撑死5个，现在直接31个，而且我居然每一个都认识。。。 干脆整理成一篇，顺手把最近字节的TraeWork和豆包工作也补了进来，凑成33个。 今天给大家推荐一遍，每个工具说说它是干什么","https:\u002F\u002Fimage.kjdaohang.com\u002Fimg\u002F20260909210838518.png","\u002Fnews\u002F932",[19],{"id":60,"kind":7,"title":61,"summary":62,"image":63,"href":64,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":65},"NEWS_ARTICLE:935","[Agent Memory \u002F 强化学习] MemPO源码学习笔记 ---（1）--- 总体","[Agent Memory \u002F 强化学习] MemPO源码学习笔记 （1） 总体 目录[Agent Memory \u002F 强化学习] MemPO源码学习笔记 （1） 总体0x00 概要0x01 基础 &amp; 背景1.1 用RL训练记忆系统的要点1.2 主要难点1.3 主要思路1.4 RL训练方案1.","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1850883\u002F202609\u002F1850883-20260906190854637-986949885.jpg","\u002Fnews\u002F935",[19],{"id":67,"kind":7,"title":68,"summary":69,"image":70,"href":71,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":72},"NEWS_ARTICLE:940","LLama-Factory 实现大模型LoRA-SFT微调指南","LLaMA Factory 是一款开源、低代码、一站式大语言与多模态模型微调框架，用于降低大模型的微调落地门槛。框架兼容 Qwen、LLaMA、ChatGLM、LLaVA 等上百款主流开源模型，支持增量预训练、SFT 监督微调、DPO、KTO、ORPO 等多种训练对齐方案，原生集成 LoRA、QLo","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1379525\u002F202609\u002F1379525-20260909140111199-832484524.png","\u002Fnews\u002F940",[19],{"id":74,"kind":7,"title":75,"summary":76,"image":77,"href":78,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":79},"NEWS_ARTICLE:950","每天白嫖 WorkBuddy 100 积分，我让WorkBuddy自己领","有没有小伙伴跟我一样，每天都去白嫖 WorkBuddy 的 100 积分，每天都怕忘记领。 其实我早就开了会员，但还是会有积分焦虑，天天惦记着今天的积分领没领。后来我发现 WorkBuddy 可以自己领积分，今天把这个技能分享给小伙伴们。 Buddy 加油站每天签到领 100 积分，连续签满 7 天","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F2381533\u002F202609\u002F2381533-20260910080820041-1607850292.png","\u002Fnews\u002F950",[19],{"id":81,"kind":7,"title":82,"summary":83,"image":15,"href":84,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":85},"NEWS_ARTICLE:949","AI知识库，是捷径吗？","从信息化到数字化，再到当下的智能化，技术进步带来的效率提升，如果往好处想应该是：节省更多的时间和成本，用来做更多的事情。但从现实的角度看，节流比开源来得容易。","\u002Fnews\u002F949",[19],{"id":87,"kind":7,"title":88,"summary":89,"image":90,"href":91,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":92},"NEWS_ARTICLE:977","【OpenClaw具身硬件】ZeroClaw 源码阅读笔记（4）--- 代码执行","【OpenClaw具身硬件】ZeroClaw 源码阅读笔记（4） 代码执行 目录【OpenClaw具身硬件】ZeroClaw 源码阅读笔记（4） 代码执行0x00 概要0x01 代码合成1.1 核心思想1.2 业务逻辑具体应用场景能力体现1.3 实现细节生成种类Rust 代码Arduino CLI的","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1850883\u002F202608\u002F1850883-20260823212850242-1723558087.png","\u002Fnews\u002F977",[19],{"id":94,"kind":7,"title":95,"summary":96,"image":97,"href":98,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":99},"NEWS_ARTICLE:986","千问大模型完整RLHF全参数微调指南","大模型微调是实现模型领域定制的核心方案，本文承接《千问大模型二次 LoRA‑SFT 指令微调指南》部分内容，聚焦 Qwen3.5‑Base 纯文本基座全参数微调，完整复现 ChatGPT 风格 RLHF 对齐工程链路，覆盖数据预处理、SFT 监督微调、RM 奖励模型训练、PPO 强化学习、DPO 直","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1379525\u002F202609\u002F1379525-20260908150528296-1541712777.png","\u002Fnews\u002F986",[19]]