news 2026/8/29 11:08:53

Pandas核心概念与实战:从Series/DataFrame到数据清洗与性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pandas核心概念与实战:从Series/DataFrame到数据清洗与性能优化

1. 为什么我们还在聊Pandas?

如果你在数据科学或者数据分析的圈子里待过一阵子,可能会觉得“Pandas”这个话题已经被聊烂了。随便一搜,从“十分钟入门”到“高级技巧”,教程铺天盖地。那我为什么还要写这个“回顾与分享”系列?原因很简单:我发现很多朋友,包括一些工作了几年的同行,对Pandas的使用依然停留在“能跑就行”的层面。他们能写出功能正确的代码,但一遇到稍复杂的数据清洗、性能瓶颈或者需要灵活转换数据形态时,就不得不去Stack Overflow上复制粘贴,知其然不知其所以然。

Pandas远不止是一个用来读取CSV文件的工具。它是一套基于NumPy构建的、用于处理带标签的、关系型或表格型数据的完整编程接口。它的核心设计哲学是提供直观、灵活且高效的数据操作方式。然而,正是这种灵活性,让很多初学者甚至中级使用者感到困惑:为什么有时候用.loc,有时候用.ilocapplyvectorization到底该选哪个?MultiIndex到底该怎么用才不头疼?

这个系列,我想从一个“老用户”和“踩坑者”的角度,和你重新梳理Pandas。我们不追求面面俱到的API手册,而是聚焦于那些真正影响你日常工作效率和代码质量的核心概念、惯用法和性能陷阱。我会结合大量的实际场景,比如从那些热搜词里看到的“天气数据分析”、“字符串分析”、“词频统计”等,把抽象的概念落到实处。目标不是让你记住所有方法,而是帮你建立起对Pandas数据模型的深刻理解,让你在遇到新问题时,能自己推导出优雅的解决方案。

2. 基石:深入理解Series和DataFrame

很多教程一上来就教pd.read_csv,然后马上开始各种操作,这其实有点本末倒置。就像学开车,你得先知道方向盘、油门、刹车是什么,而不是直接上路。Pandas的“方向盘”和“油门”就是SeriesDataFrame

2.1 Series:不只是带索引的数组

你可以把Series简单理解为一个一维数组加一个索引标签。但它的精髓就在这个“索引”上。

import pandas as pd import numpy as np # 从一个列表创建,默认生成整数索引(0, 1, 2...) s1 = pd.Series([10, 20, 30, 40]) print(s1) # 输出: # 0 10 # 1 20 # 2 30 # 3 40 # dtype: int64 # 创建时指定自定义索引 s2 = pd.Series([88, 92, 79, 95], index=['小明', '小红', '小刚', '小丽']) print(s2) # 输出: # 小明 88 # 小红 92 # 小刚 79 # 小丽 95 # dtype: int64

这里的关键理解是:Series的索引是它不可分割的一部分,是数据查找和对齐的键。当你通过索引标签(如s2[‘小红’])访问数据时,Pandas执行的是哈希查找,速度很快,而不是顺序遍历。

一个核心技巧:利用索引进行高效数据对齐。这是Pandas相比纯NumPy数组最大的优势之一。假设我们有两个关于学生成绩的Series:

math_scores = pd.Series({'小明': 88, '小红': 92, '小刚': 79}) english_scores = pd.Series({'小红': 90, '小刚': 85, '小丽': 88}) # 直接相加,Pandas会自动按索引标签对齐 total_scores = math_scores + english_scores print(total_scores) # 输出: # 小明 NaN # 因为english_scores里没有‘小明’ # 小刚 164.0 # 79 + 85 # 小红 182.0 # 92 + 90 # 小丽 NaN # 因为math_scores里没有‘小丽’ # dtype: float64

注意,因为数据对齐,出现了NaN(Not a Number)。这引出了Pandas数据处理的另一个核心:缺失值处理。我们后面会详细讲。

2.2 DataFrame:Series的容器,也是关系表

DataFrame可以看作一个由多个共用同一个索引的Series组成的字典,也可以看作一个二维的、带行列标签的表格。它是我们最常打交道的对象。

