Pandas数据清洗速查手册:12个高频函数实战指南

📅 2026/7/14 5:09:55 👁️ 阅读次数 📝 编程学习
Pandas数据清洗速查手册:12个高频函数实战指南

1. 项目概述:一份真正能上手的Pandas速查手册,不是贴在墙上的装饰画

我从2015年开始用Pandas处理金融数据,到后来带团队做用户行为分析、电商漏斗建模,再到最近帮朋友公司搭BI底层数据管道——十年间,我电脑里那个叫pandas_cheatsheet.py的文件,被重写过7次,删掉又重建过4次。不是因为技术迭代快,而是因为绝大多数“速查表”根本没法在真实项目里用。它们要么是把官方文档目录复制粘贴一遍,要么是堆砌一堆冷门函数,比如pd.eval()pd.infer_freq(),你翻半天找不到怎么把两列字符串拼成新列,或者为什么groupby().agg()一加字典就报错。这份手册是我每天打开频率最高的本地文件,它不叫“cheatsheet”,我管它叫“救命清单”。核心就三条:第一,只收我过去三年在生产环境里真正调用超过50次的函数;第二,每个用法都配真实业务场景的输入输出,比如“把订单表里‘2023-01-01’这种字符串转成日期,同时把缺失值统一填成上个月最后一天”;第三,所有代码块都经过实测,复制粘贴就能跑,连import pandas as pd都给你写全。关键词里的“Towards AI”只是原始出处,但内容已彻底重构——我不关心媒体平台,我只关心你下午三点要交的周报能不能按时导出。适合三类人:刚学完df.head()df.shape、正对着Kaggle数据集发懵的新手;会写for循环遍历DataFrame、却不知道apply()能提速8倍的中级使用者;还有那些天天改SQL、临时被拉来救火Python脚本的DBA同事。它解决的不是“知不知道”,而是“卡在哪儿、怎么破”。

2. 核心思路拆解:为什么这12个函数构成数据清洗与探索的“最小可行集合”

2.1 不是功能越多越好,而是覆盖“高频断点”的精准打击

很多人一上来就想学pivot_table()melt()crosstab(),结果发现90%的日常任务根本用不上。我统计过自己2023年处理的67个真实项目(含客户交付和内部分析),数据操作集中在四个断点:读进来就报错、看不清数据长啥样、改不动脏数据、合并不了多张表。这12个函数就是为这四个断点设计的“手术刀”。比如read_csv()排第一,不是因为它多高级,而是因为95%的报错发生在第一步——编码错误、分隔符识别失败、日期列自动转成字符串。这时候翻官方文档查encoding参数不如直接看我的配置模板:read_csv('data.csv', encoding='utf-8-sig', sep=r'\s+', parse_dates=['order_date'], date_parser=lambda x: pd.to_datetime(x, errors='coerce'))。这里utf-8-sig专治Windows记事本生成的CSV乱码,r'\s+'用正则匹配任意空白符(空格、制表符、连续空格),date_parser里加errors='coerce'确保解析失败时自动变NaT而不是报错中断。再比如fillna()dropna()并列,是因为新手常犯一个致命错误:看到缺失值就无脑dropna(),结果把关键用户的注册时间全删了。我的方案是先用isna().sum()定位缺失位置,再根据业务逻辑选策略——用户ID缺失?直接dropna(subset=['user_id']);订单金额缺失?用同城市同品类的中位数填充:df['amount'].fillna(df.groupby(['city', 'category'])['amount'].transform('median'), inplace=True)。这背后是“缺失值不是技术问题,是业务理解问题”的硬逻辑。

2.2 拒绝“函数罗列”,每个函数都绑定具体业务动作

官方文档里groupby()下面有27个聚合方法,但实际工作中,我90%的聚合需求就三个动作:算总数、算占比、找Top N。所以我的速查表里groupby()只展开这三个场景。算总数用size()而不是count(),因为size()统计所有行(包括NaN),count()只统计非空值,电商分析里“每个省份下单用户数”必须包含0单的省份,否则地图可视化会漏区域。算占比用value_counts(normalize=True)配合round(4),而不是groupby().size()/len(df),因为前者一行代码搞定,后者要写三行还容易忘记.reset_index()。找Top N用nlargest()nsmallest(),比如“销售额最高的10个SKU”,直接df.nlargest(10, 'sales'),比sort_values().head(10)快3倍,因为前者不用全排序。这些选择不是凭空来的,是我在处理千万级用户行为日志时,用%%timeit实测出来的性能差异。再比如merge(),我从来不用how='outer',因为业务方永远要的是“以主表为准”,所以速查表里只写how='left'的完整参数:pd.merge(df_orders, df_users, on='user_id', how='left', suffixes=('_order', '_user'), indicator=True)suffixes防字段名冲突,indicator=True生成_merge列标记每行来源(both/left_only/right_only),后续查“为什么有127个订单找不到用户信息”就靠这一列。

