公司动态

Python数据分析实战:从数学建模到Pandas核心操作与可视化

📅 2026/8/22 5:46:03
Python数据分析实战:从数学建模到Pandas核心操作与可视化
1. 项目概述从数学建模赛题到Python实战最近在整理过去带学生做数学建模竞赛的资料翻到了2011年的A题一个关于城市表层土壤重金属污染分析的问题。这道题当年难倒了不少队伍核心难点之一就在于如何处理那份庞大的、包含多种重金属元素在不同功能区采样点的浓度数据。你需要做的不是简单地算个平均值而是要对数据进行分类统计比如工业区、生活区、公园绿地区各自的污染特征计算每个采样点的综合污染指数统计不同污染等级的区域个数最后用直观的图表比如饼图来展示污染程度的构成比。这不就是一个典型的数据处理与分析流程吗用Python的Pandas和Matplotlib来搞定再合适不过了。很多朋友学Python数据处理跟着教程把df.groupby()、df.plot.pie()跑一遍就结束了但一到自己面对真实、杂乱的数据就无从下手。问题出在哪在于缺少一个从“原始需求”到“代码实现”的完整思维链条以及对这个链条上每个环节“为什么这么做”和“可能踩什么坑”的深刻理解。今天我就以这道建模题为背景抛开教科书的简单示例带你走一遍一个数据分析任务中那些看似基础却至关重要的操作如何安全地复制DataFrame以免污染原始数据、如何高效地对每一行或列进行计算、如何统计某一列里不同元素的个数以及如何画出一张既专业又美观的饼图。无论你是正在备战数模的学生还是日常需要处理报表的职场人这套思路和技巧都能直接套用。2. 核心需求解析与数据准备2.1 数学建模2011 A题背景与数据特点2011年高教社杯全国大学生数学建模竞赛A题《城市表层土壤重金属污染分析》提供了一个经典的数据分析场景。题目给出了一个城市区域内大量采样点的空间坐标x, y、所属功能区类型如1-生活区2-工业区3-山区等以及多种重金属元素如砷、镉、铬等的浓度测量值。我们的核心分析任务通常包括分类统计计算不同功能区分类变量内各种重金属浓度的平均值、最大值、最小值等描述性统计量以比较污染特征。综合评价对每个采样点需要根据多种重金属的浓度计算一个综合污染指数如内梅罗指数。这涉及到对每个样本每一行数据进行多列数据的数学运算。现状评估根据综合污染指数划分污染等级如清洁、轻度污染、重度污染并统计每个等级的区域数量即统计某一列中不同元素/类别的个数。成果可视化将污染等级的分布情况用饼图展示直观呈现各等级占比。原始数据通常是一个CSV或Excel文件我们假设其结构如下表所示仅为示例采样点IDx坐标y坐标功能区砷(As)镉(Cd)铬(Cr)...1100200112.50.845.2...2150180225.62.189.7...........................2.2 环境准备与数据加载首先确保你的Python环境安装了必要的库。打开你的终端或命令提示符执行以下命令进行安装或更新pip install pandas matplotlib numpy -U注意建议在虚拟环境中操作避免包版本冲突。可以使用venv或conda创建独立环境。接下来我们开始加载数据。这里我模拟生成一份符合题目背景的数据用于演示在实际比赛中你直接使用pd.read_csv加载即可。import pandas as pd import numpy as np # 模拟生成数据 np.random.seed(2023) # 固定随机种子确保结果可复现 n_samples 500 data { 点ID: range(1, n_samples1), x坐标: np.random.uniform(0, 1000, n_samples), y坐标: np.random.uniform(0, 1000, n_samples), 功能区: np.random.choice([1, 2, 3, 4, 5], n_samples, p[0.3, 0.25, 0.2, 0.15, 0.1]), # 1:生活区, 2:工业区, 3:交通区, 4:公园, 5:山区 As: np.random.lognormal(mean2.0, sigma0.8, sizen_samples), # 对数正态分布模拟浓度 Cd: np.random.lognormal(mean0.5, sigma1.0, sizen_samples), Cr: np.random.lognormal(mean3.5, sigma0.6, sizen_samples), Pb: np.random.lognormal(mean3.0, sigma0.7, sizen_samples), } df_raw pd.DataFrame(data) print(原始数据前5行) print(df_raw.head()) print(f\n数据形状{df_raw.shape})运行后你会看到一个包含500行样本的DataFrame。这里第一个关键操作来了我们很少直接对df_raw进行操作。因为数据分析是一个探索和试错的过程直接操作原始数据一旦误操作比如原地修改了某列就很难回溯。所以第一步通常是创建一个副本。3. 数据处理基石DataFrame的安全复制与理解3.1 浅拷贝与深拷贝一个必须厘清的概念在Pandas中直接赋值df df_raw并不会创建副本它只是创建了一个指向同一块内存数据的新引用。修改df会直接影响df_raw。df_reference df_raw # 这只是引用不是复制 df_reference[As][0] 999 # 这个修改会同步到df_raw print(fdf_raw的As第一行是否被修改 {df_raw[As].iloc[0] 999})为了避免这种情况我们需要复制。Pandas提供了两种主要方式df.copy(deepFalse)(浅拷贝)只复制DataFrame的结构索引、列名但数据本身底层的NumPy数组仍然是共享的。对于纯数值型DataFrame修改一个副本的值通常不会影响另一个因为NumPy数组在某些操作下会产生新数组但对于包含可变对象如列表的列仍有风险。一般不推荐在数据处理中主动使用浅拷贝。df.copy()或df.copy(deepTrue)(深拷贝)默认行为。完整地复制数据和结构创建一个完全独立的新对象。这是最安全、最常用的方式。df df_raw.copy() # 深拷贝安全独立 print(fdf是df_raw的副本吗 {df is not df_raw}) print(f修改df会影响df_raw吗) df[As][0] 888 print(fdf_raw的As第一行值{df_raw[As].iloc[0]:.2f} df的As第一行值{df[As].iloc[0]:.2f})实操心得养成“加载即拷贝”的习惯。在开始任何分析前先用.copy()创建你的工作副本。这能为你省去大量因数据意外污染而导致的调试时间。尤其是在使用Jupyter Notebook进行交互式分析时单元格反复执行如果没有副本保护原始数据很容易被改得面目全非。3.2 数据初探与清洗准备在深入分析前先了解你的数据副本df。# 查看基本信息 print(df.info()) # 查看列类型、非空值数量 print(df.describe()) # 数值型列的快速统计摘要 # 检查缺失值 print(f\n各列缺失值数量\n{df.isnull().sum()}) # 查看分类列功能区的取值分布 print(f\n‘功能区’取值分布\n{df[功能区].value_counts()}) print(f‘功能区’唯一值个数{df[功能区].nunique()})df.info()和df.describe()能让你快速把握数据全貌有多少行、多少列、有无缺失、数值的大致范围。df[功能区].value_counts()和df[功能区].nunique()则是我们进行“分类统计”和“不同元素个数统计”的前奏。value_counts()直接给出了每个类别的频数而nunique()则告诉你一共有多少种不同的类别。如果nunique()的结果异常大比如接近总行数那可能意味着这个字段是ID或存在大量脏数据不适合作为分类依据。4. 核心操作一分类统计GroupBy Aggregation分类统计是数据分析的“灵魂”操作之一。在本题中我们需要按“功能区”分组然后对各个重金属浓度列进行统计。4.1 基础分组聚合使用groupby()方法。# 按‘功能区’分组计算各重金属浓度的平均值 group_mean df.groupby(功能区)[[As, Cd, Cr, Pb]].mean() print(按功能区分组的平均浓度) print(group_mean) # 同时计算多个统计量 group_stats df.groupby(功能区)[[As, Cd, Cr, Pb]].agg([mean, std, min, max, count]) print(\n按功能区分组的详细统计多指标) print(group_stats)groupby(功能区)创建了一个分组对象[[As, Cd, ...]]指定了我们要分析的列.mean()或.agg()则指定了聚合函数。.agg()非常强大可以传入一个函数列表如[mean, std]也可以传入一个字典来为不同列指定不同的聚合函数如{As: mean, Cd: [min, max]}。4.2 高级分组技巧与结果处理分组后的结果是一个新的DataFrame其索引默认是分组键‘功能区’。有时我们需要将分组键重置为普通列。# 重置索引让‘功能区’变回列 group_stats_reset group_stats.reset_index() print(重置索引后的分组统计) print(group_stats_reset.head()) # 更复杂的聚合计算每个功能区污染最严重的元素浓度最大值所在的列名 def top_pollutant(series): # series 是一个功能区下所有重金属浓度列的一行数据一个Series return series.idxmax() # 返回最大值对应的索引即元素名 df_group_max df.groupby(功能区)[[As, Cd, Cr, Pb]].apply(lambda x: x.max().idxmax()) print(\n每个功能区主要污染物浓度最高者) print(df_group_max)注意事项groupby操作默认会忽略缺失值NaN。如果你的数据有NaN聚合函数如mean会跳过它们。如果你希望NaN被当作0或其他值参与计算需要在分组前用fillna()处理。另外分组操作可能会产生多层索引MultiIndex使用.reset_index()或.unstack()可以将其展平便于后续分析和绘图。5. 核心操作二行/列数据计算Apply, Vectorization接下来是计算每个采样点的综合污染指数。这需要用到每一行中多个列的数据。假设我们使用一个简单的综合指数公式sqrt( (平均浓度)^2 (最大浓度)^2 ) / 2。当然实际建模中可能会用更专业的指数如内梅罗指数但计算逻辑相通。5.1 低效循环 vs. 高效向量化新手常犯的错误是使用循环# 不推荐逐行循环效率极低 def calc_index_row(row): conc [row[As], row[Cd], row[Cr], row[Pb]] avg np.mean(conc) maxc np.max(conc) return np.sqrt(avg**2 maxc**2) / 2 # 这会非常慢尤其数据量大时 df[index_loop] df.apply(calc_index_row, axis1)正确做法是使用向量化操作# 推荐向量化计算利用NumPy/Pandas的广播机制效率极高 conc_cols [As, Cd, Cr, Pb] df[avg_concentration] df[conc_cols].mean(axis1) # 沿行方向求平均 df[max_concentration] df[conc_cols].max(axis1) df[pollution_index] np.sqrt(df[avg_concentration]**2 df[max_concentration]**2) / 2 print(计算综合污染指数后的数据前5行) print(df[[点ID, avg_concentration, max_concentration, pollution_index]].head())axis1参数是关键它指示操作沿着行的方向进行。df[conc_cols].mean(axis1)会为每一行计算这四个浓度的平均值返回一个长度与df相同的Series。整个计算过程没有显式循环代码简洁速度比循环快成百上千倍。5.2 灵活应用Apply函数虽然向量化是首选但有些复杂逻辑无法直接向量化这时可以使用apply函数。例如我们想根据污染指数添加一个污染等级标签。# 定义一个分类函数 def classify_pollution(index): if index 10: return 清洁 elif index 30: return 轻度污染 elif index 60: return 中度污染 else: return 重度污染 # 对‘pollution_index’列应用这个函数 df[pollution_level] df[pollution_index].apply(classify_pollution) print(\n添加污染等级后的数据) print(df[[点ID, pollution_index, pollution_level]].head(10))apply函数将自定义函数映射到Series的每个元素上。对于更复杂的、涉及多列的行级操作可以使用df.apply(func, axis1)但需注意其性能开销。实操心得“向量化优先”是Python数据分析的性能黄金法则。在写任何for循环或apply之前先思考能否用Pandas/Numpy的内置函数如mean,sum,diff,shift或数组运算完成。这不仅能提升代码速度也使代码更清晰。对于简单的映射关系如本例的分类pd.cut()或pd.qcut()函数是比apply更好的选择它们直接基于数值区间进行分箱效率更高。6. 核心操作三统计一列中不同元素的个数现在我们有了一列新的分类数据pollution_level我们需要统计每个污染等级有多少个采样点。这其实就是对分类变量进行频数统计。6.1 使用value_counts()这是最直接、最常用的方法。level_counts df[pollution_level].value_counts() print(污染等级统计默认降序) print(level_counts) print(type(level_counts)) # 这是一个Seriesvalue_counts()返回一个Series索引是唯一的类别值是对应的计数。它默认按计数降序排列。6.2 控制排序与处理缺失值# 按类别名称排序 level_counts_sorted df[pollution_level].value_counts().sort_index() print(\n污染等级统计按类别名排序) print(level_counts_sorted) # 包含缺失值的统计如果存在 # df[pollution_level_with_na] df[pollution_level].copy() # df.loc[0, pollution_level_with_na] np.nan # 模拟一个缺失值 # print(df[pollution_level_with_na].value_counts(dropnaFalse)) # dropnaFalse 会统计NaN6.3 获取唯一值列表与个数有时我们不需要计数只需要知道有哪些类别或者有多少种类别。unique_levels df[pollution_level].unique() n_unique_levels df[pollution_level].nunique() print(f\n唯一的污染等级有{unique_levels}) print(f共有 {n_unique_levels} 种不同的污染等级)unique()返回一个NumPy数组包含所有唯一值顺序是出现顺序。nunique()返回唯一值的数量默认忽略NaN。注意事项value_counts()默认不包含NaN。如果你的数据有缺失并且你想知道缺失了多少需要使用dropnaFalse参数。另外value_counts()的结果是一个Series非常适合直接用于绘图比如作为饼图的数据源。nunique()在数据探索阶段非常有用可以快速识别出那些本应是分类变量、但取值却异常多的字段可能是数据录入错误。7. 核心操作四使用Matplotlib绘制专业饼图统计结果出来了用饼图展示最直观。但Matplotlib默认的饼图可能有些“简陋”我们需要调整一下让它更专业。7.1 基础饼图绘制import matplotlib.pyplot as plt # 设置中文字体如果标签需要中文 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 # 准备数据 labels level_counts.index.tolist() # 等级标签 sizes level_counts.values.tolist() # 对应数量 colors [#66c2a5, #fc8d62, #8da0cb, #e78ac3] # 自定义颜色序列 # 创建图形 fig, ax plt.subplots(figsize(8, 8)) # 绘制饼图 wedges, texts, autotexts ax.pie(sizes, labelslabels, colorscolors, autopct%1.1f%%, startangle90, textprops{fontsize: 12}) # 美化 ax.set_title(采样点污染等级分布, fontsize16, fontweightbold, pad20) # 设置autotexts百分比数字的样式 for autotext in autotexts: autotext.set_color(white) autotext.set_fontweight(bold) autotext.set_fontsize(11) # 确保饼图是正圆 ax.axis(equal) plt.tight_layout() plt.show()ax.pie()是关键函数。autopct控制饼图内显示百分比的格式startangle设置第一块饼的起始角度90度表示从12点钟方向开始。wedges,texts,autotexts分别返回了饼图扇形块、外部标签文本、内部百分比文本的对象方便我们后续进行个性化设置。7.2 高级美化突出显示与图例在汇报或论文中我们可能希望突出显示最重要的部分比如“重度污染”并添加图例。# 假设我们想突出“重度污染”部分 explode (0, 0, 0, 0.1) # 只将第四块对应‘重度污染’突出0.1 fig, ax plt.subplots(figsize(10, 8)) wedges, texts, autotexts ax.pie(sizes, explodeexplode, labelslabels, colorscolors, autopctlambda pct: f{pct:.1f}%\n({int(pct/100.*sum(sizes))}), startangle90, shadowTrue) # 添加图例 ax.legend(wedges, labels, title污染等级, loccenter left, bbox_to_anchor(1, 0, 0.5, 1)) ax.set_title(采样点污染等级分布突出显示重度污染, fontsize16, fontweightbold, pad20) ax.axis(equal) plt.tight_layout() plt.show()这里做了几处改进explode参数通过一个元组指定每块饼的“爆炸”距离0表示不突出。自定义autopct使用了一个lambda函数在百分比后面加上了实际数量信息更丰富。shadowTrue添加了阴影增加立体感。添加图例使用ax.legend()并将wedges和labels传进去。bbox_to_anchor参数用于将图例放置在图形外部右侧。7.3 避免饼图陷阱与替代方案虽然饼图直观但在某些场景下需谨慎使用类别过多超过6-7个时饼图会显得杂乱不易比较。此时条形图Bar Chart是更好的选择。需要精确比较数值大小时人眼对角度和面积的感知不如对长度敏感条形图更佳。# 使用条形图展示相同数据 fig, ax plt.subplots(figsize(10, 6)) bars ax.bar(labels, sizes, colorcolors) ax.set_ylabel(采样点数量, fontsize12) ax.set_title(采样点污染等级分布条形图, fontsize16, fontweightbold, pad20) # 在条形顶端添加数量标签 for bar in bars: height bar.get_height() ax.text(bar.get_x() bar.get_width()/2., height 0.5, f{int(height)}, hacenter, vabottom, fontsize11) plt.tight_layout() plt.show()实操心得绘制用于报告或出版的图表时细节决定专业度。务必注意1)字体清晰可读特别是中文字体配置2)颜色搭配使用色盲友好的配色方案如viridis,plasma等Matplotlib色彩映射或Tableau配色3)信息密度适中避免在图上堆砌过多文字4)始终提供替代方案如考虑用条形图补充或替代饼图。保存图表时使用高DPI如plt.savefig(output.png, dpi300, bbox_inchestight)以确保印刷质量。8. 完整流程串联与代码整合现在让我们把上述所有步骤串联起来形成一个从数据加载到结果可视化的完整脚本。# -*- coding: utf-8 -*- 数学建模2011 A风格数据分析完整流程示例 功能分类统计、行计算、频数统计、饼图绘制 import pandas as pd import numpy as np import matplotlib.pyplot as plt # 1. 模拟/加载数据并创建安全副本 print(步骤1: 加载数据并创建副本...) np.random.seed(2023) n_samples 500 data { 点ID: range(1, n_samples1), x坐标: np.random.uniform(0, 1000, n_samples), y坐标: np.random.uniform(0, 1000, n_samples), 功能区: np.random.choice([1, 2, 3, 4, 5], n_samples, p[0.3, 0.25, 0.2, 0.15, 0.1]), As: np.random.lognormal(mean2.0, sigma0.8, sizen_samples), Cd: np.random.lognormal(mean0.5, sigma1.0, sizen_samples), Cr: np.random.lognormal(mean3.5, sigma0.6, sizen_samples), Pb: np.random.lognormal(mean3.0, sigma0.7, sizen_samples), } df_raw pd.DataFrame(data) df df_raw.copy() # 关键的安全拷贝 # 2. 分类统计按功能区统计重金属浓度 print(\n步骤2: 按功能区进行重金属浓度分类统计...) conc_cols [As, Cd, Cr, Pb] func_area_stats df.groupby(功能区)[conc_cols].agg([mean, std, max]) print(func_area_stats.round(2)) # 保留两位小数 # 3. 行计算计算每个采样点的综合污染指数 print(\n步骤3: 计算每个采样点的综合污染指数...) df[avg_conc] df[conc_cols].mean(axis1) df[max_conc] df[conc_cols].max(axis1) df[pollution_index] np.sqrt(df[avg_conc]**2 df[max_conc]**2) / 2 # 4. 分类与统计划分污染等级并计数 print(\n步骤4: 划分污染等级并统计数量...) def classify_pollution(index): if index 10: return 清洁 elif index 30: return 轻度污染 elif index 60: return 中度污染 else: return 重度污染 df[pollution_level] df[pollution_index].apply(classify_pollution) level_counts df[pollution_level].value_counts() print(污染等级分布) print(level_counts) # 5. 可视化绘制饼图 print(\n步骤5: 绘制污染等级分布饼图...) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False labels level_counts.index.tolist() sizes level_counts.values.tolist() colors [#66c2a5, #fc8d62, #8da0cb, #e78ac3] explode (0, 0, 0, 0.1) # 突出“重度污染” fig, (ax1, ax2) plt.subplots(1, 2, figsize(14, 6)) # 子图1饼图 wedges, texts, autotexts ax1.pie(sizes, explodeexplode, labelslabels, colorscolors, autopct%1.1f%%, startangle90, shadowTrue) for autotext in autotexts: autotext.set_color(white) autotext.set_fontweight(bold) ax1.set_title(污染等级分布饼图, fontsize14, fontweightbold) ax1.axis(equal) # 子图2条形图作为对比/补充 bars ax2.bar(labels, sizes, colorcolors, edgecolorblack) ax2.set_ylabel(采样点数量, fontsize12) ax2.set_title(污染等级分布条形图, fontsize14, fontweightbold) for bar in bars: height bar.get_height() ax2.text(bar.get_x() bar.get_width()/2., height 0.5, f{int(height)}, hacenter, vabottom, fontsize11) ax2.grid(axisy, linestyle--, alpha0.7) plt.suptitle(城市表层土壤重金属污染分析结果, fontsize16, fontweightbold) plt.tight_layout() plt.savefig(pollution_analysis_result.png, dpi300, bbox_inchestight) plt.show() print(\n分析完成结果已保存至 pollution_analysis_result.png)这个脚本整合了所有核心操作并输出了一个包含饼图和条形图的对比可视化结果更全面地展示了数据。9. 常见问题与排查技巧实录在实际操作中你肯定会遇到各种报错和意外情况。下面是我总结的一些典型问题及其解决方法。9.1 数据加载与类型问题问题用pd.read_csv加载数据后某些数值列被识别成了object类型无法进行数学运算。排查立即使用df.info()和df.head()查看列数据类型和前几行数据。很可能数据中混入了非数字字符如“-”、“NA”、“0.01”。解决指定数据类型pd.read_csv(data.csv, dtype{浓度列: float})强制转换并处理错误pd.to_numeric(df[列名], errorscoerce)errorscoerce会将无法转换的值设为NaN。清洗数据使用df[列名].replace([-, NA], np.nan, inplaceTrue)替换特定字符串。9.2 GroupBy操作结果异常问题分组后得到的统计量全是NaN或者分组数量远多于预期。排查检查分组键列是否存在大量NaN。groupby默认会忽略NaN但如果你希望NaN自成一组需要先填充或使用dropnaFalse参数但需注意这可能会将多个NaN行归为一组。检查分组键列的数据类型。如果是字符串注意首尾空格df[列名].str.strip()。如果是数值型但想作为分类确认其唯一值数量是否合理。使用df[分组列].value_counts(dropnaFalse)查看具体分布。解决在分组前进行数据清洗确保分组键列干净、类型正确。9.3 Apply函数运行缓慢或内存溢出问题对大数据集使用apply(axis1)进行行计算时程序变得极慢甚至崩溃。排查apply是灵活的但本质上是Python级循环对于大规模数据效率低下。解决优先寻找向量化方法这是根本解决方案。回顾第5节大部分行计算都能转化为列与列之间的向量运算。使用Swifter库对于复杂的、确实无法向量化的函数可以尝试import swifter; df.swifter.apply(...)它尝试利用多核并行加速。分批处理如果数据太大考虑使用df.iloc[start:end]进行分批处理。9.4 绘图时中文显示为方框问题在Matplotlib图表中中文字符显示为“□□□”。解决指定中文字体推荐如前面代码所示在绘图前设置plt.rcParams。你需要确保系统中存在指定的字体如‘SimHei’是黑体‘Microsoft YaHei’是微软雅黑。更稳健的方法指定字体路径import matplotlib zh_font matplotlib.font_manager.FontProperties(fnameC:/Windows/Fonts/msyh.ttc) # 指定字体文件路径 # 然后在需要设置字体的地方传入fontproperties参数如 ax.set_title(标题, fontpropertieszh_font) ax.legend(..., propzh_font)9.5 保存的图片分辨率不足或布局错乱问题保存的PNG图片在论文或报告中放大后模糊或者饼图标签被截断。解决提高DPIplt.savefig(output.png, dpi300)。DPI每英寸点数越高图像越清晰文件也越大。300 DPI通常满足印刷要求。使用bbox_inchestight这个参数会自动裁剪图片周围的空白区域并确保所有图形元素如标签、图例都被包含在保存的图像中。plt.savefig(output.png, dpi300, bbox_inchestight)。调整图形尺寸在创建图形时使用fig, ax plt.subplots(figsize(width, height))提前设置合适的宽高比。9.6 性能优化小技巧当处理非常大的DataFrame时比如数十万行以上一些操作会变慢。除了前面提到的向量化还有以下技巧选择合适的数据类型使用df[列名].astype(category)将低基数唯一值少的字符串列转换为分类类型可以大幅节省内存和提高groupby速度。避免链式赋值类似df[df[A]0][B] 1的操作可能引发SettingWithCopyWarning且效率不高。应使用df.loc[df[A]0, B] 1。使用查询Query对于复杂的布尔索引df.query(A 10 B 5)有时比df[(df[A]10) (df[B]5)]更清晰且在某些情况下性能略优。数据处理和分析是一个“脏活累活”80%的时间可能花在数据清洗和调试上。掌握这些常见的排查思路和技巧能让你在遇到问题时不再慌张快速定位并解决。记住清晰的思路和稳健的代码习惯比任何炫酷的技巧都更重要。从创建一个干净的数据副本开始一步步验证你的分析之路就会顺畅很多。