# 从一个字典创建,字典的键会成为列名 data = { '姓名': ['小明', '小红', '小刚', '小丽'], '数学': [88, 92, 79, 95], '英语': [78, 90, 85, 88], '班级': ['一班', '一班', '二班', '二班'] } df = pd.DataFrame(data) print(df) # 姓名 数学 英语 班级 # 0 小明 88 78 一班 # 1 小红 92 90 一班 # 2 小刚 79 85 二班 # 3 小丽 95 88 二班

创建时,如果你不指定索引,Pandas会自动生成一个从0开始的整数索引(如上所示)。但更常见的做法是,将某一列具有唯一性的数据设置为索引,比如学号、时间戳等,这能极大提升查询效率。

# 设置‘姓名’列为索引 df.set_index('姓名', inplace=True) print(df) # 数学 英语 班级 # 姓名 # 小明 88 78 一班 # 小红 92 90 一班 # 小刚 79 85 二班 # 小丽 95 88 二班

现在,DataFrame的索引变成了‘姓名’。inplace=True参数表示直接在原数据上修改,而不是返回一个新的DataFrame。这是一个需要谨慎使用的参数,因为它会改变原始数据。我的个人习惯是,除非数据量极大或确定后续不再需要原索引,否则更倾向于使用df = df.set_index(‘姓名’)这种赋值方式,代码意图更清晰。

理解.loc.iloc:这是新手最容易混淆的地方之一。

  • .loc[]:基于标签进行选择。你传入的是索引和列的名称。
  • .iloc[]:基于整数位置进行选择。你传入的是从0开始的整数下标。
# 使用.loc,通过标签获取‘小红’的所有成绩 print(df.loc['小红']) # 数学 92 # 英语 90 # 班级 一班 # Name: 小红, dtype: object # 使用.loc,获取‘小红’的‘数学’成绩(先行后列) print(df.loc['小红', '数学']) # 输出:92 # 使用.iloc,通过位置获取第二行(下标为1)的所有数据 print(df.iloc[1]) # 输出内容与 df.loc[‘小红’] 相同 # 使用.iloc,获取第1行,第0列的数据(下标从0开始) print(df.iloc[1, 0]) # 输出:92

注意:一旦你设置了自定义索引,再使用df[1]这样的整数去切片或选择行就会报错或产生歧义。养成习惯,明确使用.loc.iloc,能让你的代码意图更清晰,也避免很多意想不到的错误。

3. 数据清洗实战:从混乱到规整

我们拿到手的数据,很少是完美无瑕的。数据清洗通常要占据一个数据分析项目70%以上的时间。Pandas提供了一整套强大的工具来处理这些脏数据。

3.1 处理缺失值:识别与策略

缺失值在Pandas中用NaN(浮点数类型)或None(对象类型)表示。首先,我们需要找到它们。

# 创建一个带缺失值的数据 df_missing = pd.DataFrame({ 'A': [1, 2, np.nan, 4], 'B': [5, np.nan, np.nan, 8], 'C': ['a', 'b', 'c', np.nan] # 字符串列中的NaN }) print(df_missing) # A B C # 0 1.0 5.0 a # 1 2.0 NaN b # 2 NaN NaN c # 3 4.0 8.0 NaN # 1. 检查缺失值 print(df_missing.isna()) # 返回布尔型DataFrame print(df_missing.isna().sum()) # 每列缺失值数量 # A 1 # B 2 # C 1 # dtype: int64 # 2. 处理缺失值 # 方法一:删除。谨慎使用,可能丢失大量信息。 df_dropped = df_missing.dropna() # 删除任何包含NaN的行 print(df_dropped) # 只剩第0行 # 更常见的做法是设定阈值 df_dropped_thresh = df_missing.dropna(thresh=2) # 只保留至少有2个非NaN值的行 print(df_dropped_thresh) # 保留了第0,1,3行 # 方法二:填充。 # 用固定值填充 df_filled_constant = df_missing.fillna(0) # 所有NaN填0 # 用前向填充(用上一个有效值填充) df_filled_ffill = df_missing.fillna(method='ffill') # 用后向填充 df_filled_bfill = df_missing.fillna(method='bfill') # 用列的平均值填充(对数值列常用) df_missing['A'].fillna(df_missing['A'].mean(), inplace=True)