2.3 所有函数都遵循“输入-处理-输出”三段式验证

每个函数示例都强制包含三部分:原始数据片段(用pd.DataFrame()构造)、执行代码、结果输出(用print()display())。比如讲str.contains(),我不写“用于模糊匹配”,而是给一个真实场景:客服工单里要筛出所有投诉“物流慢”的记录。原始数据是df_tickets = pd.DataFrame({'ticket_id': [1,2,3], 'content': ['物流太慢了', '发货很快', '快递三天才到']}),代码是df_tickets[df_tickets['content'].str.contains(r'物流|快递.*慢|.*迟', case=False, na=False)],结果明确写出ticket_id为1和3的两行。这里case=False忽略大小写(用户可能写“物流慢”或“物流Slow”),na=False把空值当False处理(避免NaN导致整行消失)。这种写法牺牲了“简洁性”,但换来100%可复现性——你照着抄,结果一定跟我一样。这也是为什么我删掉所有query()eval()这类“炫技函数”,它们在复杂条件时反而增加调试成本。真实项目里,宁可用&|写长一点,也要让逻辑一眼看懂。

3. 核心函数详解与实操要点:从读取到合并的完整链路

3.1 数据加载:read_csv()——别让第一步就崩盘

read_csv()表面看只是读文件,实则是整个分析链路的“守门员”。我见过太多人因为没设对参数,在df.head()后发现第一列全是乱码,或者日期列显示为43210.0这种Excel序列号。核心参数就五个,但每个都直击痛点:

  • encoding:绝对不要用默认的'utf-8'。Windows用户生成的CSV几乎全是'utf-8-sig'(带BOM头),Mac用户常用'mac-roman',而爬虫数据可能是'gbk'。我的做法是先用文本编辑器(如VS Code)打开CSV,右下角看编码标识,没有就试'utf-8-sig',不行再试'latin-1'(万能兜底,不会报错但可能乱码)。

  • sep:别迷信逗号。电商后台导出的订单表常用制表符\t,日志文件用空格或竖线|。用sep=r'\s+'(正则匹配一个或多个空白符)能通吃空格、制表符、连续空格。如果分隔符是|且字段里含|(比如地址栏),必须加quoting=csv.QUOTE_ALL,否则解析会错位。

  • parse_dates:日期列不手动转,后续所有时间计算都会出错。但别直接写parse_dates=['date'],要加date_parser。比如原始数据是'2023/01/01''01-Jan-2023'混用,用date_parser=lambda x: pd.to_datetime(x, format='mixed', errors='coerce')format='mixed'让Pandas自动识别多种格式,errors='coerce'把无法解析的(如'unknown')变NaT

  • dtype:数值列含'N/A''-'时,Pandas会自动设为object类型,后续sum()报错。必须显式声明:dtype={'price': 'float64', 'qty': 'Int64'}。注意'Int64'(大写I)是Pandas的可空整型,能存NaN,而'int64'不能。

  • nrows:处理超大文件(>1GB)时,先用nrows=1000快速预览结构,确认字段名和类型后再全量读取。我习惯加skiprows=lambda x: x > 0 and x % 10000 == 0跳过中间行(测试用),但正式分析必须关掉。

实操案例:读取某电商平台导出的orders_2023.csv,字段为order_id, user_id, order_time, amount, status,其中order_time'2023-01-01 10:30:00'格式,amount列有'-'status含中文。正确代码:

import pandas as pd import csv df = pd.read_csv( 'orders_2023.csv', encoding='utf-8-sig', sep=',', parse_dates=['order_time'], date_parser=lambda x: pd.to_datetime(x, errors='coerce'), dtype={'amount': 'float64', 'status': 'string'}, na_values=['-', 'N/A', 'NULL'] )

这里na_values'-'等字符串统一识别为NaN,比在fillna()里处理更源头。执行后检查:df.dtypes确认order_timedatetime64[ns]amountfloat64statusstring(不是object)。

3.2 数据概览:info()describe()sample()——三分钟看清数据底细

