做电商数据分析这个事,说起来挺有意思。很多人一开始拿到一份几万行的销售订单表,第一反应是“我该用什么模型、要不要上机器学习”,结果折腾半天连数据长什么样都没看清楚。我见过太多项目卡在第一步,不是工具不够高级,而是连“这个月卖了多少、哪个商品拖后腿”这种基础问题都回答不清楚。所以我这篇实战笔记,想带你把一份典型的电商销售数据从导入、清洗、指标计算到可视化完整走一遍,用的就是Python,核心库就是pandas、matplotlib、seaborn这类日常工具。你要是有一定Python基础但没正经做过业务分析,或者刚转行数据分析想找个能落地的练手项目,这份实操记录值得你跟着敲一遍。
先说清楚这个项目能解决什么问题。电商销售数据本身是“脏”的:日期格式不统一、金额字段有负数、商品类目填错、用户ID有重复,这些不是教科书里的假设,而是真实业务里每天都在发生的。整个分析过程我会带着你从数据清洗开始,算清楚销售额、订单量、客单价、复购率这几个核心指标,再按商品和用户两个维度拆解,最后用图表把结论呈现出来。整个过程跑完,你能掌握的是一套可以复制到其他业务表上的分析套路,而不是一次性代码。
1. 项目概述与业务目标
1.1 这个项目为什么值得做
很多初学者学Python数据分析,容易陷入“光学语法、不会用”的尴尬。看教程时numpy和pandas的每个函数都能看懂,一拿到真实数据就不知道从哪下手。电商销售数据分析刚好是练习数据分析思维的最佳场景之一,因为业务逻辑大家都有体感:卖了多少、赚了多少、谁买的、买得什么东西,这些问题不需要额外解释。
更重要的一点是,电商数据集的“脏”程度非常贴近真实环境。你只要用真实业务里的导出数据做一次清洗,就会碰上缺失值、异常值、格式混乱、类型错误这些基础问题,而这些问题在官方文档的示例数据里几乎不会出现。我自己带新人时最常说的一句话就是:能把一张脏表洗到能直接分析,你就已经解决了实际工作中一半的问题。这个项目用到的技术点不算难,难点全在“怎么根据业务理解去做取舍”上面,这才是数据分析真正值钱的部分。
1.2 数据从哪来,要解决什么业务问题
我用的这份模拟数据遵循电商订单表的常见结构,主要包含订单编号、下单时间、支付时间、用户ID、商品名称、商品类目、销售数量、订单金额、支付金额、平台优惠、运费等字段。总量大概在五万行左右,时间跨度一年,覆盖近万名用户。实际项目里你从ERP后台导出的订单明细表结构基本类似,只是字段名会有差异,比如有的叫“实付款”有的叫“交易成功金额”,分析思路是一样的。
整个项目围绕三个业务问题展开:第一个,全年销售大盘怎么样,每个月的节奏是什么;第二个,哪些商品和类目真正贡献利润,哪些是虚耗流量;第三个,用户整体是“一锤子买卖”还是“回头客”,高价值用户能拆出多少。这三个问题对应业务上常说的“看大盘、找结构、看用户”,不分行业,几乎任何零售业务都能套用。我这次分析的模拟数据里,这些问题都能得到非常明确的答案,后面每个环节会逐个展开。
2. 环境准备与数据清洗实战
2.1 Python环境搭建与常用库安装
开始之前先把环境搞定。我本机用的是Python 3.10,通过Anaconda统一管理环境,好处是pandas、matplotlib、seaborn这些库都已经预装好了,不用一个个手动装。如果你目前是裸Python环境,建议直接用pip补齐本轮分析需要的那几个库:
pip install pandas numpy matplotlib seaborn这几个库的分工要清楚:pandas负责表格型数据的读取、清洗、聚合计算;numpy主要提供底层数组运算,很多场景下pandas会隐式调用它;matplotlib是基础绘图库,seaborn则是在它之上做统计图表的封装的,画热力图、分布图时比直接用matplotlib省代码。我个人习惯用seaborn出图、用matplotlib做细节调整,两者搭配效率最高。
提示:安装过程如果遇到网络超时,可以换成国内镜像源,比如
pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple,速度会快很多。
导入库时有个小细节:为了让图表里的中文显示正常,需要手动设置字体,这一点我会在后面的可视化环节单独讲。先看导入代码:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 中文字体配置,Windows和Mac有所区别 plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False2.2 数据加载与初步探查
拿到数据的第一个动作不是急着算指标,而是先看结构。我会先加载数据,然后快速过一遍shape、dtypes、head这几个命令:
df = pd.read_csv('sales_data.csv', encoding='utf-8') print(df.shape) print(df.dtypes) print(df.head())这一步的目的很纯粹:搞清楚有多少行、多少列、每列是什么类型、数据长什么样。一般真实订单表的原始字段会包含订单状态、收货地址、快递单号等一堆分析用不上的信息,先看一轮再决定留哪些列,比闷头清洗更高效。我这次拿到的数据里,冗余字段占了将近四成,修剪完只保留业务需要的核心列。
然后就是缺失值检查。这里要提醒一下:订单表里“支付时间”这列经常有缺失,因为不是所有订单都支付成功。缺失值不能一概删除,必须先确认是什么业务含义。我检查下来发现,支付时间为空的订单对应的是“未支付/已取消”状态,所以在清洗环节要先把这部分订单单独分流,而不是直接丢进分析样本里。
2.3 核心清洗逻辑:重复值、异常值与格式统一
重复值处理相对直白。订单ID理论上应该是唯一的,我查了下确实存在个别重复,处理方式是按照“订单号+商品名称”做去重判断,保留第一次出现的那条记录。操作层面用drop_duplicates()即可,但要注意指定判断的列和保留方式,否则可能误删有效数据。
接下来是异常值。销售数量和订单金额是这次分析的基石字段,这两个字段出现负数或极端大值就一定要查。数量为负的实际上是退货订单,在业务上不算销售;金额异常偏大的可能是测试单。我采用的策略是过滤掉这些数据,同时单独保存一份“异常订单表”备查,这样既不影响主分析,也能保留排查线索。
格式统一的重点是日期和时间。原始csv里的下单时间大概率是字符串类型,需要转成datetime,并且把“下单日期”和“下单小时”拆出来,后面做时间趋势和高峰时段分析都要用。具体操作:
df['order_date'] = pd.to_datetime(df['order_date']) df['order_month'] = df['order_date'].dt.to_period('M') df['order_hour'] = df['order_date'].dt.hour这一步看起来很基础,但我见过不少人栽在这里:日期字符串格式不一致,转完报错,或者转完多了几个空值。稳妥的做法是转格式之前先value_counts()看下有哪些格式,再用pd.to_datetime(..., errors='coerce')一次性处理,转化失败的行会自动变成NaT,后续再排查。
整个清洗下来,我保留了约四万五千条有效订单。这条数据从“原始可读但不可直接用”变成“干净可分析”的状态,整个过程大概投入了四十分钟。说句实在话,后面分析速度多快,完全取决于这一步做得细不细。
3. 核心销售指标计算与业务解读
3.1 大盘指标:销售额、订单量、客单价
大盘指标不用多,但每个都得算明白。电商业务最基础也最受关注的三个数字:总销售额(GMV)、总订单量、客单价。GMV的定义在不同公司有差异,我这里用“支付金额”来算,也就是剔除取消订单后实际成交的金额。代码很简洁:
total_gmv = df['pay_amount'].sum() total_orders = df['order_id'].nunique() avg_price = total_gmv / total_orders算下来的结果我不贴具体数了,但你可以留个印象:客单价在两百元上下,属于典型的日用快消品类水平。客单价这个指标单独看意义不大,但它对后续用户促销策略的制定有直接影响——客单价越高,意味着大促满减门槛要往上调。
这里想多说一句:销售额口径的选择,直接决定了你算出来的数跟财务对得上还是对不上。我遇到过不少新人用订单表里的“订单金额”去算GMV,结果发现利润薄的产品线连毛利率都是负的,后来一查才知道没剔除运费和优惠分摊。所以你在正式做分析前,一定要先确认清楚口径,或者至少建立起“订单金额、支付金额、实收金额”这三个概念的区别和联系。
3.2 时间维度:月度趋势与星期分布
大盘指标是静止的,时间趋势才好看业务节奏。我先按月份聚合看月销售额和订单量的变化,同时算环比、同比。环比很好理解,就是跟上一个月份比;如果手头有去年数据,还可以加上去年同月的数据做同比,看增长质量。
月度趋势几乎每个电商分析场景都要做,因为大促活动的效果、季节性品类的起落、新渠道引流带来的增量,都会体现在这条折线上。画图这步留到第五节细说,但这里把聚合逻辑先跑出来:
monthly = df.groupby('order_month')['pay_amount'].agg(['sum', 'count']) monthly['avg_price'] = monthly['sum'] / monthly['count']从结果里能明显看出“双11所在月份”销售额异常拔高,这倒是符合预期的正常现象。比较意外的是“6月份”也出现了一个小波峰,去看商品明细发现是夏季日用品的集中采买。这就是典型的时间维度发现,业务同事看到这个点会立刻追问“能不能把更多商品绑进这个时间窗口做套餐组合”。
星期分布是另一个容易被忽略的维度。电商订单有明显的星期效应:工作日和周末的消费偏好不同。我按星期几分组看了订单量和客单价,结果周末订单量明显上扬,但客单价反而略低,说明周末更多是零散小额补货类需求。这对广告投放和客服排班都有参考价值。
3.3 用户购买力分层:为后续精细化运营铺垫
大盘知道了,商品结构接下来拆,但在拆商品之前我更倾向于先给用户做个初步分层,因为用户价值决定运营策略,运营策略又决定主推哪些商品、定价多少。
用到的核心指标是每个用户的累计消费金额和累计消费次数,先归一化再按分位数切开。实际操作里,我画了一个简单的分位数表:把用户按消费金额从高到低排序,看前20%的用户贡献了多少销售额。这个思路就是经典的二八法则检验,代码也简单:
user_stats = df.groupby('user_id')['pay_amount'].agg(['sum', 'count']) user_stats['cumsum_ratio'] = user_stats['sum'].sort_values(ascending=False).cumsum() / user_stats['sum'].sum()结果显示,消费金额排在前20%的用户贡献了超过60%的销售额。这个数字不算极端,说明用户结构还算健康,高价值用户没到“绑架业绩”的程度。这个结论直接影响后续RFM分层的维度选择:金额和频次可以作为核心分层依据,但不必过度细分,三层足够指导运营动作。
4. 商品维度与用户行为深度拆解
4.1 商品分析:Top榜、类目结构与价格带分布
商品维度是电商数据分析里最受业务关注的内容,原因很直接:卖什么好卖、什么品要淘汰,都要从数据里找依据。第一步我按销售额排序做了商品Top20列表。这个列表看起来简单,但信息量很大:销售额高到底是因为销量高还是因为单价高,两者的运营策略完全不同。
如果是单价高带起来的销售额,那是高客单价爆品,活动策划要多给曝光、做搭配销售;如果是单价低靠销量堆出来的,那是走量款,需要控库存、防断货,同时评估毛利够不够覆盖物流成本。代码上用groupby加sort_values就能做,但只看Top列表有个盲区:那些排在后面的长尾商品有没有存在价值?我特意看了Top20之后商品的数量占比和销售额占比,将近一半的商品贡献不到百分之五的销售额,这就是典型的“长尾拖累”——虽然不能说全砍,但推广资源肯定不该平均分配。
类目结构更能说明问题。我把商品类目和销售额、订单量、客单价做了交叉透视表,发现不同类目之间的客单价差异非常悬殊:有的类目客单价能到五百以上,有的只有几十块。这种差异决定了活动的设计方式:高客单价类目适合做满减,低客单价类目更适合做第二件半价这类组合购买。
价格带是很多新手会忽略的分析角度。我按商品价格分成几档,看每个价格带的销量和销售额贡献。这个分析能直接回答“我们的主要销量集中在中低价位还是高价位”,对选品定价帮助很大。我这次的模拟数据显示“50到100元区间”是销售主力带,而当时正在主推的新品定价却在200元以上,这个信息要是早点看,产品定价决策可能完全不同。
4.2 用户行为分析:复购率、购买频次与首次购买时间
商品是“货”的角度,现在把镜头转到“人”。复购率是衡量用户忠诚度的最基础指标,定义有很多种,我用的是“统计周期内购买两次及以上的用户数占活跃用户总数的比例”。注意不同口径算出来差异可能很大,所以一旦定了口径就要保持一致,否则跨期对比没有意义。
复购率用代码实现的方式有多种,比较直接的是先按用户聚合出购买次数,再统计次数>=2的用户占比:
user_order_count = df.groupby('user_id')['order_id'].nunique() repurchase_rate = (user_order_count >= 2).mean()测出来的复购率大概在百分之二十几,这个数对快消类电商来说不算高。我看到这个结果的第一反应不是急着下“用户忠诚度差”的结论,而是去查了新客占比和首购时间。如果大量用户都是最近半年才进来的,百分之二十几的复购率其实还可以接受,因为新客还需要时间去积累第二次购买。
购买频次分布更有意思。大量用户只买过一次,这是正常的漏斗规律;但买了五单以上的用户数量虽然少,贡献的销售额比例却不低。这批“高频用户”正是RFM模型里的“高价值聚类”,也是后面做用户运营的重点触达对象。如果你在真实业务中也发现高频用户集中在某一个区域或某一类商品,那一定要深挖,那往往是业务的突破口。
4.3 RFM分层实战:用三个维度把用户划分出运营重点
RFM是零售分析里的常青树模型,全称是Recency(最近一次购买时间)、Frequency(购买频次)、Monetary(消费金额)。这三个维度拼起来能描述一个用户“最近有没有来、来得多不多、花了多少钱”,对电商而言是最经典的用户价值评估框架。
实操的时候我不建议直接套公式,因为不同业务的购买周期差异太大。比如家具类电商两三个月买一次很正常,快消食品却可能每周都买,直接统一阈值会失真。我这次的做法是看数据分布来定阈值:R维度取最近一次购买距统计截止日期的中位数天数,F维度取购买次数的中位数,M维度取消费金额的均值。每个维度二分类,组合出八大类用户。
截取核心代码思路:
snapshot_date = df['order_date'].max() rfm = df.groupby('user_id').agg({ 'order_date': lambda x: (snapshot_date - x.max()).days, 'order_id': 'count', 'pay_amount': 'sum' }) rfm.columns = ['recency', 'frequency', 'monetary']分完层之后,我重点关注了“重要价值用户”和“重点保持用户”这两组。重要价值用户的特征是“最近买过、买得多、花得多”,对应的运营动作简单粗暴:给到足够力度的会员权益和上新提醒,目的是防御竞争对手挖走他们。重点保持用户特征是“买得多花得多但最近没来”,这类用户最值得用召回券刺激一笔复购。这个思路很朴素,但真实轮运营里几乎天天用得上。
RFM模型在分析圈子里被很多人说“过时”,但我一直觉得不是模型问题,是用法问题。纯按公式分层给的运营建议确实泛泛,但如果结合上一节的商品偏好交叉分析,每类用户到底该推什么品,RFM完全能落到非常具体的运营动作,那就不空了。
5. 可视化图表实战与避坑指南
5.1 matplotlib中文乱码与横坐标日期过密的处理
电商销售数据的可视化,第一道坎往往是中文乱码。直接画图你会发现图表标题、图例全部变成方块字,这就是默认字体没有对应中文导致的。解决办法前面环境准备那节已经提过,核心就是改plt.rcParams里的字体设置。这个配置要放在绘图前统一执行一次,而不是每画一张图就设一次。
第二道坎是横坐标时间标签挤压。按月看趋势还好,如果是按周甚至按天看数据,图表底部的时间标签就会叠成一团黑点,完全没法读。我处理这个问题有三个常用方案:一是旋转标签,简单省事但不能根本解决;二是隔几个点显示一个标签,用plt.xticks()手工指定;三是把图表尺寸拉宽,让每个标签有足够空间。实际操作中我习惯先拉长尺寸再隔点显示,两招组合基本能解决绝大多数过密情况。
fig, ax = plt.subplots(figsize=(12, 6)) plt.xticks(ticks=range(0, len(monthly), 3), labels=monthly.index[::3], rotation=30)5.2 用组合图快速定位业务问题
可视化不是把图表堆出来,而是每张图都有自己的分析目标。我做这次分析时,最常用的是组合图:柱状图加折线图叠加。柱状表示订单量,折线表示客单价,两者用双Y轴展示,目的是快速判断“订单量涨的时候客单价有没有跟着涨”,如果订单量上了但客单价掉了,说明增长的质量不健康,可能是低价促销带来的虚假繁荣。
订单状态分布用饼图看最直观,但饼图我觉得不是最好的选择,因为类别一多就分不清谁大谁小。这几次我改用横向条形图,数据标签直接标在末尾,一眼就能定位占比结构。分析销售数据时,我自己有套选图心法:看变化用折线、看排名用条形、看构成用堆叠柱状、看分布用箱线图。不要为了炫技硬上复杂图表,业务方看不懂的可视化就是失败的。
5.3 三张核心图表带你建立分析直觉
每次做这类分析,我会固定产出三张核心图表,它们基本能覆盖老板和运营最关心的信息。第一张是全年度月度销售额趋势线。这张图贴出来之后,全年什么时候冲刺、什么时候淡季一目了然,预算和人力安排都能对上。
第二张是商品类目销售额对比条形图。这张图要能看出头部类目和长尾类目的差距,方便确定资源投放重点。第三张是用户分层占比堆叠图或者分组柱状图,重点表现“有多少用户贡献了主要销售额”。这三张图都做出来,一次完整的业务汇报素材基本就齐了。
再强调一下配色问题。seaborn默认的配色整体观感还行,但如果你做商务汇报,建议手动调成柔和、低饱和度的色板。花里胡哨的颜色看起来“科技感”很强,但在严肃业务分析场景里反而会干扰判断。另外,所有图表都要加标题和轴标签,数据标签见机加——信息量适中才是最好的,不是越多越好。
6. 实战中的常见问题与排查技巧
6.1 数据量过大导致运行慢的优化手段
电商销售数据动辄几十万行,在小笔记本上跑pandas的groupby有时会卡得让人崩溃。我第一次跑这份数据时也遇到了同样问题,排查之后发现主要瓶颈不是数据量本身,而是聚合维度过多、中间过程反复拷贝数据。
优化手段排序下来,收益最明显的有三个。第一个是在聚合之前先只保留分析需要的字段,把几十列砍到十来列。第二个是尽量避免apply里写循环逻辑,能用内置聚合函数就一律用内置函数。第三个是如果内存还是吃紧,可以用category类型压缩类目字段,对字符串列做astype('category')有时能砍掉一大半内存。
df['category'] = df['category'].astype('category')真实项目里如果数据量超过几百万行,我通常会建议换用polars或者上SQL,但那是另一个话题了。对大多数中小型电商的订单明细来说,pandas优化后完全够用。
6.2 时间字段转换失败与金额口径不一致
时间字段转换是本项目里最容易踩坑的一环。原始数据里的订单时间格式五花八门:有的是“2024-01-05 12:23:44”,有的是“2024/1/5 12:23”,还有个别是纯文本。统一转换建议直接用pd.to_datetime(column, format='mixed'),它会自动识别不同格式。如果还是报错,就用errors='coerce'把无法解析的行置成NaT,再单独排查。
金额口径不一致的问题更隐蔽。我拿到这张表时发现“订单金额”和“支付金额”之间存在差异,有些订单的支付金额等于订单金额加运费,有些却是减掉优惠后的结果。后来我仔细核对了字段说明文档,才确认平台优惠和运费在两条路径上的处理方式不同。这个坑没有捷径,只能靠业务理解去对冲,至少你要知道每个字段的定义再动手算。
6.3 极简排查清单与避坑心得
结合我反复做这类项目的经验,给你整理一份上手就能用的踩坑清单:
groupby之后如果结果跟你预期不一致,优先检查是不是前面清洗时丢失了部分行,尤其是过滤条件是否误伤正常数据。- 计算复购率时,订单号用
nunique去重,别用count,否则一单多商品会虚增购买次数。 - 先按金额排序再算累计占比,别直接对原始数据做
cumsum。 - 做RFM分层前先把缺失用户过滤干净,否则分层结果会带偏。
- 保存最终图表时,用
dpi=150或更高,防止导出后图片模糊。
这几点看起来都是细节,但任何一个出错,呈现出来的分析结论都可能差出十万八千里。我早期做项目时,就因为复购率误把订单数当人数,直接给业务方报了个偏高一倍的数,后来核对才发现,整个过程非常尴尬。从那之后,每次算指标之前我都会在草稿纸上先写一遍业务公式,再对照代码里到底算的是哪个字段。
6.4 汇总一张速查表供日常参考
| 问题场景 | 常见原因 | 排查/解决手段 |
|---|---|---|
| 中文标签全变方块 | 默认字体不支持中文 | 设置plt.rcParams['font.sans-serif']指定中文字体 |
| 日期转换报错 | 源数据格式混杂或含非法值 | 用errors='coerce'转NaT后单独排查 |
| 销售金额出现负数 | 存在退款订单 | 先确认业务含义,必要时单列退款分析 |
| 聚合速度过慢 | 列数过多且反复拷贝 | 只保留分析字段,并尝试压缩类型 |
| 横坐标标签重叠 | 时间粒度太细 | 隔点显示标签、旋转角度、放大画布 |
| 客单价异常波动 | 未剔除未支付订单 | 清洗阶段把支付状态筛出来再算 |
| 复购率虚高 | 使用订单数而非用户数 | 统一用nunique对用户ID去重 |
| 按月趋势缺数据 | 存在大段时间空档 | 检查源数据时间范围,必要时补NULL月份 |
这张表是我日常做电商分析时翻看频率最高的内容,虽然场景不算多,但覆盖面基本能应对常见交付。
写在最后:这套分析能力可以怎么延伸
这次从零到一跑完电商销售数据分析,你会发现核心能力其实并不复杂:能理解数据里的业务含义、能把脏数据洗到能用的状态、能选择合适的指标回答具体问题、能把结论画成让人秒懂的图表。这四件事做扎实了,不管换到哪个行业的数据分析项目,你都能快速迁移。
我个人在实际操作中最大的体会是,所有模型和算法都排在“先把业务问题问清楚”之后。拿到数据先定义一个最想解决的具体问题,比闷头把能算的指标全算一遍要有用得多。如果你后续想把这个项目继续扩展,有两个方向建议先试试:一是引入订单退换货数据,做退款率归因分析,你会发现很多商品质量问题其实是可以用数据说话的;二是把用户分层和商品偏好做交叉,生成“高价值用户最爱买什么”的清单,这份清单在运营眼里比任何统计模型都直接。
最后再分享一个小技巧:分析做完之后,养成把清洗逻辑和数据口径写成文字备注的习惯。隔一个月再回看项目时,这些备注能帮你节省大量回忆时间。数据是会过期的,但方法和经验不会。希望这篇实战笔记对你的下一个分析项目有帮助。