选择哪种策略?这完全取决于你的数据和业务逻辑。

  • 删除:适用于缺失值很少,或缺失行本身没有分析价值的情况。
  • 固定值填充:适用于缺失值有明确含义的情况(比如“未知”可填0或-1)。
  • 前向/后向填充:在时间序列数据中非常常用,比如昨天的气温缺失了,用前天的值来近似。
  • 统计值填充:用均值、中位数填充,是最简单粗暴但也最常用的方法之一,但要注意这可能引入偏差。

3.2 数据类型转换:避免隐性错误

数据类型错误是许多诡异Bug的源头。Pandas在读取数据时会自动推断类型,但经常不准。

# 一个常见场景:从CSV读取的“数字”其实是字符串 df_types = pd.DataFrame({ 'id': ['001', '002', '003'], # 应该是整数 'price': ['12.5', '15.0', '9.99'], # 应该是浮点数 'date': ['2023-01-01', '2023-01-02', 'invalid'], # 应该是日期 'is_active': ['True', 'False', 'True'] # 应该是布尔值 }) print(df_types.dtypes) # id object # price object # date object # is_active object # dtype: object # 全是对象(字符串)类型! # 转换数据类型 # 错误示范:直接转换‘price’,因为字符串可以转浮点,所以不会报错,但可能不是你想要的结果 df_types['price'] = df_types['price'].astype(float) # 正确示范:使用 pd.to_numeric,配合 errors 参数处理错误 df_types['price'] = pd.to_numeric(df_types['price'], errors='coerce') # 无法转换的变成NaN print(df_types['price']) # 0 12.50 # 1 15.00 # 2 9.99 # Name: price, dtype: float64 # 转换日期,同样需要错误处理 df_types['date'] = pd.to_datetime(df_types['date'], errors='coerce') print(df_types['date']) # 0 2023-01-01 # 1 2023-01-02 # 2 NaT # 日期类型的缺失值 # Name: date, dtype: datetime64[ns] # 转换布尔值,注意‘True’/‘False’字符串需要映射 df_types['is_active'] = df_types['is_active'].map({'True': True, 'False': False})

实操心得:在数据清洗的第一步,就使用df.info()df.head()查看数据类型和样本。对于任何后续要进行数学运算或比较的列,务必确保其类型是正确的数值型(int,float)或日期时间型(datetime64)。pd.to_numericpd.to_datetime是你的好朋友,务必习惯使用errors=‘coerce’参数,它能把转换失败的值变成NaN,而不是让整个操作崩溃,这样你可以在后续统一处理这些异常值。

3.3 字符串处理:Vectorized String Operations

对于包含文本的列(dtypeobject),Pandas通过.str访问器提供了一套向量化的字符串操作方法,这比在Python里写循环快得多,也优雅得多。这在处理“python pandas 字符串 分析”这类任务时至关重要。

df_text = pd.DataFrame({ 'name': ['Alice Smith', 'bob jones', 'Charlie Brown '], 'email': ['ALICE@example.com', 'Bob@Example.COM', 'charlie@test.org'] }) # 1. 大小写转换与去除空格 df_text['name_clean'] = df_text['name'].str.strip().str.title() # 去空格,首字母大写 df_text['email_lower'] = df_text['email'].str.lower() # 2. 字符串分割与提取 # 提取姓氏(假设姓氏在最后,用空格分割) df_text['last_name'] = df_text['name'].str.split().str[-1] # 提取邮箱域名 df_text['email_domain'] = df_text['email_lower'].str.split('@').str[-1] # 3. 字符串匹配与替换 # 检查邮箱是否来自‘example.com’ df_text['is_example'] = df_text['email_domain'].str.contains('example') # 替换域名 df_text['email_obfuscated'] = df_text['email_lower'].str.replace(r'\.(com|org)$', '.xxx', regex=True) print(df_text[['name', 'name_clean', 'email_domain', 'is_example']])

.str访问器背后是高效的C语言循环,在处理大规模文本数据时优势明显。它支持几乎所有Python标准字符串方法,如len(),find(),startswith(),endswith()等,还支持正则表达式(通过regex=True参数)。

4. 核心操作:筛选、分组与聚合

数据清洗干净后,就进入了分析的深水区:如何从数据中提取信息?

4.1 条件筛选:布尔索引的艺术

这是Pandas中最常用、也最强大的操作之一。其核心思想是:先创建一个布尔值(True/False)的SeriesDataFrame,然后用它来索引原数据。

