Python数据分析与可视化全流程:从数据清洗到图表呈现

发布时间:2026/10/11 15:45:37
Python数据分析与可视化全流程:从数据清洗到图表呈现
简介Python数据分析与可视化PDF教程面向Python基础用户与数据分析入门者内容聚焦数据分析与可视化的常用工具链以简洁的库梳理和代码示例为主线系统介绍Pandas、NumPy、SciPy、StatsModels等数据分析库以及Matplotlib、Seaborn、Plotly、Bokeh、Cufflinks等可视化库的适用场景。教程包含读取CSV、计算均值、绘制柱状图等实操演示方便读者快速上手常用工具并形成分析思路避免在入门阶段盲目翻阅零散文档。资源为单个PDF文件大小约114KB轻量便携适合作为日常查阅的速查手册也便于在移动设备上随时浏览。已有1848人学习下载说明其在入门阶段具备实用参考价值尤其适合希望快速了解Python数据分析生态并动手实践的读者示例代码清晰可复制有助于从数据清洗到可视化呈现快速走通流程。1. 数据分析与可视化的落地顺序先想清楚要回答什么问题拿到一份销售明细表时大部分人的第一反应是打开软件拖几个字段、拉一张柱状图出来看看。这个动作没有问题但如果你只是想把数据“画出来”用现成的报表工具更快甚至不用写代码。真正让你需要“python数据分析与可视化.pdf”这份资料组合的场景是数据不干净、字段不规整、结论需要复现、图表需要按固定口径批量产出。我见过太多人卡在同一个位置数据加载进来了却不知道该先处理哪一步图也画出来了但给不出一个能解释业务问题的结论。说白了数据分析不是把数据变成图而是把问题变成答案。这篇文章顺着一条完整的路径走从环境搭建开始到数据加载、清洗、聚合再到可视化和交付。每一步都会给你可复现的代码、参数设定逻辑和常见坑。适合两类人一类是刚接触数据分析、想跳过碎片化教程直接跑通全流程的初学者另一类是已经用Excel或报表工具处理数据、想用Python把分析流程固化下来的从业者。学完这套路径你会对“数据进来之后到底要经过哪些步骤才能变成一张能汇报的图”有非常具体的体感。2. 搭建分析环境与加载第一份数据选对库是省时间的第一步2.1 数据分析四件套pandas、numpy、matplotlib、seaborn 的分工Python做数据分析生态里可选的库很多但真正支撑起日常分析工作的核心组合就四个pandas负责表格数据的读取、清洗、聚合numpy提供底层的数组运算pandas的很多操作底层都依赖它matplotlib是绘图基础库所有图表最终都是它画出来的seaborn基于matplotlib封装专门解决统计图表的易用性问题一行代码就能画出带分布和回归线的组合图。除了这四件套你还可能需要openpyxl读写Excel、xlrd旧版Excel读取、plotly交互式图表和jupyter交互式开发环境。我建议第一次装环境就直接用一个命令把主要依赖装齐避免后面用到某个库时才发现缺东西。pip install pandas numpy matplotlib seaborn openpyxl jupyter装完之后强烈建议验证一下导入是否正常因为pandas和numpy的版本一旦冲突导入阶段就会报错而这类报错在后续分析中会浪费很多时间。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns print(pd.__version__) print(np.__version__) print(sns.__version__)这里几个参数值得说明openpyxl不是pandas必需的但没有它pd.read_excel()无法读取.xlsx文件jupyter虽然不是运行脚本必需的但做数据分析时逐行执行、随时看中间结果的工作方式效率远高于写完整脚本再运行。如果你用的是Anaconda发行版上述库已经预装无需重复安装。2.2 用 read_csv 和 read_excel 加载数据参数决定成败数据加载是整套流程的第一步也是最容易翻车的一步。很多人在这一步就卡住了不是因为文件路径写错而是因为编码、分隔符、表头这些参数没设置对。import pandas as pd df pd.read_csv( sales_data.csv, encodingutf-8, sep,, header0, parse_dates[order_date], dtype{user_id: str} ) print(df.head()) print(df.info())这几行代码背后有几个高频参数需要理解清楚。encoding参数用来指定文件编码最常见的坑是CSV文件实际是gbk编码但代码里写了utf-8结果一读就报UnicodeDecodeError。遇到这种情况把encoding改成gbk或gb18030基本就能解决。sep参数指定分隔符CSV文件默认是逗号但有些数据源用制表符\t或分号;。parse_dates把指定的列解析为时间类型这一步提前做了后面做时间序列分析就能直接用df.groupby(月份)这类操作。dtype参数用来指定列的数据类型比如用户ID看起来是纯数字但逻辑上不能参与加减运算就应该指定为字符串防止被pandas自动推断成数值类型。header0表示第一行作为列名如果文件没有表头要改成headerNone同时用names参数给列命名。加载完成后立刻执行df.head()和df.info()是必须养成的习惯前者看数据长什么样后者看每一列的类型、非空个数和内存占用。df.info()输出的Dtypes列非常关键如果明明应该是数值的列显示object说明数据里有非数值的脏值需要回到清洗阶段处理。2.3 加载不是终点先做一次字段级体检数据加载完成后不要急着做清洗或画图。先花两分钟做一次字段级体检确认每列的真实情况。这个步骤能提前发现数据质量问题避免在分析中途才发现底层数据是错的。print(df.shape) print(df.dtypes) print(df.isnull().sum()) print(df.describe())df.shape输出的是行数和列数记住这个数字后面每做一步清洗都回去对比防止误删数据。df.isnull().sum()统计每一列的缺失值数量它决定了你后续要用什么策略处理缺失。df.describe()输出数值列的统计量均值、标准差、最小值、四分位数、最大值。通过最大最小值可以快速发现异常值比如订单金额出现负数、日期列出现1900年这样的数据。体检还有一个容易被忽视的维度唯一值。df[user_id].nunique()可以查看用户ID有多少个不同值如果行数远大于唯一值数量说明同一用户有多条记录后续聚合分析时要考虑按用户去重还是按订单明细保留。这一步做完你对数据的基本盘就有了清晰的判断接下来进入清洗环节。3. 数据清洗与变换分析工作中最耗时、最体现功力的环节3.1 缺失值处理删除还是填充取决于业务含义缺失值是数据分析中最常见的问题但没有统一的处理方案。删除还是填充要结合业务背景判断。比如订单金额缺失这直接影响营收计算如果占比很低直接删除如果占比很高删除会导致样本量严重缩水要考虑填充或单独分桶。print(df.isnull().sum() / len(df)) df_cleaned df.copy() # 金额缺失且占比低于5%直接删除 df_cleaned df_cleaned.dropna(subset[order_amount]) # 用户年龄缺失用中位数填充 age_median df_cleaned[user_age].median() df_cleaned[user_age] df_cleaned[user_age].fillna(age_median) # 渠道来源缺失填unknown df_cleaned[channel] df_cleaned[channel].fillna(unknown)这里dropna(subset[order_amount])只判断order_amount这一列是否为空行内其他列的缺失不受影响。fillna(value)用于填充指定值可以传入一个标量值也可以传入一个Series实现逐行填充不同值。df.copy()这一步很有必要避免在原始DataFrame上直接修改后无法回退——清洗操作多轮迭代时保留原始数据就是你的后悔药。有个经验值得记住缺失率超过30%的列一般不建议填充更推荐做分箱处理。比如“设备型号”有大量缺失与其填unknown不如新生成一列叫device_is_known0表示缺失1表示有值把缺失信息本身变成特征。3.2 类型转换与字符串清洗把“看起来是数字”变成“真的是数字”加载数据时数值列常常被识别成object类型。原因多种多样金额带千分位逗号、百分比带百分号、日期有多种格式。要分析这些列必须先做类型转换和字符串清洗。df_cleaned[order_amount] ( df_cleaned[order_amount] .astype(str) .str.replace(,, , regexFalse) .str.replace(¥, , regexFalse) .astype(float) ) df_cleaned[order_date] pd.to_datetime( df_cleaned[order_date], format%Y-%m-%d, errorscoerce ) df_cleaned[user_age] pd.to_numeric(df_cleaned[user_age], errorscoerce)这里每行代码都有明确意图。.astype(str)先把列强制转成字符串防止列内混合类型导致后续str.replace报错。.str.replace(,, , regexFalse)去掉千分位逗号regexFalse表示按字面字符串匹配而不是正则表达式这一步对性能有好处也能避免正则特殊字符的干扰。¥符号同理电商平台的金额导出文件经常带货币符号。pd.to_datetime用于时间转换format参数指明确切的时间格式因为自动解析日期格式不仅慢还容易把2023-01-02误判成2023年1月2日还是2月1日。errorscoerce的作用是解析失败时置为NaT而不是抛异常。pd.to_numeric是数值转换的更安全方案配合errorscoerce无法转换的值变成NaN之后用fillna或dropna处理。做完这些转换后要再做一次df_cleaned.info()确认各列类型已经变成float64、datetime64等目标类型。类型转换是整套分析的地基地基没打牢后面的聚合结果一定是错的而且很难察觉。3.3 分组聚合与透视表从明细到结论的必经之路数据清洗完成后原始明细数据变成了一张可以信任的表。接下来要做的是把它从“每一行是一条记录”的粒度汇总成“每一行是一个分组结论”的粒度。这是数据分析中最核心的思维方式转变。monthly_sales ( df_cleaned .groupby(df_cleaned[order_date].dt.to_period(M)) [order_amount] .agg([sum, count, mean]) .reset_index() ) print(monthly_sales.head())这段代码的含义是按订单日期所在月份分组对order_amount列做三种聚合——求和、计数、平均。dt.to_period(M)把时间戳转换为月份周期比如2023-01-15变成2023-01这样就实现了按月分组的效果。.agg([sum, count, mean])同时返回三个聚合结果避免分组后多次计算遍历全表。.reset_index()把分组键从索引降级为普通列否则后续用monthly_sales[order_date]取不到数据。如果要做多维度的交叉分析比如不同渠道、不同月份的销售额对比用pivot_table更直观。pivot pd.pivot_table( df_cleaned, indexchannel, columnsdf_cleaned[order_date].dt.to_period(M), valuesorder_amount, aggfuncsum, fill_value0 ) print(pivot.head())index参数是透视表的行维度columns是列维度values是聚合对象aggfunc是聚合函数。fill_value0把没有数据的交叉位置从NaN填充为0这样后续做柱状图时不会因为缺失值导致图表出现空隙。这段代码跑完你会得到一张“行是渠道、列是月份、单元格是销售额”的透视表这种结构非常适合丢给可视化库直接画图。4. 可视化呈现从草稿到可交付的图表演进4.1 图表选型先定分析目标再选图表类型数据可视化领域有一个高频错误先看图库示例觉得哪个好看就画哪个。正确的顺序是先明确这张图要回答什么问题。要回答“随时间变化的趋势”用折线图要对比不同类别的数值大小用柱状图要展示两个连续变量的关系用散点图要看数据的分布形态用直方图或者seaborn的kdeplot画核密度曲线。matplotlib是绘图底层任何图表都能画但代码量大、默认样式老旧。seaborn在底层之上做了封装尤其擅长统计图表。我的习惯是探索阶段用seaborn快速画图交付时如果对样式有自定义要求再用matplotlib细调。import seaborn as sns import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False sns.set_theme(stylewhitegrid, palettemuted)font.sans-serif指定中文字体这是Python绘图最容易踩的坑之一默认字体不支持中文画出来全是方块。如果你在Linux服务器上跑代码且没有安装SimHei用Noto Sans CJK SC替代。sns.set_theme(stylewhitegrid, palettemuted)设置统一风格避免每张图单独调网格线的颜色。4.2 三张高频图月度趋势、渠道对比、金额分布先画最常用的月度销售额趋势图。用seaborn的relplot画折线图可以同时展示多个分组。plt.figure(figsize(12, 6)) sns.lineplot( datamonthly_sales, xorder_date, ysum, markero, linewidth2 ) plt.title(月度销售额走势) plt.xlabel(月份) plt.ylabel(销售额) plt.xticks(rotation45) plt.tight_layout() plt.show()markero让数据点用实心圆标出便于观察每个月的具体数值。rotation45解决月份标签重叠的问题。tight_layout()自动调整子图参数让标题和坐标轴标签不超出画布范围。这张图画完月度趋势的涨跌一目了然。第二张图对比不同渠道的销售额。用柱状图并按月份分组形成分组柱状图。pivot_for_plot pivot.reset_index() pivot_melted pivot_for_plot.melt( id_varschannel, var_namemonth, value_namesales ) plt.figure(figsize(12, 6)) sns.barplot( datapivot_melted, xmonth, ysales, huechannel ) plt.title(各渠道月度销售额对比) plt.xticks(rotation45) plt.tight_layout() plt.show()pivot.reset_index()把透视表的索引转回列melt把宽表转成长表——这是seaborn绘图的标准步骤因为seaborn的数据接口期望长表格式每一行是一个观测列是维度编码和数值。长表格式是seaborn所有绘图函数的通用前提它让你不需要为每种图表重写数据转换逻辑。到这里你可能会发现画图本身并不难大部分时间花在了数据的格式转换上。第三张图看订单金额的分布用直方图加核密度曲线。分布情况能帮你发现异常值所在的区间也能判断金额数据是否服从常见的偏态分布。plt.figure(figsize(10, 6)) sns.histplot( df_cleaned[order_amount], bins50, kdeTrue, colorsteelblue ) plt.title(订单金额分布) plt.xlabel(订单金额) plt.ylabel(频数) plt.show()bins50控制直方图分桶数量这直接影响分布曲线的细节程度。kdeTrue叠加核密度曲线让分布的连续形态更清楚。如果看到金额分布有一个极长的右尾说明存在少数高额订单拉高了均值这时候做月度汇总时除了看总和还应该关注中位数。4.3 多子图布局一页看完多个维度的探索技巧做探索性分析时把多张图拼在一页上效率远高于逐张单独展示。matplotlib的subplots函数负责布局画布seaborn的Axes对象绘制内容。fig, axes plt.subplots(2, 2, figsize(14, 10)) # 第一张月度销售额趋势 sns.lineplot(datamonthly_sales, xorder_date, ysum, axaxes[0, 0]) # 第二张渠道占比饼图 channel_sum df_cleaned.groupby(channel)[order_amount].sum() axes[0, 1].pie( channel_sum.values, labelschannel_sum.index, autopct%.1f%%, startangle90 ) # 第三张用户年龄分布 sns.histplot(df_cleaned[user_age], bins30, kdeTrue, axaxes[1, 0]) # 第四张销售额与用户年龄散点图 sns.scatterplot( datadf_cleaned, xuser_age, yorder_amount, alpha0.5, axaxes[1, 1] ) plt.tight_layout() plt.show()subplots(2, 2)生成一个两行两列的画布返回fig画布对象和axes一个二维数组每个元素是一个子图对象。axaxes[0, 0]参数把seaborn的图绘制到指定的子图上。alpha0.5让散点呈半透明数据点密集区域的颜色更深能直观反映分布密度。autopct%.1f%%控制饼图显示的百分比格式。多子图布局在探索阶段非常实用它把数据的不同侧面放在同一视线内方便你快速发现维度之间的关联。5. 数据分析避坑指南五个最常见的翻车点与排查方法5.1 中文乱码与负号显示异常现象图表标题、坐标轴标签全部显示为方块或者图表中负号显示为乱码。原因matplotlib默认字体不含中文字形且默认使用ASCII字符渲染负号中文字体未生效时负号也会异常。解决在绘图代码前设置plt.rcParams[font.sans-serif] [SimHei]解决中文显示设置plt.rcParams[axes.unicode_minus] False解决负号问题。如果是在服务器环境运行需要先确认系统已安装中文字体用fc-list :langzh查看。没有中文字体时可以用Noto Sans CJK SC替换。5.2 CSV文件的隐秘编码问题现象pd.read_csv()打开文件直接报UnicodeDecodeError或者打开后表格内容全是乱码。原因文件实际编码与代码指定的编码不一致。Windows环境导出CSV默认使用gbkmacOS和Linux默认utf-8跨平台传输文件时极容易发生这种情况。解决先用文本编辑器打开CSV文件确认编码再在read_csv中指定正确的encoding参数。如果文件来自Windows且经常传输最稳妥的做法是统一转成utf-8-sig编码保存——这比utf-8多一个BOM头但好处是Excel直接打开不会乱码pandas读取时也不需要额外参数。如果已经报错别急着换编码用errorsignore先读取部分数据看一眼再回头判断正确编码。5.3 日期时间列被解析成字符串现象明明用pd.to_datetime()转换过日期列但后续df.groupby(df[date].dt.month)仍然报错AttributeError: Series object has no attribute dt。原因pd.to_datetime()返回的是一个新的Series如果你没有把它赋值回原列原列仍然是字符串类型。解决转换后必须赋值回原列即df[order_date] pd.to_datetime(df[order_date])。还有一种隐蔽情况列内混入了无法解析的日期字符串pd.to_datetime默认会抛异常但如果你先用了errorscoerce无法解析的值会变成NaT后续按月聚合时这些行会落入NaN分组。遇到这种情况先看一下df[df[order_date].isnull()]筛选出这些异常行检查原始数据格式。5.4 pandas链式赋值警告现象代码执行时出现SettingWithCopyWarning但结果看起来是对的于是一路忽略直到某天分析结果莫名其妙不对。原因pandas在执行链式赋值时无法确定你操作的是原始DataFrame的视图还是拷贝。比如df[df[amount] 100][amount] 200这一行代码前半部分筛选出子集后半部分对子集赋值但pandas可能操作的是临时拷贝赋值最终没有写入原始DataFrame。解决统一使用.loc进行条件赋值df.loc[df[amount] 100, amount] 200。更稳妥的做法是遵循“先拷贝再修改”的原则df_copy df.copy()在拷贝上做所有变换操作。这个习惯能帮你避免绝大部分赋值相关的隐性问题。5.5 透视表聚合结果与手动计算不一致现象用pivot_table算出的合计值和用Excel手工透视表算出的不一样。原因大多数情况是索引或列中存在重复项。pivot_table默认对重复项执行aggfunc指定的聚合操作如果你不指定aggfunc默认是mean而Excel透视表的默认行为是求和——同样的操作不同的默认值结果自然对不上。解决明确指定aggfuncsum并在透视前检查重复项df_cleaned.duplicated().sum()。如果重复项多先做去重df_cleaned.drop_duplicates()。数据量大时重复项引起的结果偏差会被进一步放大所以检查重复项应该作为透视操作的前置固定步骤。6. 把分析流程固化成脚本面向复用的进阶技巧分析做完一轮之后如果下次拿到新数据还要重新执行这些代码效率就太低了。进阶的用法是把清洗、聚合、可视化的流程封装成模块化的函数让整套分析流水线可以一键重跑。def load_and_clean(filepath): df pd.read_csv(filepath, encodingutf-8) df[order_amount] ( df[order_amount].astype(str) .str.replace(,, , regexFalse) .astype(float) ) df[order_date] pd.to_datetime(df[order_date], errorscoerce) df df.dropna(subset[order_amount]) return df def monthly_summary(df): return ( df.groupby(df[order_date].dt.to_period(M)) [order_amount] .agg([sum, count, mean]) .reset_index() ) def plot_monthly_trend(summary_df, output_pathNone): plt.figure(figsize(12, 6)) sns.lineplot(datasummary_df, xorder_date, ysum, markero) plt.title(月度销售额走势) plt.xticks(rotation45) plt.tight_layout() if output_path: plt.savefig(output_path, dpi150) plt.show() df load_and_clean(sales_data.csv) summary monthly_summary(df) plot_monthly_trend(summary)把这些函数放在同一个脚本文件中下次拿到同结构的新数据只需要替换文件路径整条流程自动重跑。这里有一个我自己反复用的习惯所有绘图步骤都预留output_path参数分析完成时直接把图保存为PNG文件而不只是弹出一个窗口。保存图片用plt.savefig(output_path, dpi150)dpi参数决定图片清晰度150适合屏幕演示300适合打印。这样数据更新后图表文件也能自动更新你不用在汇报前临时改图。这个沉淀过程还有一个值得投入的方向把常用的聚合逻辑固化成模板比如“月度汇总”“渠道对比”“新老客户分析”每个模板包含固定的清洗规则、聚合逻辑和图表配置。这套模板覆盖了日常分析里超过60%的重复场景每周至少能省出半天工时。我做数据分析最大的教训就是第一次分析时把过程写成函数比第二次复制粘贴改路径再东拼西补要省时间得多。希望这些经验能帮到你。本文还有配套的精品资源点击获取