1. 零基础学数据分析,到底在学什么?先别急着背语法
我一直觉得“Python零基础14:数据分析”这个标题,放在系列的第14篇,其实是恰到好处的。前面十几篇如果已经让你把Python的基本语法、列表字典、循环判断这些东西混了个脸熟,那么到数据分析这一篇,你就不是“从零开始”,而是“从语法开始走向应用”。但如果你真是从头直接跳到这里来看,也没关系,我会把所有用到的基础语法都解释到“能抄作业”的程度。
先说清楚一个最容易被误解的事:数据分析不是数学竞赛。你不需要先学完概率论、线性代数才能动手,实际工作中大量的分析任务,说白了就是四件事——把数据读进来、把脏数据洗干净、把数据分组汇总、再把结果画成图讲给别人听。这四件事,Python生态里都有现成的工具,尤其是pandas和matplotlib这两个库,它们就是为“零基础也能上手干活”设计的。
那这篇博文到底要解决什么问题?我用一句话概括:给一个只有Python基础语法、完全没接触过数据分析库的人,一条能最快跑通“导入数据—清洗—统计—出图—得出结论”全流程的路线图。你跟着做下来,不会成为数据分析专家,但你会拥有一套可以套用到很多真实场景的完整套路,以后再看见Excel表、CSV文件、后台导出的数据,你不会再发怵,而是知道从哪里下手。
另外要补充一句,这篇内容里面我会用到两个核心库:pandas和matplotlib。如果你还没安装,不用慌,后面我会单独讲安装和验证,把这个零基础最容易卡住的环节也一并理顺。
2. 第一步不是写代码,是把“数据原料”准备好
2.1 选一个够真实又够简单的数据集
我见过太多零基础的人倒在“选数据”这一步。有人一上来就去爬网站、找那种几十万行的真实业务数据,结果数据里面的坑比收获还多,光清洗就洗了两天,最后得出结论“数据分析太难了,我不适合”。
我的建议是:第一次练手,数据集要满足三个条件——体量小、字段少、贴近生活。体量小是指几百行就够,甚至几十行也能跑通流程;字段少是指列名一眼能看懂,比如日期、商品名、销售额、数量,而不是一堆英文缩写;贴近生活是指你看到数据就知道它大概在讲什么,这样即使分析结果有点奇怪,你也能凭常识判断是不是哪里做错了。
我常用的练手数据是某公司的模拟销售记录,格式大致长这样:
日期,商品类别,商品名称,销售量,单价,销售金额,销售区域,销售人员 2025-01-03,数码,无线耳机,12,299,3588,华东,张三 2025-01-05,食品,坚果礼盒,45,88,3960,华南,李四这种表格就是一个典型的“宽表”,每一行是一条销售记录,每一列是一个维度的信息。你不需要去网上下载什么复杂数据,自己在记事本里敲几十行这样的记录,或者从Excel里导出一个CSV文件,就完全可以作为入门数据。CSV(逗号分隔值文件)是数据分析里最常见的数据格式,Excel可以直接另存为CSV,后面我们所有代码示例都基于这种文件。
2.2 环境选择:Jupyter Notebook比纯脚本更适合入门
我强烈建议零基础阶段用Jupyter Notebook,而不是直接在纯Python文件里写代码。原因很简单:数据分析是一个“试错”的过程,你经常要读一行数据、看一个结果、再决定下一步干什么。Notebook的“单元格”模式让你可以把代码分成小块,一块一块地执行,每一个中间结果都看得见,这对培养直觉非常有帮助。
安装这一步也顺便说清楚。如果你之前已经装好了Python,那么直接在命令行运行:
pip install pandas matplotlib如果你想一次性把环境都搞定,也可以直接安装Anaconda,它会自带pandas、matplotlib、Jupyter Notebook这些工具,省去很多依赖问题的折腾。装完之后,在命令行输入jupyter notebook,浏览器里会打开一个界面,新建一个Python 3的笔记本,就可以开始了。
注意:我在实际带人入门的时候,发现最常遇到的问题不是库装不上,而是“装到了不同的Python环境里”。如果你电脑上装过多个Python版本,建议在命令行里用
pip --version确认一下当前pip属于哪个Python,再用python -c "import pandas; print(pandas.__version__)"验证导入是否成功。这个步骤虽然不起眼,但能省掉后面一大半的“ModuleNotFoundError”。
2.3 用pandas读入数据后,先别急着做分析
拿到CSV文件之后,读入只需要一行代码:
import pandas as pd df = pd.read_csv("sales_data.csv", encoding="utf-8")读进来之后,我建议你第一件事不是算总额,也不是画图,而是“打量”这份数据。用什么打量?三招就够了:df.head()看前几行长什么样,df.info()看每一列的数据类型和缺失值情况,df.describe()看数值列的统计概况。这三行代码跑完,你对整个数据的基本盘就有数了。
这里有一个零基础容易忽略的点:read_csv里的encoding参数。如果文件里有中文,经常会出现乱码,常见的原因就是编码不匹配。UTF-8读取不了的时候,可以试试encoding="gbk",尤其是从Windows上的Excel导出的CSV文件,大概率要用GBK。这一行参数虽然小,但第一次遇到乱码时能救命。
3. 让数据“听话”:清洗与整理是真正见功底的地方
3.1 先做类型检查和转换,别让pandas“猜错”
很多人以为数据分析最难的环节是建模、是算法,但实际上,真正占据一个数据分析师日常时间最多的工作是数据清洗。有一句话我特别认同:数据分析里80%的时间都花在清洗数据上,20%的时间用来分析,但大家记住的都是那20%的成果。初次接触清洗,你不需要掌握所有方法,只需要先关注三类最常见的问题:类型不对、缺失值、重复值。
先看类型不对。比如“销售金额”这一列,在Excel里看起来是数字,但导入pandas后很有可能是对象(object)类型,也就是字符串。原因通常有两个:数据里有“,”这种千分位分隔符,或者有些单元格里混进了空格、异常字符。要检查类型,用df.dtypes;要转换,用pd.to_numeric,它会自动尝试把字符串转成数字。
df["销售金额"] = pd.to_numeric(df["销售金额"], errors="coerce")这里的errors="coerce"意思是:转不过来的值变成NaN(缺失值),而不是报错中断。很多教程不会专门强调这个参数,但实际数据里几乎一定会有脏数据,加了这句就不会因为一行坏数据让整个程序崩溃。这就是“以防万一”和“硬碰硬”的区别。
3.2 缺失值处理:先弄清楚“为什么缺”,再决定“怎么处理”
缺失值在pandas里显示为NaN,它不是0,也不是空字符串,而是一个“这里没有值”的标记。很多零基础的人看到NaN就条件反射地删除,这其实是偷懒的做法。我在实际项目里踩过坑之后才明白,处理缺失值之前,一定要先问自己:这行数据为什么缺?是数据源本身没记录,还是清洗过程中转类型失败造成的?
如果只是少数几行缺失,而且后续分析用不到这行数据,直接删除是可以接受的,用df.dropna()。但如果你要做的是统计汇总,比如算销售总额,那缺失值就会影响结果。这时候更稳妥的做法,是用合理的值去填充。什么叫合理?要看业务场景。销售金额缺失,用整列的平均值填,算是一个比较中性的选择:
df["销售金额"] = df["销售金额"].fillna(df["销售金额"].mean())但对于“销售量”这种本身取值应该比较规律的字段,用中位数可能比平均数更合理,因为中位数不容易被极大极小值拉偏。我教零基础朋友的时候会打一个比方:平均数像班里所有同学成绩的“平均水平”,但如果有一个同学考了特别高的分,平均水平就会被拉高;中位数则是“站在中间那个人”的成绩,更皮实。在数据有离群值的时候,中位数往往更可靠。
3.3 增加新字段:从日期里拆出“月份”,是性价比最高的一步
数据清洗不光是修问题,也包括“造新数据”。最典型的一个操作,是从日期列里提取出年、月、日、星期几之类的信息。为什么这么做?因为很多时候,单看“2025-03-15”这个完整日期,你很难直接看出规律,但如果你把它拆出“月份”和“星期几”,数据里隐藏的周期规律就会浮现出来。
df["日期"] = pd.to_datetime(df["日期"]) df["月份"] = df["日期"].dt.month df["星期几"] = df["日期"].dt.dayofweekpd.to_datetime是把文本日期转成真正的时间类型,转完之后,.dt后面就可以接很多时间相关的属性了。月份是1到12的数字,星期几是0到6的数字(周一是0,周日是6)。这一步做完,你的数据表就比原来多了两列“现成的分析维度”,后面做按月汇总、按星期汇总都变得非常方便。
4. 分组聚合与排序:分析思路从“看一眼”变成“算一算”
4.1 groupby的底层逻辑:一句话说清“拆、算、合”
数据分析里最常干的一件事,就是“按某个维度汇总数值”。比如,按销售区域求总销售额,按商品类别求平均销售量,按月份求总销售额并找出旺季淡季。这个需求在pandas里用groupby实现,看名字就知道,它的逻辑是“分组”。
我拆解一下df.groupby("销售区域")["销售金额"].sum()这行代码到底发生了什么:第一步,pandas先把整张表按“销售区域”的不同值分成几个小组;第二步,对每一组的“销售金额”这一列执行求和操作;第三步,把每个组的结果合并成一张新表。这个思路你不需要背,只需在脑子里记住“拆、算、合”三个字。
实操中经常是从一个分组维度变成两个分组维度。比如,想看“每个区域每个月的销售额”,就可以这样写:
monthly_region = df.groupby(["销售区域", "月份"])["销售金额"].sum()这样得到的结果是一个带双层索引的Series,有点别扭。如果想让结果更像表格,用reset_index()就能把索引变成普通列。再加一个参数as_index=False,可以让聚合结果直接保持为DataFrame:
monthly_region = df.groupby(["销售区域", "月份"], as_index=False)["销售金额"].sum()这里我想多说一句:零基础学groupby,最容易犯的错误是忘了reset_index或者as_index,导致结果是一个“看起来很奇怪”的对象,然后就开始怀疑自己写错了。其实不是写错了,只是pandas默认把分组的键变成了索引,它不是用来直接“看”的,而是用来“继续操作”的。遇到这种情况,别慌,加一行reset_index()就回到你熟悉的表格结构了。
4.2 透视表:把“分析维度”摊开来看
如果你用过Excel的数据透视表,那pandas的pivot_table对你来说应该很亲切。它能做跟groupby类似的事情,但结果是以“交叉表”的形式呈现:行是一个维度,列是另一个维度,表格中间是汇总值。这种形式的优势是直观,适合人脑直接对比。
举个例子。我想看每个销售区域在不同商品类别上的总销售额,用透视表写:
pivot = pd.pivot_table( df, values="销售金额", index="销售区域", columns="商品类别", aggfunc="sum", fill_value=0 )这段代码的意思一目了然:values指定要对哪一列求和,index是行维度,columns是列维度,aggfunc是汇总方式(sum、mean、count都可以),fill_value是把没有数据的格子填成0而不是NaN。我建议第一次做透视表时,就按这个“四要素”来理解:值、行、列、怎么汇总。
对比groupby和pivot_table,我的经验是:如果分析目标是“接下来还要继续用这些数据做各种计算”,用groupby更顺手;如果目标是“把结果打印出来给人看、让老板一眼看懂对比关系”,用pivot_table更直观。两者不是谁替代谁的关系,而是不同场景下的两种表达。
4.3 排序和Top N:分析结果别一锅端,聚焦关键对象
分组聚合做完,你得到的结果往往是一张几十行的汇总表。这个时候,如果直接拿全表去讲结论,听众根本抓不住重点。比较聪明的做法是排序,然后只看前几名。
按销售额降序排列,然后取前三名,用sort_values加head:
top3 = df.groupby("商品类别", as_index=False)["销售金额"].sum().sort_values("销售金额", ascending=False).head(3)这行代码有点长,但它的结构其实很像一条流水线:先分组求和,再按金额排序,最后取前3行。零基础阶段,我建议把这种“链式写法”拆成几步来做,每步用print看看中间结果,熟悉之后再合成一行。
head()和tail()是pandas里特别常用的两个“取数”工具,分别取前几行和后几行。在探索阶段,多用head()看数据,能有效防止程序一次性打印几千行把控制台刷爆。
5. 画图:让分析结论“一眼被人看懂”
5.1 matplotlib第一步:从折线图开始,理解“画布”和“坐标系”
数据算完了,下一步是把结果可视化。Python生态里画图工具不少,但对于零基础,matplotlib永远是我的第一推荐。原因有三个:它是最基础的绘图库,几乎所有其他绘图库都建立在它之上;它的概念简单,就是“画布+坐标系+图形元素”;它跟pandas的配合很自然。
折线图适合展示数据随某个连续变量变化的趋势,最典型的就是“月度销售趋势”。先算按月汇总的数据:
monthly = df.groupby("月份", as_index=False)["销售金额"].sum()然后画图:
import matplotlib.pyplot as plt plt.figure(figsize=(8, 5)) plt.plot(monthly["月份"], monthly["销售金额"], marker="o") plt.xlabel("月份") plt.ylabel("销售金额") plt.title("月度销售金额趋势") plt.show()这里面figure(figsize=...)是创建画布并设置宽高,plot是画线,marker="o"是在每个数据点画一个圆点标记,方便看清楚每个月的具体位置。xlabel、ylabel、title不言自明。跑完这段,如果能看到一张带趋势的折线图,恭喜你,你已经在“用Python讲故事”了。
5.2 柱状图和饼图:对比和构成要选对图
柱状图适合做“分类对比”,比如对比各个区域的销售总额。画法很简单:
region_sum = df.groupby("销售区域", as_index=False)["销售金额"].sum() plt.figure(figsize=(8, 5)) plt.bar(region_sum["销售区域"], region_sum["销售金额"]) plt.xlabel("销售区域") plt.ylabel("销售金额") plt.title("各区域销售金额对比") plt.show()饼图适合展示“构成比例”,也就是“谁占了多少”。但这里我要提醒一句:饼图在数据项很多的时候会非常丑,而且不同扇区面积相近时很难对比。所以我的建议是,饼图最多用来展示3到5个分类的占比,超过5个就改用柱状图,或者把次要的类别合并成“其他”。这不是什么高深原理,纯粹是用图的经验——人的眼睛对“长度”的对比能力强,对“面积”和“角度”的对比能力弱得多。
5.3 中文乱码:新手碰到的第一个“系统性难题”
matplotlib默认是不支持中文显示的,标题或坐标轴里凡是中文,画出来全是方块。这是零基础阶段最让人崩溃的问题之一,但解决方法其实非常简单,在你的绘图代码前加上三行:
plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False第一行指定了中文字体,SimHei(黑体)是Windows系统自带的;第二行是为了让负号正常显示。如果你用的是Mac系统,可以把"SimHei"换成"Arial Unicode MS"或"PingFang SC"。我建议把这套配置固定写到一个单独的单元格里,每次开始分析前先跑一次,相当于“全局设置”。
还有一个细节容易被忽略:rcParams设置要在绘图之前执行。很多人把设置的代码贴在plt.show()之后,发现没生效,就开始折腾其他东西,最后折腾好久才发现是顺序问题。先设置后绘图,这个顺序记住了,能少一次白熬夜。
6. 一个完整小案例:从零开始跑通“数据到结论”
6.1 案例目标:找出哪个月卖了多少钱、哪类商品贡献最大、哪个区域需要关注
前面讲了一堆工具,现在把它们拼起来走一遍流程。我假设你已经准备好一份模拟的销售数据CSV,里面包含日期、商品类别、销售金额、销售区域这几个字段。我们这次的目标很简单:回答三个问题。
第一,全年销售趋势如何,哪个月是旺季、哪个月是淡季;第二,哪个商品类别贡献了最多的销售额;第三,哪个销售区域的表现最需要关注。这三个问题分别对应折线图、柱状图和排序分析,正好把前面学的知识点都用上。
6.2 完整代码流程:读数据、清洗、汇总、画图一气呵成
下面我写一个完整流程,每一步都带注释。你不需要一次看懂每一行,跟着跑一遍,再回头逐行理解,这个过程中建立的“整体感”比看十段零散代码都更有效。
import pandas as pd import matplotlib.pyplot as plt # 1. 设置中文字体 plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False # 2. 读入数据 df = pd.read_csv("sales_data.csv", encoding="utf-8") # 3. 类型转换与新增月份字段 df["销售金额"] = pd.to_numeric(df["销售金额"], errors="coerce") df["日期"] = pd.to_datetime(df["日期"]) df["月份"] = df["日期"].dt.month # 4. 去除销售金额缺失的记录 df = df.dropna(subset=["销售金额"]) # 5. 月度汇总 monthly = df.groupby("月份", as_index=False)["销售金额"].sum() # 6. 月度趋势图 plt.figure(figsize=(8, 5)) plt.plot(monthly["月份"], monthly["销售金额"], marker="o") plt.xlabel("月份") plt.ylabel("销售金额") plt.title("月度销售金额趋势") plt.show() # 7. 商品类别汇总排序 category_sum = df.groupby("商品类别", as_index=False)["销售金额"].sum() category_sum = category_sum.sort_values("销售金额", ascending=False) # 8. 类别对比柱状图 plt.figure(figsize=(8, 5)) plt.bar(category_sum["商品类别"], category_sum["销售金额"]) plt.xlabel("商品类别") plt.ylabel("销售金额") plt.title("各商品类别销售金额") plt.show() # 9. 找到月度销售额最低的区域 region_monthly = df.groupby(["销售区域", "月份"], as_index=False)["销售金额"].sum() worst_region_month = region_monthly.sort_values("销售金额").head(1) print(worst_region_month)这段代码跑完,你会得到:一张趋势图、一张对比图、一个输出结果。虽然只是基础操作,但你已经完成了一轮完整的“数据探索式分析”。
6.3 把结果写成一句“人话”
分析做完,最后一步不是展示代码,而是把结果讲清楚。这一步往往是很多零基础教程会漏掉的关键。我自己的习惯是,每次分析收尾时,强制自己用一句话写下结论。
比如,看完上面代码跑出来的结果,你可以这样总结:下半年整体销售趋势上升,12月是全年峰值,可能是年末促销影响;数码类是销售额最大品类,占比约40%;某区域在某个月的销售额明显偏低,建议核查该区域的促销排期或人员配置。这段话没有写任何代码,但它是整个分析的最终产出,因为数据只有被翻译成业务结论,才真正产生价值。
7. 常见问题速查:那些教程没写但一定会遇到的坑
7.1 报错“ModuleNotFoundError: No module named 'pandas'”
说明pandas还没有安装,或者当前运行环境不是安装pandas的那个环境。解决方案是回到命令行执行pip install pandas,然后确认python -c "import pandas"不报错。如果之前已经安装但仍然报错,检查是否在Jupyter里使用的Python内核与命令行中的Python不一致,这是环境问题里最常见的一种。
7.2 数据中有中文,读进来乱码
优先检查文件编码。CSV文件如果带BOM头,UTF-8读取时列名会带上奇怪的字符,这时候用encoding="utf-8-sig"可以解决。如果中文内容乱码,可以尝试encoding="gbk"。这个经验我写过无数次了,几乎每个零基础项目都会遇到一次。
7.3 日期列一直转不成日期类型
先打印这一列的独特值看一看。df["日期"].unique()能快速列出所有不重复的值,如果里面有日期格式不统一的字符串,比如有的写“2025/1/1”,有的写“2025年1月1日”,pd.to_datetime可能无法统一转换。遇到这种情况,先用errors="coerce"让转不出来的变成缺失值,然后针对异常值单独处理。零基础阶段不追求处理得完美,追求的是“知道问题出在哪”。
7.4 画出来的图没有线条,只有一个空坐标系
常见的原因是plt.show()在代码块里执行了两次,或者前面已经show过一次导致当前画布被清空。另一个常见原因是,你在一个函数里画图,但没有在函数开头创建新画布,导致所有图形画在了同一块画布上。解决办法是:每次画图前先plt.figure(figsize=(8, 5)),相当于“换一张新纸再开始画”。
7.5 groupby之后打印结果发现多了一列索引
这不是错误,是pandas的默认行为。如果你不想看到索引,加reset_index();如果你希望以后继续按这个分组的键进行操作,保留索引有时候反而更方便。理解索引的概念确实需要一点时间,但它是pandas里你最值得花功夫弄懂的概念,因为几乎所有的“奇怪现象”都和索引有关。
8. 送你一套“数据分析第一周”的练习路径
到这里,整条流程已经走完了。如果只给你一个建议,我会说:不要急着学那些复杂的“进阶技巧”,先用小数据集把今天这套流程扎实地跑三遍。
第一遍,我建议你完全照抄代码,跑通即可,目的是熟悉工具和环节;第二遍,换一个自己熟悉的数据集,哪怕是自己手工输入的“一周零花钱记录”,也要逼着自己完成读入、清洗、汇总、画图这四个环节;第三遍,尝试改一个环节,比如把“按月份汇总”改成“按星期几汇总”,看看得到的结果能讲出什么新故事。
挖掘标题背后的深层价值,其实就是希望你能从“看我操作”变成“自己动手”,从“跟着代码走”变成“带着问题来试”。数据分析这门技能,最大的门槛从来不是语法,而是“动手去折腾”的勇气。很多朋友学了语法就看教程,看了教程就觉得自己会了,但真正关掉参考文档、对着一个空白笔记本发愣时,才知道自己哪里没懂。没有关系,犯错也是数据的一部分,分析自己的“错误日志”,也算一次不错的数据分析练习。