# 接续之前的学生成绩df (索引为‘姓名’) print(df) # 数学 英语 班级 # 姓名 # 小明 88 78 一班 # 小红 92 90 一班 # 小刚 79 85 二班 # 小丽 95 88 二班 # 筛选出数学成绩大于90的学生 math_gt_90 = df['数学'] > 90 print(math_gt_90) # 姓名 # 小明 False # 小红 True # 小刚 False # 小丽 True # Name: 数学, dtype: bool top_math_students = df[math_gt_90] # 将布尔Series传入df[] print(top_math_students) # 数学 英语 班级 # 姓名 # 小红 92 90 一班 # 小丽 95 88 二班 # 更常见的写法是内联 df[df['数学'] > 90] # 多条件组合:使用 & (与), | (或), ~ (非),注意每个条件要用括号括起来 df[(df['数学'] > 85) & (df['英语'] > 80)] # 数学>85 且 英语>80 df[(df['数学'] > 90) | (df['英语'] > 90)] # 数学>90 或 英语>90 df[~(df['班级'] == '一班')] # 班级不是‘一班’的学生

一个性能陷阱:对于非常大的数据集,这种布尔索引操作可能会创建中间布尔数组,占用大量内存。在极端情况下,可以考虑使用df.query()方法,它有时能更高效地解析表达式,但可读性稍差。

4.2 分组聚合:GroupBy的魔力

GroupBy是Pandas数据分析的“杀手锏”。它的操作可以概括为“Split-Apply-Combine”三步:

  1. Split:根据一个或多个键(列)将数据拆分成多个组。
  2. Apply:对每个分组独立应用一个函数(如求和、求平均)。
  3. Combine:将各组的计算结果合并成一个新的数据结构。
# 按‘班级’分组,并计算各班的数学平均分 grouped_by_class = df.groupby('班级') print(type(grouped_by_class)) # <class 'pandas.core.groupby.generic.DataFrameGroupBy'> # 此时数据已经被“拆分”,但还没有计算 # 应用聚合函数 class_math_avg = grouped_by_class['数学'].mean() # 对‘数学’列求均值 print(class_math_avg) # 班级 # 一班 90.0 # 二班 87.0 # Name: 数学, dtype: float64 # 一次应用多个聚合函数 class_summary = grouped_by_class['数学'].agg(['mean', 'max', 'min', 'count']) print(class_summary) # mean max min count # 班级 # 一班 90.0 92 88 2 # 二班 87.0 95 79 2 # 对不同列应用不同的聚合函数 agg_dict = { '数学': ['mean', 'max'], '英语': 'mean' } class_summary_detail = df.groupby('班级').agg(agg_dict) print(class_summary_detail) # 数学 英语 # mean max mean # 班级 # 一班 90.0 92 84.0 # 二班 87.0 95 86.5

分组键的多样性:分组键可以是列名、Series、数组,甚至是多个键组成的列表(实现多级分组)。例如,df.groupby([‘年级’, ‘班级’])会先按年级分,再在每个年级内按班级分,形成一个层次化索引的结果。

4.3 透视表:更直观的交叉分析

透视表(pivot_table)可以看作是GroupBy的一种更结构化、更直观的输出形式,特别适合制作报告。

# 假设我们有更丰富的销售数据 df_sales = pd.DataFrame({ '日期': pd.date_range('2023-01-01', periods=6, freq='D').tolist() * 2, '产品': ['A', 'A', 'A', 'B', 'B', 'B'] * 2, '区域': ['北区']*6 + ['南区']*6, '销售额': [100, 150, 200, 80, 120, 90, 110, 160, 190, 85, 125, 95] }) # 创建一个透视表:行是‘区域’,列是‘产品’,值是‘销售额’的求和 pivot = pd.pivot_table(df_sales, values='销售额', index='区域', columns='产品', aggfunc='sum') print(pivot) # 产品 A B # 区域 # 北区 450 290 # 南区 460 305 # 更复杂的透视:多级索引和多个聚合函数 pivot_multi = pd.pivot_table(df_sales, values='销售额', index=['区域', '日期'], # 行是多级索引 columns='产品', aggfunc=['sum', 'mean'], # 同时计算总和和均值 fill_value=0) # 填充缺失值为0 print(pivot_multi.head())

