news 2026/9/10 8:15:10

Pandas数据分析全流程:从数据清洗到可视化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pandas数据分析全流程:从数据清洗到可视化实战

想聊一个很实际的问题:Pandas在数据分析里到底怎么用?很多朋友学了一堆函数,打开真实数据还是一脸懵。这篇文章我会从数据清洗讲到可视化,用一套完整的流程串起Pandas的核心操作,包括环境配置、类型转换、分组聚合、绘图定制,以及那些文档里不会写但实际一定会踩的坑。适合刚入门Python数据分析、准备用Pandas做实际项目,或者从Excel迁移过来的朋友参考。

1. 为什么数据分析的第一站要选Pandas

1.1 从Excel到Pandas:一张大表引发的迁移

我最早处理业务数据也是用Excel,后来遇到一张几百万行的订单明细表,Excel直接卡死,筛选一下要好几分钟,透视表转两圈风扇就起飞。那时候就意识到,Excel的瓶颈不在功能,而在内存和算法效率。

Pandas干脆把数据加载到内存里,用Python的向量化运算来处理,速度完全不在一个量级。更重要的是,清洗、转换、分析的每一步都可以写成脚本,下次拿到新数据直接跑,不用再重复点鼠标。对于周期性的月度报表、日报统计,这个优势特别明显。

1.2 DataFrame和Series:先搞懂这两个概念

Pandas的两个核心对象,一个是Series,一个是DataFrame。Series可以理解成一列带有索引的数据,结构很像一个字典加一个顺序;DataFrame则是一张二维表,由多个Series按列组成,自带行索引和列名。

import pandas as pd # 创建一个Series s = pd.Series([100, 200, 300], name='销售额') # 创建一个DataFrame df = pd.DataFrame({ '门店': ['A店', 'B店', 'A店'], '销售额': [100, 200, 150], '成本': [60, 110, 80] })

实际处理数据时,90%的操作都是围绕DataFrame展开的。你只需要记住:行是记录,列是字段,索引是行标签。后面所有操作——筛选、分组、透视、绘图——都是在跟这张表打交道。

2. 环境准备:Pandas安装与PyCharm里的常见坑

2.1 PyCharm中安装Pandas包的两种方式

搜索热度很高的"pycharm怎么安装pandas包"说明这是很多新手的第一个坎。其实很简单,两个途径任选。

第一种,在PyCharm里操作:打开File → Settings → Project → Python Interpreter,在右侧点击+号,搜索pandas,点Install Package。这种方法最直观,适合刚接触虚拟环境的朋友。

第二种,用命令行:在PyCharm底部的Terminal里执行。

pip install pandas

这里有个特别容易踩的坑:如果电脑里装了多个Python环境,命令行里的pip可能指向的是全局Python,而PyCharm项目用的是虚拟环境,两边互不相通。常见表现为命令行里import pandas正常,但PyCharm里报ModuleNotFoundError。解决办法就是看PyCharm右下角或Settings里选中的Interpreter路径,在命令行用对应路径下的pip安装。

如果你在国内网络环境,pip下载慢可以加国内镜像源:

pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

2.2 AttributeError: module 'pandas' has no attribute 'core' 排查链路

这个报错在网上检索量非常高,我实际排查过一次,分享一下完整思路,你以后遇到类似问题可以照着走。

当时一个同事跑代码,import pandas后访问pd.core,直接抛了AttributeError: module 'pandas' has no attribute 'core'。第一步,我先看了pandas版本:pip show pandas,发现版本是2.0,按道理core是内部模块,不应该没有。第二步,检查是否安装了多个pandas,pip list | findstr pandas,发现有两个路径下各有pandas。这通常意味着环境污染。

第三步,也是最常见的原因:项目目录下存在pandas.py文件,或者用户自定义模块命名为pandas.py。Python在导入时会优先搜索当前工作目录,如果当前目录下有个pandas.py,import pandas导入了这个文件,而不是真正的库。解决办法很简单,把自定义的pandas.py改个名字,删掉缓存目录__pycache__,再重启解释器。

