资讯动态

Python数据科学入门:从数据管道到Pandas数据清洗实战

发布时间:2026/10/20 10:53:24 来源:尧图企业网站定制
1. 为什么我建议从数据管道的角度入门而不是直接啃语法很多初学者学 Python 数据科学拿到手的通常是这么一条路先学基础语法、再学 NumPy、再学 Pandas、然后是可视化、最后接触机器学习。这套路本身没错但我带过几轮新人之后发现真正让人坚持不下去的恰恰是学了一堆语法却不知道自己能干什么。我建议换个切入点——把 Python 数据科学当成一条数据管道来看。数据从哪来、怎么读进来、怎么整理干净、怎么分组统计、怎么转成图表、怎么输出结论这是一个完整闭环。你不需要先精通所有语法再动手而是顺着这条管道走一遍用到什么学什么。这篇文章是第一篇我先把这条管道的入口部分讲透环境、数据读取、数据结构基础、以及第一个完整的小项目闭环。这篇内容适合什么人完全没写过 Python 的纯新手可以照着操作学过 Python 但对数据分析没概念的后端开发也能在这里补齐用数据说话的思维。我会尽量少讲纯理论多放能直接抄的代码和踩坑记录。2. 环境搭建别在安装这一步浪费超过二十分钟2.1 用 Anaconda 还是纯 Python 加 pip这是新人问得最多的问题。我的答案是做数据科学相关的事情直接装 Anaconda省心。它是 Python 发行版里面预装了大量数据科学库最核心的价值是 conda 可以帮你处理库与库之间的依赖关系不会出现装 A 库把 B 库搞崩的情况。纯 Python 加 pip 的方式不是不行但对于入门阶段的人来说版本冲突是最大的隐形杀手。我见过不止一个人折腾两天装环境最后连 matplotlib 都导入不了原因就是 NumPy 版本跟其他库不兼容。如果你非得用纯 Python记住一个原则创建虚拟环境再加库别往全局环境里塞东西。安装完成之后打开终端Windows 用 Anaconda Prompt 或 PowerShellmacOS/Linux 用自带的终端输入python --version检查一下版本。如果有多个 Python 版本共存的环境建议用python -V和pip -V确认下当前用的是哪个。2.2 准备项目目录和第一个文件我习惯每个小项目单独建一个文件夹里面放数据文件和脚本文件。比如data-science-beginner/ ├── data/ │ └── sales.csv └── notebooks/ └── first_analysis.ipynb你可以用 Jupyter Notebook 写代码也可以用 VS Code 搭配 Python 插件。我个人推荐新手先从 Jupyter Notebook 开始因为可以一格一格地运行代码随时看到中间结果交互感强特别适合探索数据。但要注意不要只会在 Notebook 里写代码我后面会讲怎么把探索用的脚本转成正式脚本。提示如果用的是 Jupyter第一次运行某个库之前先等几秒首次导入会加载依赖之后再运行就快了。这不是卡住别急着关。3. 数据从哪来读取 CSV 是第一堂必修课3.1 真实世界里大部分数据都不是完美格式打开一个 CSV 文件你会看到什么通常情况下不是教科书里那种干干净净的表格而是满屏的幺蛾子列名带中文、行尾有多余逗号、中间有空行、日期格式是2024/05/06和2024-05-06混用的某些单元格里还带着换行符。这才是真实数据的常态。Pandas 的read_csv函数是读取这类文件的主力工具。它的强大之处在于支持非常多的参数来应对混乱情况。我先把最常用的几个参数列出来后续会结合例子讲import pandas as pd df pd.read_csv( data/sales.csv, encodingutf-8, sep,, skipinitialspaceTrue, parse_dates[日期], dtype{数量: int64} )参数作用什么时候用encoding指定文件编码遇到UnicodeDecodeError时试gbk或utf-8-sigsep分隔符文件用\t分隔时改为sep\tskipinitialspace忽略分隔符后多余空格常见于手工编辑过的 CSVparse_dates将某列解析为日期类型后续需要按时间排序或按月统计时dtype强制指定列的数据类型防止 ID 列被读成数值导致丢前导零skiprows跳过文件开头若干行文件前几行有说明性文字时我见过有人强烈推荐先读进来再慢慢处理列类型从效率上讲这是最省事的方式但如果你明确知道某些列的类型在读取时指定dtype会省去后面不少麻烦。比如订单号001235如果不指定dtypePandas 会把它读成整数1235前导零直接没了等你反应过来已经丢了一批数据。3.2 先用info()和head()感知数据读进来之后别急着分析先看一眼数据长什么样print(df.head()) print(df.info())head()看前五行info()看每列的类型、非空数量、内存占用。这两步能让你快速发现明显的异常列数对不对、数据量多大、有没有整列是空值、日期列是不是被读成了字符串。我踩过的一个坑某次读一个销售明细表info()显示count为 1.8 万行但日期列非空数量只有 1.2 万马上就意识到有大量日期缺失。后面 [[section 5|清洗的时候花了很多时间]] 才补上如果当时读取时就用parse_dates配合keep_default_na控制空值识别能省不少事。从这次之后我养成了习惯读取之后第一件事永远是体检。4. DataFrame 的核心操作结构与筛选别急着学全部方法4.1 用一组例子理解行列定位逻辑DataFrame可以简单理解成带标签的表格既有行索引又有列名。这里最核心的概念是按标签定位和按位置定位的区别。用loc是按标签名字取用iloc是按整数位置取。# 按标签取行索引为 0~4 的行取数量列 rows_by_label df.loc[0:4, 数量] # 按位置取前 5 行第 2 到第 4 列 rows_by_position df.iloc[0:5, 1:4]这两个方法对应的逻辑差异很容易被忽略loc[0:4]是包含5 这个边界位置的iloc[0:5]也是取前 5 行但如果你混用内心想的是从第一行到第五行嘴里写的是iloc[0:4]就会漏掉一行。所有 DataFrame 操作中我建议入门期把loc、iloc、at、iat这四个搞清楚就够了不需要急着学query()、isin()这些进阶筛选。筛选的核心是三个概念行是条件筛选列是字段提取值是对应条件的取值。把这三个概念记住剩下的方法无非是换写法。4.1.1 条件筛选的秘密先说条件筛选它背后的逻辑是布尔索引。你写df[df[数量] 100]实际上分成两步首先df[数量] 100生成一个布尔序列长度跟行数一致True的位置表示满足条件然后 DataFrame 像筛子一样只保留布尔序列中True对应的行。理解了这个机制复杂的多条件组合就不会乱。# 多条件筛选数量大于 100 且单价小于 50 的行 condition (df[数量] 100) (df[单价] 50) filtered df[condition]两个细节新手经常踩一是多个条件必须用括号括起来不然的优先级会出问题二是且用或用|跟 Python 原生的and、or不能混用。原生操作符会尝试对整个 Series 做布尔判断结果就是报错或者返回一个让人摸不着头脑的标量。4.2 分组聚合不是groupby一句话那么简单groupby是 Pandas 里最常用也最容易被误解的 API。初学时以为它跟 Excel 的数据透视表一样拖两个字段就出结果其实它的运行机制是拆分-应用-合并三步先按某个列的值把数据拆成若干小组再对每个组应用聚合函数最后把结果合并回一个表。# 按商品类别分组求数量总和 summary df.groupby(商品类别)[数量].sum()注意这里[数量]是必须的它告诉 Pandas分组之后只对这个列做聚合。如果直接写df.groupby(商品类别).sum()Pandas 会对所有数值列都求和如果你表格里恰好有个列是订单号这种数值型的伪数值结果里就会多一个毫无意义的求和列。更进一步的用法是多个聚合函数一起做summary_multi df.groupby(商品类别).agg( total_qty(数量, sum), avg_price(单价, mean), max_qty(数量, max) )agg配合元组列表的写法在后期做业务报表时几乎天天用到。它能一次把不同列用不同函数处理完而且新列名字自己起。入门阶段把这一个函数吃透比死记十几个sum()、mean()、count()的调用方式更有效。5. 数据清洗入门真实项目中大部分时间都花在这里5.1 空值处理的三条原则数据清洗的脏数据来源非常集中第一就是空值。面对空值你应该先想三件事这个字段是否允许为空这一行是否还有保留价值这个字段的缺失值是否可以通过规则推导补全这三个问题的答案决定了处理策略。处理方式对应代码适用场景直接丢弃含空值的行df.dropna()空值占比很小且该行其他字段价值有限按常量填充df[列名].fillna(未知)分类字段的空值本身也是一种信息按均值/中位数填充df[列名].fillna(df[列名].median())数值型字段分布比较集中时用中位数更稳注意dropna()默认是所有列都非空才保留如果你想只要某列非空就保留要写成df.dropna(subset[订单号])。这个参数别说新手老手有时候也会忽略结果把明明还有用的行误删了。5.2 重复值比空值更难缠重复值分两种完全重复的行和关键列重复但其他列不同的行。前者用df.drop_duplicates()直接去重后者要先把判断是否重复的列名传给subset参数然后自己决定保留下哪一条。# 按订单号和商品ID判断重复保留最后一条记录 df_dedup df.drop_duplicates(subset[订单号, 商品ID], keeplast)keep除了last还有first和False。False会把所有重复的行全部干掉的如果数据本身就需要保留一处就别用False。这个参数的语义在实际使用中一定要先验证再大批量执行不然数据量大的时候你根本发现不了误删。5.3 数据类型清洗字符串和日期的坑从 CSV 读进来的数据默认都是字符串。你看着 Excel 里是数字读进 Pandas 它也显示数字但dtype可能还是object这样后面算平均值会报错。# 清洗时统一转数值类型coerce 让无法转换的变成 NaN df[售价] pd.to_numeric(df[售价], errorscoerce) df[日期] pd.to_datetime(df[日期], errorscoerce, format%Y-%m-%d)这一个习惯能帮你提前排查掉 80% 的为什么结果不对问题。errorscoerce的意思是转换失败的置为 NaN然后你可以再用空值处理的方法去处理它们不会因为某个脏数据导致整个脚本崩溃。6. 防御式编程思维让数据管道跑得更稳6.1 断言就是给数据管道装保险丝处理数据的过程中时不时要停下来验证一下当前的数据状态是否符合预期。Python 的断言语句assert在这里特别好用条件不满足就会抛出异常让你立刻知道哪步出了问题。# 检查清洗后没有空值 assert df[订单号].notna().all(), 有订单号为空请检查清洗逻辑 # 检查去重后最小订单数大于 0 assert len(df) 0, 数据为空后续操作无意义有人会觉得断言会拖慢速度实际上对于数据分析场景这点开销完全值得。它相当于在流水线关键节点装了安全阀门数据不对就停下来而不是带着问题跑到底最后输出一个看起来正常实际全是错的结论。6.2 用管道的每一段只做一件事来设计代码我见过很多新人把整个数据流程写成一长串操作二十行代码挤在一起中间任何一步出错都很难定位。更好的做法是划分成几个函数每个函数负责一段逻辑读取、清洗、统计、可视化各管各的。def load_data(path: str) - pd.DataFrame: return pd.read_csv(path, encodingutf-8) def clean_data(df: pd.DataFrame) - pd.DataFrame: df_clean df.dropna(subset[订单号]) df_clean[售价] pd.to_numeric(df_clean[售价], errorscoerce) return df_clean def summarize(df: pd.DataFrame): return df.groupby(商品类别)[数量].sum()这样的结构有三个好处每段可以单独调试后续数据源变了只需改load_data统计逻辑变了只需改summarize。四个模块独立进化互不干扰。提示函数参数的: str和- pd.DataFrame这种是类型注解Python 本身不做强制检查但配合 IDE 写代码时提示特别友好也能让后来的读者一看就知道该传什么类型。建议从入门第一天就养成写的习惯。7. 把第一个读取-清洗-统计-可视化闭环跑通7.1 自建一箱脏数据练手纸上谈兵没意义我提供一个模拟的销售数据生成脚本故意混入各种真实世界的脏数据。你可以建立文件make_dirty_data.py运行一次生成或者直接在 Jupyter 里跑import pandas as pd import numpy as np np.random.seed(42) data { 订单号: [A001, A002, A003, A003, A005], 商品类别: [食品, 数码, 食品, 数码, 日用], 数量: [3, 2, 0, 5, 1], 单价: [12.5, 399, 15, 待定, 8.9], 日期: [2024-05-01, 2024-05-01, 2024/05/03, 2024-05-03, 2024-05-04] } # 故意混入空值 df_raw pd.DataFrame(data) df_raw.loc[3, 单价] np.nan df_raw.to_csv(data/sales_demo.csv, indexFalse, encodingutf-8)这份数据里既有重复订单 A003又有字符串类型的数字、中文脏数据还有日期格式混用几乎把前面提到的坑都集齐了。7.2 完整代码16 行完成从读到图表import pandas as pd import matplotlib.pyplot as plt # 1. 读取 df pd.read_csv(data/sales_demo.csv, encodingutf-8) # 2. 清洗 df[单价] pd.to_numeric(df[单价], errorscoerce) df[日期] pd.to_datetime(df[日期], errorscoerce) df df.dropna(subset[单价]) df df.drop_duplicates(subset[订单号]) # 3. 过滤掉数量为 0 的无效记录 df df[df[数量] 0] # 4. 统计各品类销售额 df[销售额] df[数量] * df[单价] summary df.groupby(商品类别)[销售额].sum().sort_values(ascendingFalse) # 5. 可视化 summary.plot(kindbar) plt.title(各品类销售额) plt.ylabel(销售额) plt.show()看一遍语法你可能大部分都认识但真正读懂还需要理解每一步在清洗层面对应的脏数据病灶。为了说明清洗的重要性我建议你先故意不改任何清洗步骤跑一次原始sales_demo.csv看看结果有多离谱待定和np.nan会让销售额直接一整列变成缺失或报错根本画不出图。跑出图之后建议再做三件事把plot(kindbar)换成kindbarh感受下横向柱状图把.sort_values(ascendingFalse)去掉再画一次看看排序对柱状图可读性的影响最后把清洗步骤逐行注释掉观察每一步对最终结果的影响。这三步做完你对管道调优的理解会比看十篇文章都深。8. 踩坑记录新人最容易掉进去的三个数值陷阱8.1 陷阱一整数值误判为空值用pd.to_numeric(..., errorscoerce)转换时Pandas 有时会把空字符串和空格也转成NaN这本来是好事情但如果你原本是空即有特殊含义的数据比如该商品不出售所以数量为空也会一起被干掉。这种时候得先问业务含义再决定要不要转。8.2 陷阱二日期解析的夏令时问题我一度以为只有洋数据才有时区麻烦直到某次处理跨区域线上销售的订单表才发现不同系统导出的日期有的带08:00有的是 UTC 零时区。pd.to_datetime虽然会自动推断但当数据量庞大时它会优先按第一行格式推断后面出现不同格式就全乱了。稳妥做法是指定format参数或者用errorscoerce把解析失败的置空再看日志排查。日期时间在数据分析里是绝对的高危区宁可多花两步检查也不要信默认没问题。8.3 陷阱三布尔索引的与和或到底是什么语义这前面提过但因为它太常出错我再单独强调一次。Python 里and、or是逻辑运算符作用于布尔值Pandas 里、|是按位运算符作用于布尔序列。你判断两个条件时如果用andPandas 会尝试把一个数组作为整体求布尔值然后抛错。这个报错信息对新手非常不友好反反复复。# 对Pandas 的多个条件用 | valid df[(df[数量] 0) (df[单价].notna())] # 错Python 的 and 无法处理 Series invalid df[(df[数量] 0) and (df[单价].notna())]9. 这个系列的下一篇会讲什么到这里第一篇的全部核心内容已经走完了环境准备、CSV 读取、DataFrame 基础操作、清洗方法、分组聚合、以及一个完整的可视化闭环。这一篇是数据管道的入口阶段解决的是把数据读进来并整理成可用的形态。下一篇我会沿着管道继续往前走重点讲三个方向第一个是数据探索性分析EDA包括怎么用describe()和分组统计发现异常分布第二个是时间序列的常见预处理比如按月重采样和滑动平均第三个是更多实战场景中的多表连接。到那时候你手里的工具会比现在多得多但核心思维仍然是读取-清洗-统计-输出这个循环。先把这篇里的每个代码至少亲手跑三遍下一篇见。我在实际带新人的过程中反复强调一个观点数据科学入门最难的从来不是某个算法或者某个函数的参数而是建立每一步都在和脏数据打交道的警觉心。你有 Python 基础语法背景当然好没有也问题不大只要跟着这篇文章先把管道走通一遍后面所有的知识都会找到它们该去的位置。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