新手常犯的错是跳过概览直接写分析代码,结果groupby()KeyError才发现字段名是'user id'(带空格)不是'user_id'。我的固定流程是三步走:

  • df.info():第一眼盯Non-Null CountDtype。如果某列Non-Null Count远小于Rows,说明缺失严重;如果Dtypeobject但应该是数字(如'price'),立刻用df['price'] = pd.to_numeric(df['price'], errors='coerce')转换。info()还显示内存占用,memory usage: 1.2 MB提示小数据,memory usage: 2.4 GB就得考虑dtype优化(如int64int32)。

  • df.describe(include='all')include='all'是关键!默认只统计数值列,加这个参数后会显示字符列的unique(去重数)、top(出现最多值)、freq(频次)。比如df['city'].describe()返回unique: 321, top: 'Shanghai', freq: 12780,立刻知道有321个城市,上海最多。数值列看std(标准差),如果amountstd0.0,说明全是一样的值,可能导出错误。

  • df.sample(5):随机抽5行看真实数据。比head()更可靠,因为head()可能只显示前5行(全是测试数据),而sample()暴露真实分布。我习惯加random_state=42保证可重现:df.sample(5, random_state=42)。抽样后重点看:日期是否在合理范围(如order_time没有'1970-01-01'这种占位符);数值是否异常(如amount出现-999999);文本是否含不可见字符(用repr()看:repr(df.iloc[0]['content'])显示'物流太慢了\\u200b'\u200b是零宽空格,需用str.replace('\u200b', '')清理)。

实操心得:我写了个一键概览函数,放在所有分析脚本开头:

def quick_inspect(df): print("=== DATA INFO ===") df.info() print("\n=== DESCRIPTIVE STATISTICS ===") print(df.describe(include='all')) print("\n=== RANDOM SAMPLE (5 rows) ===") print(df.sample(5, random_state=42)) print("\n=== MISSING VALUES ===") print(df.isna().sum().sort_values(ascending=False)) # 调用 quick_inspect(df_orders)

运行后,缺失值最多的列、数据类型异常的列、抽样中的脏数据,一目了然。这比盯着屏幕手动检查快10倍。

3.3 缺失值处理:isna()fillna()dropna()——业务逻辑决定技术方案

缺失值不是bug,是业务信号。user_id缺失可能意味着游客下单,phone缺失可能代表隐私保护设置。我的处理铁律:先问业务,再动手。速查表里这三个函数的用法,全部绑定业务场景:

  • isna():不只是df.isna().sum()。要定位具体行,用df[df['amount'].isna()]看哪些订单金额为空;要查组合缺失,用df[df['user_id'].isna() & df['email'].isna()]找既没用户ID也没邮箱的“幽灵订单”。更狠的是df.isna().any(axis=1),生成布尔列标记“该行是否有任一缺失”,再df[df.isna().any(axis=1)]直接揪出所有问题行。

  • fillna():绝不无脑填0或均值。按业务分三类:

    • 分类变量:用众数填充。df['category'].fillna(df['category'].mode()[0])mode()[0]取第一个众数(可能有多个)。
    • 数值变量:用分组统计填充。如“不同城市订单金额的中位数”:df['amount'].fillna(df.groupby('city')['amount'].transform('median'))transform()保持原索引,比map()安全。
    • 时间变量:用业务规则填充。如order_time缺失,按created_time推算:df['order_time'].fillna(df['created_time'] + pd.Timedelta(days=1))
  • dropna()subset参数是灵魂。df.dropna(subset=['user_id', 'amount'])只删这两列都空的行,保留user_id有值但amount空的行(后续可补)。how='any'(默认)删任一指定列空的行,how='all'删所有指定列都空的行。最常用的是threshdf.dropna(thresh=len(df.columns)-2),要求每行至少有总列数-2个非空值,避免因个别字段缺失删掉整行。

避坑提醒:fillna()默认返回新DataFrame,要加inplace=True才修改原数据,但我不推荐。我的习惯是df = df.fillna({...}),明确赋值,避免意外。另外,fillna()datetime列填pd.NaT,对string列填pd.NA(Pandas 1.0+),别用'None'字符串。

3.4 字符串清洗:str.replace()str.contains()str.split()——正则是你的瑞士军刀