排查这种东西,切记不要一上来就卸载重装。先看报错来自哪一行,导入的是哪个文件,再看项目中是否有同名文件,最后才考虑环境问题。这个顺序能省下很多时间。

3. 数据清洗:从脏数据到干净DataFrame的四个关键步骤

3.1 读取数据:CSV、Excel到DataFrame

数据清洗的第一步是读数据。日常最常见的就是CSV和Excel。

# 读CSV df = pd.read_csv('sales.csv', encoding='utf-8') # 读Excel df = pd.read_excel('sales.xlsx', sheet_name='订单明细')

读文件时有两个容易被忽视的参数:一个是encoding,另一个是parse_dates。CSV文件经常因为编码问题乱码,一般用utf-8或gbk交替试;如果文件里有日期列,最好在读入时指定parse_dates,让Pandas自动转成datetime类型,而不是事后用字符串处理。

Excel读取也有坑。如果你的Python环境没装openpyxl或xlrd,read_excel会报错。手动补一下:

pip install openpyxl

正式跑项目前,建议先读一个nrows做抽样:

pd.read_csv('sales.csv', nrows=100)

看一眼列名和数据类型是否符合预期,再全量读取。对于千万级的大文件,这一步能避免读入后发现结构不对、重新加载的尴尬。

3.2 缺失值与重复值:先统计策略,再动手清理

拿到数据后,先别急着dropna。我见过很多人上来就把含空值的行删掉,结果把有效数据也删了一半。正确做法是先做统计:

# 查看每列缺失值数量 print(df.isna().sum()) # 查看重复行数量 print(df.duplicated().sum())

然后根据业务场景决定策略:

  • 关键字段(比如订单号)为空,直接删除,因为后续无法使用;
  • 数值字段为空,可以用均值/中位数填充,或者用前后填充法;
  • 分类字段为空,可以填"未知"或众数;
  • 整行重复,直接drop_duplicates()去重。
# 删除订单号为空的记录 df = df.dropna(subset=['订单号']) # 销售额空值用中位数填充 df['销售额'] = df['销售额'].fillna(df['销售额'].median()) # 删除完全重复的行 df = df.drop_duplicates()

填充缺失值的逻辑要写清楚理由。比如销售额用中位数,是因为销售额分布往往右偏,用均值会被极值拉高,中位数更稳健。

3.3 数据类型转换:让每一列都"各归其位"

Pandas里最烦人的错误之一就是类型不匹配。看数据明明全是数字,但一算sum就报错,因为存储类型是字符串。所以读取后第一件事就是检查dtypes。

print(df.dtypes)

看到object类型但内容其实是数字的列,要转换类型:

df['销售额'] = pd.to_numeric(df['销售额'], errors='coerce') df['日期'] = pd.to_datetime(df['日期'], format='%Y-%m-%d') df['门店'] = df['门店'].astype('category')

用to_numeric而不是直接astype(float),是因为它会给你一个兜底:errors='coerce'能把无法转换的值变成NaN,之后再统一处理。如果直接astype,一个脏字符就能让整个程序崩溃。

这里有个非常重要的顺序问题:先转换类型,再处理缺失值。因为转换过程中可能产生新的NaN(比如字符串里混入了"null"或"暂无"),如果先fillna再转换,新产生的缺失就漏掉了。

日期类型的转换也值得单独说。有时候日期列是"2024/03/15"或"20240315"这种格式,直接to_datetime可能会解析失败,建议指定format参数,既提速又避免歧义。

3.4 列名清洗与索引重置

列名的问题在真实数据里特别常见,比如列名带着空格、大小写不统一、有的叫"门店"有的叫"门店名称"。如果不统一列名,后面写筛选条件时非常容易踩坑。

# 去掉列名首尾空格,统一小写 df.columns = df.columns.str.strip().str.lower() # 重命名个别列 df = df.rename(columns={'门店名称': '门店'})

索引经过各种筛选和清洗后会变得很乱,通常复位一下:

df = df.reset_index(drop=True)

reset_index有两个作用:一是把旧的索引变成普通列,二是把连续但不齐整的索引恢复成0开始的顺序。drop=True表示不用保留旧索引。这一步看起来不起眼,但对后续分组和拼接操作影响很大。

4. 数据分析实操:筛选、分组聚合与透视表

4.1 条件筛选与布尔索引

清洗干净之后,终于可以开始分析数据了。最常用的操作是条件筛选。

# 筛选销售额大于5000的记录 high_sales = df[df['销售额'] > 5000] # 多条件筛选 target = df[(df['门店'] == 'A店') & (df['品类'] == '数码')]

这里有两个容易出错的地方。第一,多个条件必须用括号括起来,否则Python的运算符优先级会出错;第二,逻辑与是&,逻辑或是|,不是Python里的and和or。Pandas的布尔索引要求的结果是数组级别的逻辑运算,所以用位运算符。

筛选之后不要忘了reset_index。

4.2 groupby分组聚合:Excel透视表的平替

从"按门店统计销售额"这种需求开始,就要用到groupby了。

# 按门店分组,计算销售额总和 sales_by_store = df.groupby('门店')['销售额'].sum()

这个操作的结果是一个Series,门店变成了索引。通常我希望把它变成规整的DataFrame:

sales_by_store = df.groupby('门店')['销售额'].sum().reset_index()

groupby之后reset_index几乎是每个数据分析师都写的代码,目的就是让分组字段恢复成普通列,方便后续操作或者导出到Excel。

如果要对多个维度同时聚合,可以用列表:

summary = df.groupby(['门店', '品类']).agg({ '销售额': 'sum', '成本': 'sum', '订单号': 'count' }).reset_index()

agg函数允许用字典指定每个字段的聚合方式,非常灵活。除了内置的sum、mean、count、max、min之外,还可以传lambda自定义函数。

4.3 用pivot_table实现更复杂的透视分析

groupby能解决大部分问题,但如果你要做一个行列交叉的透视表,比如行是门店、列是品类、值是销售额,用pivot_table会更直观。

pivot = pd.pivot_table( df, index='门店', columns='品类', values='销售额', aggfunc='sum', fill_value=0 )

pivot_table相比groupby有个明显优势:会自动把缺失的组合填成NaN,再通过fill_value参数填0,这样透视表看起来和Excel里的一模一样。加一行reset_index()可以把它转回长格式数据。

这里有一个业务上的细节:透视表方便人看,但机器处理时往往需要"长表"(每一行是一个唯一的组合)。所以做分析时我倾向于先groupby搞出长表,最后展示时再pivot。数据和展示分离,代码更清晰。

5. 可视化:从Pandas内置绘图到Matplotlib定制

5.1 为什么先学Pandas自带的绘图

Pandas的DataFrame有plot方法,底层是Matplotlib,但语法更简洁。对于快速查看数据分布、做个临时报表,完全够用。

import matplotlib.pyplot as plt # 设置中文字体,避免乱码 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 折线图 df.groupby('日期')['销售额'].sum().plot(kind='line', figsize=(10, 5)) # 柱状图 df.groupby('门店')['销售额'].sum().plot(kind='bar') # 饼图 df.groupby('品类')['销售额'].sum().plot(kind='pie', autopct='%.1f%%') plt.show()

Pandas的plot会根据数据类型自动设置x轴,比如日期字段会自动排列,Series的索引变成x轴,值变成y轴,这个默认行为非常适合做时序分析。

5.2 常用图表的场景选择

很多人做可视化时经常纠结该用哪种图。我用下面的表做了一个归纳,也就是Excel数据分析里经常提到的那批常用图表,Pandas和Matplotlib基本都能覆盖。

图表类型适用场景Pandas实现
折线图连续时间序列趋势df.plot(kind='line')
柱状图分类对比df.plot(kind='bar')
条形图分类名称较长时横向对比df.plot(kind='barh')
饼图占比结构df.plot(kind='pie')
直方图数值分布df['销售额'].plot(kind='hist', bins=20)
箱线图离群点和分位数分布df.boxplot(column='销售额')
散点图两个数值变量的关系df.plot(kind='scatter', x='成本', y='销售额')
面积图累积趋势df.plot(kind='area')