透视表能快速让你看到数据在不同维度下的交叉汇总情况,是探索性数据分析(EDA)的利器。

5. 性能优化与高级技巧

当数据量变大(比如超过几十万行),性能问题就会凸显。这里分享几个立竿见影的优化技巧。

5.1 选择正确的数据类型

Pandas默认的整数类型是int64,浮点数是float64。但对于取值范围有限的数据,这会造成巨大的内存浪费。

# 查看内存使用 print(df.info(memory_usage='deep')) # 向下转换数据类型 df['数学'] = df['数学'].astype('int8') # 成绩通常在0-100,int8足够 df['班级'] = df['班级'].astype('category') # 类别数据用category类型,内存和速度都有优化

category类型对于重复值很多的字符串列(如性别、省份、产品类别)特别有效,它能将字符串存储为整数代码,大幅节省内存并提升groupby等操作的速度。

5.2 避免循环,拥抱向量化

这是Pandas性能优化的黄金法则。能用Pandas内置的向量化操作或.apply(),就绝对不要用Python的for循环。

# 慢:Python循环 def calculate_total(row): return row['数学'] + row['英语'] # 不要这样做! # for i in range(len(df)): # df.loc[i, ‘总分’] = calculate_total(df.loc[i]) # 快:向量化操作(最快) df['总分'] = df['数学'] + df['英语'] # 中:使用.apply() (当操作无法直接向量化时) # 假设有一个复杂的函数,需要用到多列 def complex_score(math, english, class_name): # ... 一些复杂逻辑 return math * 0.6 + english * 0.4 + (10 if class_name == ‘一班’ else 0) df['综合分'] = df.apply(lambda row: complex_score(row['数学'], row['英语'], row['班级']), axis=1)

注意事项.apply()虽然比纯Python循环快(因为内部使用了Cython优化),但它仍然是按行或按列迭代,对于超大数据集(数百万行)可能成为瓶颈。如果可能,尽量将逻辑转化为基于NumPy数组的向量化运算。

5.3 高效的数据合并

合并多个DataFrame是常见操作,pd.concat()pd.merge()是最常用的工具。

  • pd.concat():用于沿某个轴(行或列)堆叠多个DataFrame。适用于结构相同的数据追加。
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]}) result_concat = pd.concat([df1, df2], ignore_index=True) # 忽略原索引,生成新索引 print(result_concat)
  • pd.merge():用于基于一个或多个键将两个DataFrame连接起来,类似于SQL的JOIN。这是更常用、也更强大的功能。
left = pd.DataFrame({'key': ['K0', 'K1', 'K2'], 'A': ['A0', 'A1', 'A2']}) right = pd.DataFrame({'key': ['K0', 'K1', 'K3'], 'B': ['B0', 'B1', 'B3']}) # 内连接(默认) inner_merge = pd.merge(left, right, on='key') print(inner_merge) # 只保留两个表都有的key(K0, K1) # 左连接 left_merge = pd.merge(left, right, on='key', how='left') print(left_merge) # 保留左表所有行,右表没有的填NaN # 外连接 outer_merge = pd.merge(left, right, on='key', how='outer') print(outer_merge) # 保留所有key,缺失值填NaN

merge的关键参数

  • on:用于连接的列名。如果两边列名不同,用left_onright_on
  • how:连接方式,‘inner’,‘left’,‘right’,‘outer’
  • suffixes:当两个表有同名列但不是连接键时,用于区分列名的后缀。

理解并熟练使用merge,是进行复杂数据整合的基础。

6. 实战:一个完整的数据分析小案例

让我们用一个接近热搜词“python pandas 石家庄 天气 气数 分析”的简化案例,串联起上面讲到的部分知识点。假设我们有一个weather.csv文件,记录了某城市一段时间内的天气数据。

