直方图实战:从 50 万条评论里看”评分是怎么分布的”
老板们拿到一份数据,第一反应不是平均值,而是 “这数据到底长啥样?”
- “5 万条评论的评分,是集中在 5 星还是分散的?“
- “有没有 1 星差评爆发的异常?“
- “2 个商品的评分分布有啥差异?“
直方图 是回答这些问题的最快方式。
一、什么是直方图?和柱状图啥区别?
老板们最容易混淆的两兄弟:
| 类型 |
数据类型 |
X 轴 |
Y 轴 |
适合 |
| 柱状图 |
离散类别 |
商品名(类别) |
销量(数值) |
类目对比(哪个卖得多) |
| 直方图 |
连续数值 |
区间(0-10, 10-20) |
频数(个数) |
数据分布(分布形状) |
核心区别:柱状图的 X 轴是离散的(苹果/华为/小米),直方图的 X 轴是连续的(0-10 分一档,10-20 分一档)。
直方图解决 3 大问题:
- 数据分布形状(正态?偏态?双峰?)
- 数据集中位置(众数在哪个区间)
- 异常点检测(远离主体的孤立柱)
二、最常见的 4 大直方图应用
应用 1:评分分布
1 2 3 4 5
| 1星: ▍ 200条 2星: ▍▍ 800条 3星: ▍▍▍▍▍▍▍ 5000条 4星: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 12000条 5星: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 20000条
|
老板一眼看出:5 星最多,典型好评偏态分布。
应用 2:价格分布(看定价合理性)
1 2 3 4 5
| 0-50元: ▍▍▍▍▍ 1000条 50-100元: ▍▍▍▍▍▍▍▍▍▍▍ 2000条 100-200元: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 5000条 200-500元: ▍▍▍▍▍▍▍▍▍▍ 2000条 500-1000元: ▍▍▍ 500条
|
老板一眼看出:主销 100-200 元 → 定价/选品都对准中端。
应用 3:用户活跃度分布
1 2 3 4 5
| 0次: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 3000人 1-5次: ▍▍▍▍▍▍▍▍▍▍▍▍ 1500人 5-20次: ▍▍▍▍▍ 600人 20-50次: ▍▍ 200人 50+: ▍ 50人
|
老板一眼看出:80% 用户只来 1 次 → 留存是大问题。
应用 4:字数分布(文本分析预处理)
1 2 3 4
| 0-10字: ▍▍▍▍▍ 100条 10-50字: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 1000条 50-200字: ▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍▍ 5000条 200字+: ▍▍▍ 200条
|
老板一眼看出:大量 50-200 字评论 → 适合做主题分析。
三、Python 5 行核心代码
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
| import pandas as pd from pyecharts import options as opts from pyecharts.charts import Bar
df = pd.read_csv('ratings.csv') ratings = df['rating'].dropna().tolist()
bins = [1, 2, 3, 4, 5, 6] labels = ['1星', '2星', '3星', '4星', '5星'] counts = pd.cut(ratings, bins=bins, labels=labels, right=False).value_counts().sort_index()
x_data = labels y_data = counts.tolist()
Bar(init_opts=opts.InitOpts(width='1000px', height='500px')) .add_xaxis(x_data) .add_yaxis('评论数', y_data, color='#00d4ff') .set_global_opts( title_opts=opts.TitleOpts(title='评分分布直方图'), xaxis_opts=opts.AxisOpts(name='评分', type_='category'), yaxis_opts=opts.AxisOpts(name='评论数'), ) .render('histogram.html')
|
四、进阶:自动分箱 + 异常点检测
1. 自动分箱(数据范围未知时)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import numpy as np
data = df['rating'].dropna().values
bins_equal_width = np.linspace(data.min(), data.max(), 11)
bins_equal_freq = np.quantile(data, np.linspace(0, 1, 11))
n = len(data) n_bins = int(np.log2(n)) + 1 bins_sturges = np.histogram_bin_edges(data, bins=n_bins)
counts, edges = np.histogram(data, bins=bins_equal_freq)
|
2. 异常点检测(IQR 法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def detect_outliers_iqr(data): """基于 IQR 的异常点检测""" q1 = np.percentile(data, 25) q3 = np.percentile(data, 75) iqr = q3 - q1 lower = q1 - 1.5 * iqr upper = q3 + 1.5 * iqr outliers = data[(data < lower) | (data > upper)] return outliers, (lower, upper)
ratings_clean = df['rating'].dropna().values outliers, bounds = detect_outliers_iqr(ratings_clean) print(f'异常值: {outliers}') print(f'正常范围: [{bounds[0]:.2f}, {bounds[1]:.2f}]')
|
案例:50 万条评论评分,发现 0.3% 评分为 0(刷单嫌疑)→ 剔除。
3. 多列对比(2 个商品)
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
| from pyecharts import options as opts from pyecharts.charts import Bar
df_a = pd.read_csv('product_a_ratings.csv')['rating'] df_b = pd.read_csv('product_b_ratings.csv')['rating']
def pct_dist(series): counts = series.value_counts().sort_index() return (counts / counts.sum() * 100).round(2).tolist()
x_data = ['1星', '2星', '3星', '4星', '5星'] y_a = pct_dist(df_a) y_b = pct_dist(df_b)
Bar() .add_xaxis(x_data) .add_yaxis('商品 A', y_a, color='#00d4ff') .add_yaxis('商品 B', y_b, color='#ff6b6b') .set_global_opts( title_opts=opts.TitleOpts(title='商品 A vs B 评分分布(%)'), xaxis_opts=opts.AxisOpts(type_='category'), yaxis_opts=opts.AxisOpts(name='占比(%)'), ) .render('compare.html')
|
结果:
1 2 3
| 1星 2星 3星 4星 5星 A: 5% 8% 15% 30% 42% B: 2% 5% 10% 33% 50%
|
老板一眼看出:B 商品好评率(83%)>A 商品(72%)。
五、直方图 + 核密度估计(KDE)
直方图的最大问题:分档方式决定形状。KDE 曲线连续平滑,更准:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import seaborn as sns import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
ax1.hist(ratings, bins=20, color='#00d4ff', edgecolor='black') ax1.set_title('直方图') ax1.set_xlabel('评分') ax1.set_ylabel('频数')
sns.kdeplot(ratings, ax=ax2, fill=True, color='#ff6b6b') ax2.set_title('KDE 密度图') ax2.set_xlabel('评分') ax2.set_ylabel('密度')
plt.tight_layout() plt.savefig('hist_kde.png', dpi=150)
|
对比:
- 直方图:看频数 / 绝对值
- KDE:看密度 / 形状
六、踩坑大全
1. 分档太多/太少
- 太少(5 档):看大趋势,丢细节
- 太多(50 档):噪音多,看不清主体
- 推荐:10-20 档
2. 边界值被错分
pd.cut 默认 right=True(右闭左开)
[1,2,3,4,5,6] + right=True → [1,2) [2,3) [3,4) [4,5) [5,6)(5 星 = 5.0)
- 评分 5.0 = 5 星,评分 4.99 = 4 星,符合直觉
3. Y 轴量级差太大
- 双 Y 轴:一个柱状(频数)+ 一条线(累计%)
- 或用对数 Y 轴:
yaxis_opts=opts.AxisOpts(type_='log')
4. 多列对比时颜色撞色
- 推荐 2-3 种:青(#00d4ff) / 红(#ff6b6b) / 黄(#ffd93d)
5. 数据为空 / NaN
- 必须先
dropna(),否则 pandas 分箱报错
七、软件功能
我们的 数据分析直方图软件 ¥9.80(全店最便宜):
| 功能 |
详情 |
| 输入 |
Excel / CSV / TXT,单列或多列 |
| 分箱 |
等宽 / 等频 / Sturges / 自定义边界 |
| 对比 |
2-5 列同图对比(自动归一化 %) |
| 异常检测 |
IQR / 3σ / 自定义阈值,自动标红 |
| KDE |
一键叠加密度曲线 |
| 输出 |
HTML(可交互) + PNG(4K) + 统计表 |
| 平台 |
Windows / Mac(Apple + Intel) |
为什么这么便宜(¥9.80)? 因为它是所有分析软件的基础——几乎每个分析报告里都有直方图。老板买回去当工具用,顺手就买了我们词频、LDA、情感。
八、典型用户场景
① 运营分析:评分分布、销量分布、用户活跃度分布
② 数据预处理:发现异常值 / 缺失值,定清洗策略
③ 学术研究:实验数据分布、问卷数据分布
④ 财务分析:工资分布、成本分布、毛利分布
⑤ 文本分析预处理:评论字数分布、关键词频数分布
九、立即试用
| 软件 |
售价 |
适用场景 |
| 数据分析直方图软件 |
¥9.80 |
数据分布 + 异常检测 |
| 词频统计软件 |
¥26.80 |
文本频数 |
| 情感分析软件 |
¥29.80 |
评论情感 |
| 一站式文本分析套餐 |
¥68.00 |
入门组合 |
本文由 小朵科技工作室 原创,专注 Python 数据可视化 6 年。