选图表的核心逻辑是:看清比较关系用柱状,看趋势用折线,看分布用直方图,看占比用饼图,看相关性用散点图。不要在饼图里塞超过5个品类,否则视觉上一团糟。

5.3 中文字体和可视化大屏的边界

用Pandas绘图时,最让人抓狂的就是中文字体乱码。设置方式就是前面写过的两行rcParams。注意SimHei是Windows自带的黑体;Mac和Linux上没有,需要改为'PingFang SC',或者下载一个中文字体文件注册进去。

还有一个常见问题:负号在默认字体下会显示成方块。所以必须把axes.unicode_minus设为False。

热搜词里出现了"可视化大屏"。这里要明确一点:Pandas不是做大屏的工具,Pandas负责出数据和基础图表,大屏通常用专门的Web可视化方案(比如ECharts),从Pandas导出数据或JSON,前端渲染。所以如果你要做指挥中心那种炫酷大屏,把精力放在数据格式转换和接口设计上,而不是在Pandas里硬画。

6. 一个完整实战:从清洗到图表的全流程

6.1 场景设定与数据说明

为了把前边的内容串起来,我用一个模拟销售数据来讲。假设有文件sales.xlsx,字段包括:订单编号、销售日期、门店、品类、销售额、成本、数量。

初始数据存在几个典型问题:

  • 销售日期有多行是文本格式,如"2024/3/15 0:00";
  • 销售额列混入了"NaN"字符串;
  • 存在完全重复的订单行;
  • 门店列有一些前后带空格的值。

6.2 完整清洗与数据分析代码

下面是一段可直接运行的脚本:

import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 读取数据 df = pd.read_excel('sales.xlsx', sheet_name='订单明细', parse_dates=['销售日期']) # 查看数据概览 print("数据形状:", df.shape) print("缺失值分布:\n", df.isna().sum()) print("重复行数:", df.duplicated().sum()) # 数据清洗 df = df.dropna(subset=['订单编号']) df['销售日期'] = pd.to_datetime(df['销售日期'], errors='coerce') df['销售额'] = pd.to_numeric(df['销售额'], errors='coerce') df['成本'] = pd.to_numeric(df['成本'], errors='coerce') df['数量'] = pd.to_numeric(df['数量'], errors='coerce') df['门店'] = df['门店'].str.strip() df['品类'] = df['品类'].str.strip() df = df.drop_duplicates() # 再处理清洗过程中产生的新缺失值 df = df.dropna(subset=['销售日期', '销售额']) # 新增毛利率列 df['毛利率'] = (df['销售额'] - df['成本']) / df['销售额'] # 分组统计 store_summary = df.groupby('门店').agg({ '销售额': 'sum', '成本': 'sum', '订单编号': 'count' }).reset_index() store_summary.columns = ['门店', '总销售额', '总成本', '订单数'] # 透视表:门店 x 品类 pivot_sales = pd.pivot_table( df, index='门店', columns='品类', values='销售额', aggfunc='sum', fill_value=0 ) print(store_summary) print(pivot_sales) # 可视化 fig, axes = plt.subplots(1, 2, figsize=(14, 5)) # 柱状图:门店总销售额 store_summary.plot(kind='bar', x='门店', y='总销售额', ax=axes[0], legend=False) axes[0].set_title('各门店总销售额') axes[0].set_ylabel('销售额') # 按日期的销售额趋势 daily_sales = df.groupby('销售日期')['销售额'].sum() daily_sales.plot(kind='line', ax=axes[1]) axes[1].set_title('销售趋势') axes[1].set_ylabel('销售额') plt.tight_layout() plt.show()

6.3 结果解读与业务价值

跑完这段代码,你就能得到三个核心产出:清洗后的完整DataFrame、各门店经营汇总、按门店和品类的透视矩阵,以及两张可以直接放进周报的图。