import pandas as pd import numpy as np # 1. 数据加载与初探 df_weather = pd.read_csv('weather.csv') print("数据形状:", df_weather.shape) print(df_weather.head()) print(df_weather.info()) # 假设列包括:date, city, temp_max, temp_min, precipitation, wind_speed # 2. 数据清洗 # 检查缺失值 print(df_weather.isna().sum()) # 假设发现precipitation有少量缺失,用0填充(假设无降水记为0) df_weather['precipitation'].fillna(0, inplace=True) # 转换日期类型 df_weather['date'] = pd.to_datetime(df_weather['date']) # 3. 数据分析 # 计算每日平均温度 df_weather['temp_avg'] = (df_weather['temp_max'] + df_weather['temp_min']) / 2 # 按城市分组,分析气候差异(假设数据中有多个城市) city_stats = df_weather.groupby('city').agg({ 'temp_avg': 'mean', 'temp_max': 'max', 'temp_min': 'min', 'precipitation': 'sum' }).round(2) city_stats.columns = ['平均气温', '最高气温', '最低气温', '总降水量'] print(city_stats) # 4. 高级筛选与聚合 # 找出所有降水量大于10mm的雨天 rainy_days = df_weather[df_weather['precipitation'] > 10] print(f"雨天共有 {len(rainy_days)} 天") # 按月份统计平均气温和总降水量 df_weather['month'] = df_weather['date'].dt.month monthly_stats = df_weather.groupby('month').agg({ 'temp_avg': 'mean', 'precipitation': 'sum' }) print(monthly_stats) # 5. 简单可视化 (Pandas内置绘图,适合快速探索) import matplotlib.pyplot as plt plt.style.use('seaborn-v0_8-whitegrid') # 使用好看的样式 monthly_stats['temp_avg'].plot(kind='line', title='月平均气温变化', figsize=(10,6)) plt.ylabel('平均气温 (°C)') plt.xlabel('月份') plt.show() # 创建一个透视表,查看各城市每月的降水量 pivot_rain = pd.pivot_table(df_weather, values='precipitation', index='month', columns='city', aggfunc='sum', fill_value=0) print(pivot_rain)

这个案例涵盖了从数据读取、清洗、转换、分组聚合到简单可视化的完整流程。你会发现,大部分工作都是通过Pandas清晰、链式的方法调用完成的,这正是Pandas生产力之所在。

7. 常见问题与排查技巧实录

在实际使用中,你肯定会遇到各种报错和奇怪的行为。这里记录几个高频问题。

问题1:SettingWithCopyWarning警告这是Pandas最著名的“坑”之一。它警告你,你的操作可能是在一个副本(view)上修改数据,而不是原始数据(original)。

# 触发警告的典型代码 df_subset = df[df['数学'] > 80] df_subset['等级'] = '优秀' # 这里会弹出 SettingWithCopyWarning

原因与解决:Pandas不确定df_subset是原始数据的一个切片(view)还是一个独立的副本(copy)。直接在上面赋值可能无法修改原df。为了避免歧义和潜在错误,Pandas发出警告。

  • 解决方案A(推荐):使用.loc进行链式赋值。
    df.loc[df[‘数学’] > 80, ‘等级’] = ‘优秀’
  • 解决方案B:如果你明确需要副本并修改它,就显式地复制。
    df_subset = df[df[‘数学’] > 80].copy() df_subset[‘等级’] = ‘优秀’

问题2:合并数据后出现意外的重复行或NaN在使用pd.merge()时,如果连接键在左右两边不唯一,会产生笛卡尔积,导致行数爆炸。

left = pd.DataFrame({'key': [1, 1, 2], 'A': ['a1', 'a2', 'a3']}) right = pd.DataFrame({'key': [1, 1, 3], 'B': ['b1', 'b2', 'b3']}) result = pd.merge(left, right, on='key', how='inner') print(result) # key A B # 0 1 a1 b1 # 1 1 a1 b2 # 2 1 a2 b1 # 3 1 a2 b2

左表的key=1对应两行,右表的key=1也对应两行,内连接后产生了2*2=4行。

排查:合并前,先用df[‘key’].duplicated().sum()检查连接键的唯一性。如果存在重复,需要想清楚业务逻辑上应该如何处理(比如先做聚合,或者使用其他键)。

问题3:使用.apply()速度极慢如前所述,.apply()是迭代操作。如果函数本身很复杂,或者数据量巨大,就会成为瓶颈。

排查与优化

  1. 检查函数:你的自定义函数里有没有低效的操作?比如在函数内部又调用了其他.apply
  2. 能否向量化?:这是首要考虑。很多数学和逻辑运算可以直接在列上进行。
  3. 使用swifter:这是一个第三方库,可以自动判断并选择对.apply()进行并行化处理,有时能显著提速。
  4. 终极方案:对于超大规模数据,考虑使用DaskModin这类并行计算库,或者将数据导入真正的数据库(如SQLite, PostgreSQL)中用SQL处理。