电商和客服数据里,80%的脏数据是字符串问题:前后空格、特殊符号、大小写混乱、格式不一。str访问器是Pandas最被低估的利器。三个函数覆盖95%场景:

  • str.replace():别只用字面替换。df['name'].str.replace(r'\s+', ' ', regex=True)把多个空格变一个;df['phone'].str.replace(r'[^0-9]', '', regex=True)删所有非数字字符(留手机号);df['email'].str.replace(r'@.*$', '@domain.com', regex=True)批量更换域名。关键是regex=True(默认False),不开它正则无效。

  • str.contains():业务搜索的灵魂。df[df['content'].str.contains(r'退款|退货|cancel', case=False, na=False)]筛客服投诉。case=False忽略大小写,na=False让空值不参与判断(否则返回NaN,布尔索引失效)。进阶用flags=re.IGNORECASE替代case=False,支持更多正则选项。

  • str.split():拆分地址、标签、多值字段。df['tags'].str.split(',', expand=True)'A,B,C'拆成三列;df['address'].str.split(' ', n=2, expand=True)按空格拆,最多拆2次(n=2),得到[省, 市, 区+街道]expand=True生成DataFrame,expand=False生成Series of lists。

实操案例:清洗用户地址' Shanghai \t China '。一步到位:

df['address_clean'] = (df['address'] .str.strip() # 去首尾空格\t\n .str.replace(r'\s+', ' ', regex=True) # 多空格变单空格 .str.title()) # 首字母大写 # 结果:'Shanghai China'

注意顺序:先strip()replace(),否则'\t Shanghai'\t还在。str.title()str.capitalize()好,后者只大写首字母,title()让每个单词首字母大写。

3.5 数值计算:agg()apply()transform()——告别for循环的三大支柱

新手写for index, row in df.iterrows():,处理10万行要2分钟;用向量化,2秒搞定。这三个函数是向量化的“三剑客”,分工明确:

  • agg()聚合汇总,输出比输入行数少。df.groupby('category').agg({'amount': ['sum', 'mean'], 'qty': 'count'})。注意字典值可以是函数名字符串('sum')或函数本身(np.sum),但混合用会报错。agg()返回MultiIndex列,用columns=['amount_sum', 'amount_mean', 'qty_count']重命名。

  • apply()逐行/逐列处理,输出行数不变。df['amount'].apply(lambda x: x * 1.1 if x > 100 else x)给高价商品加10%税。axis=1时处理整行:df.apply(lambda row: row['amount'] / row['qty'] if row['qty'] > 0 else 0, axis=1)算单价。apply()慢于agg()transform(),只在逻辑复杂时用。

  • transform()广播式计算,输出行数不变,但能用分组统计。df['amount_zscore'] = df.groupby('category')['amount'].transform(lambda x: (x - x.mean()) / x.std())算每类商品的金额Z-score。transform()apply()的高效版,只要函数返回标量或与输入同长的数组,就比apply()快5-10倍。

性能对比实测:对100万行数据算amount的Z-score:

  • for循环:142秒
  • apply():8.3秒
  • transform():0.9秒

所以我的口诀:汇总用agg(),复杂逻辑用apply(),分组计算用transform()

3.6 表连接:merge()——左连接是你的默认安全带

merge()是数据整合的核心,但90%的错误源于how参数选错。我的原则:永远从主表出发,用how='left'。主表是你分析的主体(如订单表),右表是补充信息(如用户表)。how='left'保证主表所有行都在,右表缺失则填NaN,不会丢数据。

关键参数:

  • on:同名列直接写on='user_id';不同名用left_on='order_user_id', right_on='user_id'
  • suffixes:防字段名冲突。suffixes=('_order', '_user'),合并后amount_orderamount_user
  • indicator:加indicator=True生成_merge列,值为'both'(两表都有)、'left_only'(仅主表)、'right_only'(仅右表)。查“为什么有500个订单找不到用户”就df[df['_merge']=='left_only'][['order_id']]

避坑指南:

  • 合并前务必df_left['key'].nunique()df_right['key'].nunique(),如果右表key去重数远小于左表,说明右表有重复键,合并会爆炸(1对多变多对多)。
  • 时间字段合并用pd.merge_asof(),按时间最近匹配。如订单表和物流表,按order_timedelivery_time找最接近的物流记录。

实操案例:合并订单表df_orders(字段order_id, user_id, amount)和用户表df_users(字段user_id, city, age):

df_merged = pd.merge( df_orders, df_users, on='user_id', how='left', suffixes=('_order', '_user'), indicator=True ) # 查找未匹配订单 unmatched = df_merged[df_merged['_merge'] == 'left_only'][['order_id', 'user_id']] print(f"Unmatched orders: {len(unmatched)}")

3.7 数据透视:pivot_table()——不是魔法,是结构化思维

pivot_table()常被神化,其实就干一件事:把长表变宽表,把维度变行列。核心三要素:index(行)、columns(列)、values(值)。aggfunc是聚合方式。

典型场景:销售日报表,原始是长表date, product, sales,要变成宽表date为行,product为列,sales为值。

pivot = df.pivot_table( index='date', columns='product', values='sales', aggfunc='sum', fill_value=0 # NaN变0 )

fill_value=0是关键,否则空单元格是NaN,画图时断开。

进阶用法:多维度indexcolumnsindex=['year', 'month']columns=['region', 'category'],生成四层索引。margins=True加汇总行/列。

避坑:pivot_table()默认dropna=True,会删indexcolumnsNaN的行。如果product列有空值,想保留,加dropna=False

4. 实操全流程:从原始CSV到可交付分析报告的七步工作流

4.1 第一步:加载与诊断(5分钟)

拿到raw_data.csv,不急着分析,先执行诊断脚本:

import pandas as pd import numpy as np # 1. 加载(带容错) try: df = pd.read_csv('raw_data.csv', encoding='utf-8-sig', low_memory=False) except UnicodeDecodeError: df = pd.read_csv('raw_data.csv', encoding='gbk', low_memory=False) # 2. 快速诊断 print(f"Shape: {df.shape}") print(f"\nData Types:\n{df.dtypes}") print(f"\nMissing Values:\n{df.isna().sum().sort_values(ascending=False)}") print(f"\nSample:\n{df.sample(3)}") # 3. 关键列检查 if 'order_date' in df.columns: print(f"\nOrder Date Range: {df['order_date'].min()} to {df['order_date'].max()}") if 'amount' in df.columns: print(f"\nAmount Stats: min={df['amount'].min()}, max={df['amount'].max()}, mean={df['amount'].mean():.2f}")

输出一看:Shape: (12450, 18)Missing Valuesuser_id缺217个,Sample显示order_date是字符串'2023/01/01'——立刻知道要转日期、处理缺失。

4.2 第二步:基础清洗(10分钟)

基于诊断结果,写清洗链:

# 1. 日期转换 df['order_date'] = pd.to_datetime(df['order_date'], format='%Y/%m/%d', errors='coerce') # 2. 数值列清理 df['amount'] = pd.to_numeric(df['amount'], errors='coerce') df['qty'] = pd.to_numeric(df['qty'], errors='coerce') # 3. 字符串标准化 df['product_name'] = df['product_name'].str.strip().str.title() # 4. 缺失值处理 # user_id缺失:标记为'guest' df['user_id'] = df['user_id'].fillna('guest') # amount缺失:用同产品平均值填充 df['amount'] = df['amount'].fillna(df.groupby('product_name')['amount'].transform('mean')) # 5. 异常值过滤(金额<0或>100万) df = df[(df['amount'] >= 0) & (df['amount'] <= 1000000)]

每步后加df.shape检查行数变化,确保没误删。

4.3 第三步:特征工程(15分钟)

从业务出发构造新特征:

# 1. 时间特征 df['order_year'] = df['order_date'].dt.year df['order_month'] = df['order_date'].dt.month df['order_dayofweek'] = df['order_date'].dt.dayofweek # 0=周一 # 2. 业务指标 df['revenue'] = df['amount'] * df['qty'] df['is_weekend'] = df['order_dayofweek'].isin([5, 6]) # 周六日 # 3. 用户分层(RFM简化版) # R: 最近购买天数 last_order = df.groupby('user_id')['order_date'].max().max() df['recency_days'] = (last_order - df.groupby('user_id')['order_date'].transform('max')).dt.days # F: 购买频次 df['frequency'] = df.groupby('user_id')['order_id'].transform('count') # M: 总金额 df['monetary'] = df.groupby('user_id')['revenue'].transform('sum')

transform()在这里避免了merge(),效率翻倍。

4.4 第四步:探索分析(20分钟)

用速查表函数快速产出洞察:

# 1. 销售趋势(月度) monthly_sales = df.groupby('order_month')['revenue'].sum().sort_index() print("Monthly Revenue:") print(monthly_sales) # 2. 产品TOP10 top_products = df.groupby('product_name')['revenue'].sum().nlargest(10) print("\nTop 10 Products by Revenue:") print(top_products) # 3. 用户分层分析 # RFM分层:R低、F高、M高为高价值 df['r_score'] = pd.qcut(df['recency_days'], q=5, labels=False, duplicates='drop') + 1 df['f_score'] = pd.qcut(df['frequency'], q=5, labels=False, duplicates='drop') + 1 df['m_score'] = pd.qcut(df['monetary'], q=5, labels=False, duplicates='drop') + 1 df['rfm_score'] = df['r_score'] + df['f_score'] + df['m_score'] high_value = df[df['rfm_score'] >= 12] print(f"\nHigh Value Users: {len(high_value)} ({len(high_value)/len(df)*100:.1f}%)")

qcut()按分位数分箱,比cut()更适应数据分布。

4.5 第五步:数据合并(5分钟)

关联用户表获取城市信息:

# 假设用户表users.csv有user_id, city users = pd.read_csv('users.csv') df_enriched = pd.merge(df, users, on='user_id', how='left', suffixes=('', '_user')) # 检查合并质量 print(f"Merge check - unmatched users: {df_enriched[df_enriched['city'].isna()]['user_id'].nunique()}")

4.6 第六步:透视与汇总(10分钟)

生成管理层需要的宽表:

# 城市-月份销售透视 city_month_pivot = df_enriched.pivot_table( index='city', columns='order_month', values='revenue', aggfunc='sum', fill_value=0, margins=True # 加总计 ) print("\nCity-Month Revenue Pivot:") print(city_month_pivot.round(0))

4.7 第七步:导出与交付(2分钟)

导出多张Sheet到Excel:

with pd.ExcelWriter('sales_report.xlsx') as writer: df_enriched.to_excel(writer, sheet_name='Raw_Data', index=False) city_month_pivot.to_excel(writer, sheet_name='City_Month_Sales') top_products.to_excel(writer, sheet_name='Top_Products') print("Report exported: sales_report.xlsx")

ExcelWriter自动处理多Sheet,比多次to_excel()更稳。

5. 常见问题与排查技巧实录:那些让我凌晨三点还在debug的坑

5.1 “KeyError: 'xxx'”——字段名陷阱大全

这是新手最高频报错,原因五花八门:

  • 空格和不可见字符df.columns.tolist()显示['user_id', ' amount '],后面有空格。解决方案:df.columns = df.columns.str.strip()
  • 大小写混淆:CSV里是'UserID',代码写df['userid']。解决方案:df.columns = df.columns.str.lower()统一小写。
  • 中文标点:字段名是'订单时间',但复制时带了全角空格。用repr(df.columns.tolist())看真实字符。
  • 特殊符号'price($)',括号在正则里是元字符。解决方案:用反斜杠转义df['price($)']df.loc[:, 'price($)']

我的自查清单:

  1. print(df.columns.tolist())看原始字段名
  2. print(repr(df.columns.tolist()))看隐藏字符
  3. df.columns = df.columns.str.strip().str.replace(r'[^\w\s]', '_', regex=True).str.lower()一键标准化

5.2 “SettingWithCopyWarning”——链式赋值的幽灵警告

当你写df[df['amount']>100]['discount'] = 0.1,Pandas会警告:你可能在修改视图而非原DataFrame。这不是错误,但结果不可控。

根治方案只有两个:

  • .loc[]明确索引df.loc[df['amount']>100, 'discount'] = 0.1
  • copy()创建真副本df_subset = df[df['amount']>100].copy(),再改df_subset['discount'] = 0.1

为什么df[df>100]是视图?因为Pandas为性能优化,小数据时返回视图,大数据时返回副本,行为不一致。.loc[]强制按标签索引,100%安全。

5.3 “ValueError: cannot convert float NaN to integer”——类型转换的雷区

df['qty'].astype('int64')报错,因为NaN不能转int。解决方案:

  • 用Pandas可空整型:df['qty'] = df['qty'].astype('Int64')(大写I)
  • 或先填空值:df['qty'] = df['qty'].fillna(0).astype('int64')
  • 或用pd.to_numeric()df['qty'] = pd.to_numeric(df['qty'], downcast='integer', errors='coerce')

downcast='integer'自动选最小整型(int8/int16),省内存。

5.4 “MemoryError”——大文件处理的生存指南

处理10GB CSV时,read_csv()直接崩。我的四步法:

  1. 分块读取chunk_iter = pd.read_csv('big.csv', chunksize=50000),用for chunk in chunk_iter:逐块处理。
  2. 列筛选usecols=['col1','col2']只读需要的列。
  3. 类型压缩:`dtype={'col1': 'category', '