从业务角度看,柱状图能直观看见哪个门店贡献最高;趋势图能看出是否存在明显的周末效应或节假日前后的需求爬升;透视矩阵能帮你发现某个品类在特定门店卖得好。这些结论如果用Excel手动透视,每换一个维度都要拖半天,脚本化之后只需要改一下参数重跑。

有一点要提醒:Pandas输出的图是静态图,适合做分析报告,但不适合做实时监控。如果需要周报自动化,可以把plt.savefig保存图片,再用脚本拼接到Word或PDF里。这里用到的是一个All in One的思路:清洗、分析、图表全部跑一遍,数据更新后只需要重新运行脚本。

7. 我踩过的坑和一些实用建议

7.1 链式赋值和SettingWithCopyWarning

新手经常写这类代码:

df[df['门店'] == 'A店']['销售额'] = 100

Pandas会报一个SettingWithCopyWarning,意思是说你在一个临时副本上做修改,不会真正写回原DataFrame。这个警告非常坑,它不报错,只是警告,但你的修改实际上不生效。

我的建议是:尽量用.loc做条件赋值

df.loc[df['门店'] == 'A店', '销售额'] = 100

.loc接受两个参数,第一个是行筛选条件,第二个是列名,这样修改是直接在原数据上进行的,不会触发警告。

7.2 中文CSV导出乱码

清洗完数据要导出时,直接:

df.to_csv('result.csv', index=False)

在Excel里打开大概率乱码。原因是Excel默认用GBK编码打开CSV文件,而Pandas默认用UTF-8写出。解决办法是换成utf-8-sig,这个编码会在文件开头带上BOM标记,Excel识别后就能正常显示。

df.to_csv('result.csv', index=False, encoding='utf-8-sig')

7.3 大文件内存不足时怎么办

Pandas读大文件容易把内存撑爆。我处理几个G的日志文件时,最常用的方式是分块读取:

chunk_iter = pd.read_csv('big.csv', chunksize=100000) result = [] for chunk in chunk_iter: # 对每个块做清洗 chunk = chunk[chunk['金额'] > 0] result.append(chunk) df = pd.concat(result, ignore_index=True)

另一个技巧是只读取需要的列,通过usecols参数过滤掉无关字段,减少内存开销。如果数据量到了几十G,Pandas就比较吃力了,这时候要考虑Spark那套分布式方案,但那是另一个故事了。

7.4 代码可维护性:把清洗流程写成函数

我个人的一个体会是,如果你只写一次脚本,那无所谓;但如果这个数据每周都要分析一次,建议把清洗流程封装成函数,输入文件路径,输出干净DataFrame。

def load_and_clean(file_path): df = pd.read_excel(file_path) df['销售额'] = pd.to_numeric(df['销售额'], errors='coerce') df = df.dropna(subset=['订单编号']) df = df.drop_duplicates() return df

这样别人接手你的代码时,只需要看函数名和docstring就知道在干什么,而不是在一堆中间变量里找逻辑。数据分析脚本很容易变成一坨屎山,但如果每步都配合清晰的重命名和函数拆分,代码会好维护很多。

最后再分享一个小技巧,也是我写数据分析项目时最受益的习惯:每清洗完一步,记得print一下当前数据的shape和dtypes。不要等到最后才输出结果,否则中间某个操作把数据类型弄脏了,你根本不知道是哪一步搞坏的。借助这些小输出,排查问题时会非常有底。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 8:11:28

Delegatecall存储碰撞漏洞详解:从EVM存储布局到DeFi安全审计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 8:10:29

ESP32 RMT外设详解:从NEC协议到红外学习发射器实战

看看家里的电器遥控器,十个里面有八个是红外,电视、空调、机顶盒、风扇,全是那一枚小灯珠在前头闪。但你要是真拿示波器去量它的输出,会发现这玩意儿一点都不简单——一串宽度各异的脉冲,有的几百微秒,有的…

作者头像 李华