问题4:读取大文件内存不足使用pd.read_csv()读取几个G的CSV文件很容易撑爆内存。

解决策略

  • 分块读取:使用chunksize参数。
    chunk_iter = pd.read_csv(‘large_file.csv’, chunksize=100000) for chunk in chunk_iter: process(chunk) # 对每个块进行处理
  • 指定数据类型:在读取时就用dtype参数指定每列的类型,避免Pandas自动推断占用更多内存。
  • 只读取需要的列:使用usecols参数。
  • 考虑其他格式:对于静态分析,ParquetFeather格式比CSV读写更快、更省空间。

Pandas是一个功能极其丰富的库,一篇文章无法穷尽。这个“回顾与分享(一)”主要聚焦于核心数据结构和日常最高频的操作。理解了SeriesDataFrame的本质,掌握了数据清洗、筛选、分组聚合的套路,并时刻警惕性能陷阱,你就已经能解决工作中80%以上的问题了。在后续的分享中,我们会深入更多专题,比如时间序列处理、多层索引(MultiIndex)、样式设置以及如何与数据库、Web API等进行交互。希望这些从实战中总结出的经验,能让你在使用Pandas时更加得心应手。

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

感知先于监督:基于反事实盲点的自包含视觉蒸馏方法

做模型压缩和知识蒸馏时&#xff0c;很多同学都会遇到一个相似的问题&#xff1a;知识蒸馏的流程看起来非常简单&#xff0c;加载一个训练好的大模型当教师&#xff0c;用它的 soft label 训练一个小模型当学生&#xff0c;整个流程就能跑通。但真正把蒸馏用在自己的业务数据上…

作者头像 李华
网站建设 2026/8/29 11:06:42

1.5kW数字可编程AC-DC电源,紧凑设计如何重塑系统供电方案

我最近看到 XP Power 发布了一款 1.5kW 数字可编程 AC-DC 电源的消息&#xff0c;标题很短&#xff0c;但信息密度相当高。这个功率档位非常有意思&#xff1a;比 500W 到 800W 的板载模块高出一截&#xff0c;又不到 3kW 以上需要三相输入的级别&#xff0c;正好卡在系统工程师…

作者头像 李华
网站建设 2026/8/29 11:05:59

慢速英语故事怎么精听?从裸听到跟读的完整听力训练法

这类标题看起来像童话故事&#xff0c;实际上是非常典型的慢速英语学习素材。慢速英语故事视频最值钱的不是情节&#xff0c;而是它把日常对话拉到你能听清的速度&#xff0c;让"认识但听不懂"的问题有地方可以解决。如果你看英语文本基本能懂&#xff0c;但裸听同样…

作者头像 李华
网站建设 2026/8/29 11:05:44

容器服务接口怎样约定减少返工

容器服务接口怎样约定减少返工容器化不会自动让接口更稳定。服务拆开后&#xff0c;调用发生在网络、超时、重试和版本演进之中&#xff1b;若契约只写在某个客户端实现里&#xff0c;联调迟早会把分歧暴露出来。减少返工的办法是把输入、输出和失败语义当作正式交付物。 先定义…

作者头像 李华
网站建设 2026/8/29 11:04:05

UNION与UNION ALL:从执行计划看懂SQL去重与性能优化

背八股的时候谁都能说一句“UNION会去重&#xff0c;UNION ALL不去重”&#xff0c;可真到了线上排查慢SQL、写报表统计的时候&#xff0c;翻车的人一抓一大把。我见过太多人因为想当然地选错操作符&#xff0c;导致几千万行的临时表把磁盘撑爆&#xff0c;也见过很多人明明该用…

作者头像 李华
网站建设 2026/8/29 11:02:29

30B本地Agent塞进24GB显存:量化部署与实战指南

把 30B 模型塞进 24GB 显存这件事&#xff0c;放在几个月前&#xff0c;多数人第一反应是“量化之后勉强能跑”。但如果再加一个条件——它不是跑一次对话&#xff0c;而是跑一个具备工具调用、多步规划、记忆管理的本地 Agent&#xff0c;情况就完全不一样了。最近“Meta 重返…

作者头像 李华