尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python数据规整实战:连接、联合与重塑三大核心操作详解

Python数据规整实战:连接、联合与重塑三大核心操作详解 1. 项目概述数据规整的核心价值在数据分析的日常工作中我们拿到手的数据很少是“完美”的。它们可能散落在多个Excel文件里可能来自不同的数据库表也可能因为业务系统的原因同一份数据被拆成了多个结构相似但内容不同的表格。这时候如果你只会用pandas的read_csv和describe工作就会陷入僵局。数据规整或者说数据预处理就是打通这“最后一公里”的关键技能。它不像建模算法那样充满数学美感但却是决定分析结果可靠性和效率的基石。《利用Python进行数据分析》这本书的第八章深入探讨了数据规整中几个最核心的操作连接、联合与重塑。这不仅仅是几个函数的简单罗列而是一套处理复杂数据关系的“组合拳”。连接Join解决的是如何根据键值将不同数据集的行关联起来类似于数据库的表连接联合Concatenation处理的是将结构相同的数据沿某个轴行或列堆叠在一起而重塑Reshaping则是改变数据表格的布局比如将“宽格式”数据转换为“长格式”以满足不同分析工具如统计绘图或机器学习库的输入要求。掌握这些技能意味着你能从容应对业务部门丢过来的各种“脏乱差”数据将碎片化的信息整合成一张清晰、完整的数据“地图”为后续的探索性分析和建模打下坚实基础。接下来我将结合多年实战经验为你拆解这三大操作的原理、应用场景以及那些官方文档里不会写的“避坑指南”。2. 数据连接Join从关系代数到业务逻辑数据连接是数据规整中最常用、也最容易出错的操作之一。它的核心思想源于关系型数据库的SQL JOIN但在pandas中有了更灵活的实现。理解连接首先要理解“键”Key。2.1 连接的类型与选择逻辑pandas主要提供了四种基本的连接类型它们对应着SQL中的不同JOIN操作选择哪一种完全取决于你的业务问题。1. 内连接Inner Join这是最严格的一种连接。它只返回两个数据集中键值完全匹配的行。想象一下你有两张表一张是“订单表”一张是“客户信息表”。内连接就相当于问“哪些客户下了订单” 结果中只会包含既有订单记录、又有客户信息的客户。如果你只关心发生了交互的实体内连接是最佳选择它能确保结果集中没有缺失值在连接键上。2. 左连接Left Join左连接以左边的数据集为基准。它会返回左表的所有行即使在右表中没有匹配的行。对于右表中没有匹配项的行结果中对应的列会填充为缺失值NaN。继续上面的例子左连接以订单表为左表相当于问“所有订单都是谁下的对于那些找不到对应客户的订单我们也保留记录。” 这在业务中非常常见比如你需要分析所有订单即使用户信息表可能因为数据同步问题缺失了部分新客户。3. 右连接Right Join与左连接相反它以右表为基准。在实际工作中右连接的使用频率远低于左连接因为你通常可以通过调换两个表的顺序然后用左连接来实现同样的效果。代码的可读性更重要所以通常建议统一使用左连接并通过明确指定哪个是“主表”来控制逻辑。4. 外连接Outer Join / Full Outer Join外连接是左连接和右连接的并集。它返回两个表中所有的行当某一行在另一个表中没有匹配时就用NaN填充。这相当于问“请列出所有订单和所有客户并把能关联上的信息对齐。” 外连接能给你最完整的数据视图但也会引入大量的缺失值后续需要仔细处理。实操心得连接类型的选择口诀我个人的经验是在业务分析中80%的情况你会使用左连接。因为你通常有一个“事实表”或“主表”如交易记录、日志流水然后去关联各种“维度表”如用户属性、产品目录。左连接能确保你的主分析对象不丢失任何记录。内连接常用于数据清洗后、构建分析模型前的确定数据集。外连接则更多用于数据探查或完整性检查比如找出哪些客户没有订单或者哪些产品从未被购买。2.2pd.merge()你的主力连接函数pandas的pd.merge()函数是执行连接操作的瑞士军刀。它的参数看起来很繁杂但掌握几个核心的就能应对绝大多数场景。import pandas as pd # 示例数据员工表和部门表 df_employees pd.DataFrame({ emp_id: [1, 2, 3, 4], name: [Alice, Bob, Charlie, David], dept_id: [101, 102, 101, 999] # 注意David的部门999在部门表中不存在 }) df_departments pd.DataFrame({ dept_id: [101, 102, 103], dept_name: [Sales, Engineering, Marketing] }) # 1. 内连接只保留两个表都有的部门员工 df_inner pd.merge(df_employees, df_departments, ondept_id, howinner) print(内连接结果:) print(df_inner) # 输出David (dept_id999) 被过滤掉了 # 2. 左连接保留所有员工没有部门的显示NaN df_left pd.merge(df_employees, df_departments, ondept_id, howleft) print(\n左连接结果:) print(df_left) # 输出David的dept_name为NaN # 3. 处理重复键名suffixes参数 # 如果两个表有同名的非连接键列会自动加后缀区分 df_emp_extra pd.DataFrame({ emp_id: [1, 2, 3, 4], value: [100, 200, 300, 400] }) df_dept_extra pd.DataFrame({ dept_id: [101, 102, 103], value: [500, 600, 700] # 同名列 }) df_merge_suffix pd.merge(df_emp_extra, df_dept_extra, left_onemp_id, right_ondept_id, howinner, suffixes(_emp, _dept)) print(\n使用后缀区分的连接:) print(df_merge_suffix)关键参数解析on: 最简单的用法当两个表的连接键列名相同时使用。left_on/right_on: 当两个表的连接键列名不同时分别指定左表和右表的键列名。这是非常常见的情况比如一个表里叫user_id另一个表里叫customer_id。how: 指定连接类型即inner、left、right、outer。suffixes: 处理重复列名的救命稻草。默认是(_x, _y)但为了可读性我强烈建议根据业务含义自定义如(_from_order, _from_user)。2.3 多键连接与索引连接现实中的数据关系往往更复杂有时需要多个字段共同确定一条关联关系。# 示例销售记录表需要同时按‘年份’和‘产品ID’连接库存表 df_sales pd.DataFrame({ year: [2023, 2023, 2024, 2024], product_id: [A, B, A, C], sales: [150, 200, 180, 90] }) df_inventory pd.DataFrame({ year: [2023, 2023, 2024, 2024], product_id: [A, B, A, B], # 注意2024年产品C在库存表中不存在 stock: [300, 250, 320, 270] }) # 多键连接 df_multi_key pd.merge(df_sales, df_inventory, on[year, product_id], howleft) print(多键左连接结果:) print(df_multi_key) # 2024年产品C的库存为NaN另一种高效的做法是使用索引进行连接。如果某个列或复合列在数据集中具有唯一性且频繁用于查询和连接将其设置为索引可以提升性能并使代码更简洁。# 将部门表的‘dept_id’设为索引 df_departments_indexed df_departments.set_index(dept_id) # 使用left_index和right_on进行连接 df_merge_with_index pd.merge(df_employees, df_departments_indexed, left_ondept_id, right_indexTrue, howleft) print(\n使用索引连接的结果:) print(df_merge_with_index)注意事项连接的性能陷阱键值唯一性检查在连接前务必检查连接键在右表中的唯一性。如果右表存在重复键连接操作会产生笛卡尔积导致结果行数爆炸式增长。使用df[right_key].is_unique或df[right_key].duplicated().any()进行检查。数据类型必须一致连接键的数据类型必须完全相同。一个常见的坑是一个表中的user_id是整数int另一个表中却是字符串object可能是因为前面有字母前缀。这会导致连接失败或结果为空。务必先用df[key].dtype检查并统一类型。处理缺失键左连接后产生的NaN行需要根据业务决定是删除、填充默认值还是标记为异常。盲目删除可能会丢失重要信息如新上线的产品尚无销售记录。3. 数据联合Concatenation数据的堆叠艺术如果说连接是关于“横向”的扩展增加列那么联合就是关于“纵向”或“横向”的堆叠增加行或列。它适用于合并多个结构相同或相似的数据集。3.1pd.concat()轴向堆叠pd.concat()是处理这类任务的主要工具其核心参数是axis。# 示例三个月的销售数据结构相同 df_jan pd.DataFrame({product: [A, B], sales_jan: [100, 150]}) df_feb pd.DataFrame({product: [A, B], sales_feb: [120, 130]}) df_mar pd.DataFrame({product: [A, B], sales_mar: [95, 160]}) # 1. 纵向堆叠axis0追加行 # 假设我们有一个包含所有月份销售记录的列表 df_list [df_jan, df_feb, df_mar] # 直接concat会出问题因为列名不同 df_v_concat_naive pd.concat(df_list) print(纵向堆叠列名不同产生NaN:) print(df_v_concat_naive) # 更常见的场景统一列名然后堆叠 df_jan_renamed df_jan.rename(columns{sales_jan: sales}).assign(monthJan) df_feb_renamed df_feb.rename(columns{sales_feb: sales}).assign(monthFeb) df_mar_renamed df_mar.rename(columns{sales_mar: sales}).assign(monthMar) df_combined pd.concat([df_jan_renamed, df_feb_renamed, df_mar_renamed], ignore_indexTrue) print(\n统一格式后的纵向堆叠:) print(df_combined) # 2. 横向堆叠axis1拼接列 # 注意横向拼接要求行索引对齐。这里我们以‘product’为索引 df_jan_idx df_jan.set_index(product) df_feb_idx df_feb.set_index(product) df_mar_idx df_mar.set_index(product) df_h_concat pd.concat([df_jan_idx, df_feb_idx, df_mar_idx], axis1) print(\n横向堆叠按索引对齐:) print(df_h_concat)关键参数解析axis:0或index表示纵向堆叠默认1或columns表示横向堆叠。ignore_index: 当axis0时如果设为True会忽略原有索引生成一个新的连续整数索引。这通常是想要的结果。join: 当axis1横向堆叠时如果数据框的索引不完全一致joininner会取交集joinouter会取并集用NaN填充。这类似于连接操作。3.2 联合与连接的区别这是初学者容易混淆的地方。简单来说连接Merge基于一个或多个“键”的值将两个数据集中的行有逻辑地组合起来。它关心的是数据之间的关系。联合Concat将两个或多个数据集物理地堆叠在一起可以是上下堆叠增加行也可以是左右堆叠增加列。它不关心行与行之间的逻辑关系只要求结构兼容。一个典型的应用场景是你每天都会导出一份结构完全相同的日志文件一周后你有7个DataFrame。要分析整周的数据你需要的是pd.concat([df_day1, df_day2, ...], axis0)而不是merge。实操心得批量合并文件的技巧我经常需要合并几十个甚至上百个结构相同的CSV或Excel文件。手动一个个read再concat效率太低。一个高效的写法是import os import pandas as pd folder_path ./daily_reports/ all_files [f for f in os.listdir(folder_path) if f.endswith(.csv)] df_list [] for file in all_files: file_path os.path.join(folder_path, file) # 可以在读取时做一些统一处理比如指定列类型、解析日期 df pd.read_csv(file_path, dtype{user_id: str}, parse_dates[date]) df[source_file] file # 添加一列记录来源便于追溯 df_list.append(df) df_combined pd.concat(df_list, ignore_indexTrue) print(f合并了 {len(df_list)} 个文件总行数{len(df_combined)})这个技巧的关键是在合并前就给每个数据块打上“来源标签”这样在后续数据清洗时如果发现问题可以快速定位到原始文件。4. 数据重塑Reshaping透视与逆透视数据重塑指的是改变数据表的结构而不改变其内容。最常见的两种操作是透视Pivot宽表变长表和逆透视Melt长表变宽表。不同的分析工具对数据格式有不同偏好例如许多统计绘图库如seaborn更喜欢“长格式”数据而某些报告则需要“宽格式”的交叉表。4.1pivot与pivot_table创建宽格式数据pivot方法非常简单直观但它要求索引和列的组合是唯一的否则会报错。# 示例长格式销售数据 df_long pd.DataFrame({ date: [2024-01-01, 2024-01-01, 2024-01-02, 2024-01-02], product: [A, B, A, B], sales: [100, 150, 120, 130] }) print(原始长格式数据:) print(df_long) # 使用pivot将‘product’的值变成列名 df_wide_pivot df_long.pivot(indexdate, columnsproduct, valuessales) print(\n使用pivot重塑为宽格式:) print(df_wide_pivot) # 行索引是日期列索引是产品单元格是销售额然而现实数据中经常存在重复项。比如同一天同一个产品可能有多个销售渠道的记录。这时pivot会报错ValueError: Index contains duplicate entries, cannot reshape。解决方案是使用更强大的pivot_table它支持数据聚合。# 示例数据存在重复项 df_long_duplicates pd.DataFrame({ date: [2024-01-01, 2024-01-01, 2024-01-01, 2024-01-02], product: [A, A, B, A], channel: [Online, Offline, Online, Online], sales: [60, 40, 150, 120] }) print(\n包含重复项的长格式数据:) print(df_long_duplicates) # 使用pivot_table对重复的‘date-product’组合进行求和 df_wide_pivot_table df_long_duplicates.pivot_table( indexdate, columnsproduct, valuessales, aggfuncsum, # 聚合函数默认为mean这里用sum fill_value0 # 填充缺失值为0 ) print(\n使用pivot_table聚合求和重塑为宽格式:) print(df_wide_pivot_table) # 2024-01-01产品A的销售额被汇总为100 (6040)pivot_table的功能非常强大几乎可以替代Excel的数据透视表。aggfunc参数可以接受字符串如sum,mean,count、函数如np.std甚至函数列表[sum, mean]实现多维度聚合。4.2melt宽格式变长格式melt是pivot的逆操作。当你拿到一份“很宽”的表格比如月份作为列名Jan,Feb,Mar, ...而你需要按月份进行分析或绘图时就需要melt将其变“长”。# 示例宽格式数据各月销售额作为单独的列 df_wide pd.DataFrame({ product: [A, B, C], Jan: [100, 150, 80], Feb: [120, 130, 90], Mar: [95, 160, 85] }) print(原始宽格式数据:) print(df_wide) # 使用melt进行逆透视 df_long_melted df_wide.melt( id_vars[product], # 要保留的标识列不被转换的列 value_vars[Jan, Feb, Mar], # 需要被“融化”成行的列 var_namemonth, # 新生成的变量列的名称原来列名的去处 value_namesales # 新生成的值列的名称原来单元格值的去处 ) print(\n使用melt重塑为长格式:) print(df_long_melted.sort_values([product, month]).reset_index(dropTrue))关键参数解析id_vars: 指定哪些列是标识符不需要被“融化”。这些列会在结果中重复出现。value_vars: 指定哪些列需要被“融化”成两列变量列和值列。如果不指定默认会融化所有不在id_vars中的列。var_name/value_name: 为新生成的列命名让数据更易读。注意事项重塑中的数据陷阱pivot的唯一性约束在使用pivot前一定要用df.duplicated(subset[‘index_col’, ‘columns_col’]).any()检查(index, columns)组合是否唯一。如果不唯一果断改用pivot_table并指定aggfunc。pivot_table的聚合逻辑明确你的业务需求。对销售额求和是合理的但对温度求平均值是合理的对ID求和则毫无意义。选择合适的聚合函数。melt后的数据类型被melt的列value_vars通常数据类型相同比如都是销售额。如果这些列数据类型混杂如有整数、有浮点数、有字符串melt后生成的value列可能会变成object类型影响后续计算。最好在melt前统一数据类型。性能考虑对非常大的数据集进行重塑尤其是pivot_table涉及复杂聚合可能会消耗大量内存。可以考虑分块处理或使用Dask等库。5. 高级规整技巧与实战模式掌握了连接、联合、重塑这三板斧你已经能解决大部分规整问题。但在复杂业务场景下还需要一些组合技和高级技巧。5.1 多层索引MultiIndex的连接与重塑当数据具有多个层级维度时如“年-月-日”、“国家-省份-城市”多层索引就非常有用。连接和重塑操作也支持多层索引。# 创建具有多层列索引的DataFrame index pd.MultiIndex.from_tuples([(A, x), (A, y), (B, x)], names[first, second]) df_multi_col pd.DataFrame({data1: [1, 2, 3], data2: [4, 5, 6]}, indexindex).T print(具有多层列索引的DataFrame:) print(df_multi_col) # 连接时可以指定多层索引的层级 df_left_multi pd.DataFrame({value: [10, 20, 30]}, indexindex) df_right_multi pd.DataFrame({value: [40, 50, 60]}, indexindex) # 连接操作与单层索引类似 df_merged_multi pd.merge(df_left_multi, df_right_multi, left_indexTrue, right_indexTrue, suffixes(_left, _right)) print(\n基于多层索引的连接:) print(df_merged_multi) # 重塑操作stack和unstack # stack: 将列索引的某一层“压缩”到行索引使数据变“长” df_stacked df_multi_col.stack(levelfirst) # 或 level0 print(\n使用stack将列索引‘first’层压到行索引:) print(df_stacked) # unstack: 将行索引的某一层“展开”到列索引使数据变“宽” df_unstacked df_stacked.unstack(levelsecond) # 或 level1 print(\n使用unstack将行索引‘second’层展开到列索引:) print(df_unstacked)stack和unstack是处理多层索引重塑的利器可以非常灵活地在“长格式”和“宽格式”之间切换特别适用于面板数据Panel Data或交叉表数据的处理。5.2 条件连接与模糊匹配标准的merge是基于精确匹配。但有时业务逻辑需要更灵活的连接条件比如基于范围匹配如连接时间戳落在某个时间段内的记录或者基于字符串相似度匹配如连接公司名称但两边写法略有不同。pandas本身不直接支持这种“非等值连接”但我们可以通过一些技巧实现。范围连接示例基于数值# 假设有一个事件时间表和一个状态时间段表 df_events pd.DataFrame({ event_id: [1, 2, 3], event_time: pd.to_datetime([2024-01-01 10:00, 2024-01-01 14:30, 2024-01-02 09:15]) }) df_status_periods pd.DataFrame({ status: [Morning, Afternoon, Night], start_hour: [6, 12, 18], end_hour: [12, 18, 6] # 注意Night跨天 }) # 为事件表提取小时 df_events[hour] df_events[event_time].dt.hour # 使用条件连接通过笛卡尔积过滤实现 # 1. 先创建一个临时的“键”用于笛卡尔积 df_events[key] 1 df_status_periods[key] 1 # 2. 进行全连接笛卡尔积 df_cartesian pd.merge(df_events, df_status_periods, onkey).drop(key, axis1) # 3. 根据时间范围进行过滤 # 处理跨天的情况如Night: 18点 - 次日6点 def is_in_period(hour, start, end): if start end: return start hour end else: # 跨天 return hour start or hour end mask df_cartesian.apply(lambda row: is_in_period(row[hour], row[start_hour], row[end_hour]), axis1) df_result df_cartesian[mask][[event_id, event_time, status]] print(\n基于时间范围的条件连接结果:) print(df_result)这种方法在数据量不大时可行但效率不高。对于大数据集可以考虑使用专门支持非等值连接的库如pandasql允许写SQL或DuckDB。5.3 利用merge的indicator参数追踪数据来源在进行复杂的数据整合时搞清楚每一行数据最终来自哪个源表非常重要。pd.merge()的indicator参数可以帮我们大忙。df_main pd.DataFrame({id: [1, 2, 3, 4], value_main: [a, b, c, d]}) df_ref pd.DataFrame({id: [3, 4, 5, 6], value_ref: [x, y, z, w]}) # 使用外连接并添加来源指示器 df_merge_with_indicator pd.merge(df_main, df_ref, onid, howouter, indicatorTrue) print(带数据来源指示器的外连接结果:) print(df_merge_with_indicator) # 根据指示器进行筛选 only_in_main df_merge_with_indicator[df_merge_with_indicator[_merge] left_only] only_in_ref df_merge_with_indicator[df_merge_with_indicator[_merge] right_only] in_both df_merge_with_indicator[df_merge_with_indicator[_merge] both] print(f\n仅出现在主表的数据行数: {len(only_in_main)}) print(f仅出现在参考表的数据行数: {len(only_in_ref)}) print(f在两个表都出现的数据行数: {len(in_both)})这个功能在数据质量检查和数据同步场景下极其有用可以快速定位数据差异。6. 性能优化与内存管理当处理GB级别甚至更大的数据集时规整操作的性能就变得至关重要。不当的操作可能导致内存溢出或运行时间过长。6.1 选择合适的数据类型pandas默认的数据类型可能不是最节省内存的。例如一个文本列即使只包含‘Yes’/‘No’也会被存储为object类型Python字符串占用大量内存。将其转换为category类型可以大幅减少内存占用。# 查看数据类型和内存使用 df pd.DataFrame({col1: [A, B, A, C] * 100000, col2: range(400000)}) print(转换前内存使用:) print(df.info(memory_usagedeep)) # 转换对象列为类别型 df[col1] df[col1].astype(category) print(\n转换后内存使用:) print(df.info(memory_usagedeep))对于整数列如果其值范围有限可以使用int8,int16,int32等子类型。对于浮点数列可以考虑使用float32代替默认的float64。使用pd.to_numeric(df[col], downcastinteger/float)可以进行自动向下转换。6.2 避免链式赋值与使用.loc链式赋值如df[df[a] 0][b] 1在pandas中可能会产生不可预知的行为或触发SettingWithCopyWarning。正确的做法是使用.loc进行明确索引。# 不推荐链式赋值 # df[df[sales] 100][flag] High # 可能无效或产生警告 # 推荐使用.loc进行单次赋值 df.loc[df[sales] 100, flag] High6.3 使用迭代器与分块处理对于无法一次性读入内存的超大文件可以使用pandas的chunksize参数进行分块读取和处理。chunk_size 100000 # 每次读取10万行 processed_chunks [] for chunk in pd.read_csv(huge_file.csv, chunksizechunk_size, dtype{id: str}): # 对每个数据块进行规整操作例如过滤、连接如果连接表可以放入内存 chunk_processed pd.merge(chunk, df_small_lookup_table, onkey, howleft) processed_chunks.append(chunk_processed) # 最后将所有处理好的块合并起来 df_final pd.concat(processed_chunks, ignore_indexTrue)6.4 考虑使用更高效的工具当pandas遇到性能瓶颈时可以考虑以下替代或补充方案Modin/Dask: 这些库提供了与pandas类似的API但可以利用多核CPU甚至分布式集群进行并行计算加速大规模数据处理。DuckDB/polars: 这是新兴的高性能数据分析库。DuckDB是一种进程内SQL OLAP数据库执行复杂连接和聚合非常快。polars是一个用Rust编写的DataFrame库其惰性执行和查询优化引擎在处理大数据时性能显著优于pandas。数据库: 对于极其复杂和耗时的连接操作如果数据已经存储在数据库中如PostgreSQL, MySQL直接使用SQL进行预处理可能是更高效的选择然后再将结果导入pandas进行后续分析。7. 常见问题排查与调试实录即使对原理了然于胸在实际操作中依然会遇到各种“坑”。下面是我总结的一些典型问题及其解决方法。7.1 连接结果为空或行数异常这是最常见的问题。请按以下清单排查检查连接键数据类型这是头号杀手。使用df1[key].dtype和df2[key].dtype对比。常见问题包括字符串vs整数、日期格式不一致、字符串首尾空格等。使用df[key] df[key].astype(str).str.strip()进行清洗和转换。检查连接键唯一性使用df[key].is_unique检查右表。如果右表键不唯一左连接会产生多行内连接和外连接会产生笛卡尔积导致行数激增。确认连接类型how参数你确定你要的是左连接吗是不是误用了内连接导致很多行被过滤掉了回顾业务逻辑。检查是否存在NaN连接键中的NaN值不会相互匹配。使用df[key].isna().sum()检查缺失情况并决定是填充还是删除。7.2 重塑时出现ValueError或结果不符合预期pivot报错“duplicate entries”立即改用pivot_table并指定聚合函数aggfunc。先思考业务逻辑对于重复的索引-列组合你是想求和、求平均、取第一个还是其他melt后数据格式混乱检查id_vars和value_vars参数是否设置正确。确保value_vars中的列是你真正想“融化”的数值列而id_vars中的列是标识列。如果value_vars中的列数据类型不一致结果列可能会变成object类型。多层索引操作后索引混乱使用reset_index()将多层索引变回普通列或者使用stack/unstack的level参数精确控制操作层级。df.columns和df.index的属性在操作后要随时查看。7.3 内存不足MemoryError优化数据类型如上文所述将object转为category将数值列向下转换downcast。选择性读取列使用pd.read_csv(usecols[col1, col2])只读入需要的列。分块处理如上文所述使用chunksize。删除中间变量在处理流水线中及时删除不再需要的大DataFrame使用del df_large并调用gc.collect()手动触发垃圾回收。使用更高效的数据结构考虑是否真的需要pandasDataFrame对于纯数值计算numpy数组更节省内存。对于复杂的连接查询也许该用SQL数据库了。7.4 性能缓慢连接键没有索引对于需要反复连接的大型表在连接键上设置索引可以极大提升速度df.set_index(key, inplaceTrue)。但注意这会改变DataFrame的结构。避免在循环中进行连接如果需要在循环中多次与同一个大表连接应先将大表读入内存并建立索引而不是每次循环都重新读取和连接。使用更快的连接方法对于简单的按索引合并df1.join(df2)比pd.merge(df1, df2, left_indexTrue, right_indexTrue)语法更简洁性能也可能略有差异通常join更快一点。考虑使用pandas的merge的sort参数对于已经按连接键排序的数据设置sortFalse可以避免不必要的排序开销。数据规整是数据分析工作中既繁琐又极其重要的一环。它没有炫酷的模型和算法却直接决定了上游数据的质量和下游分析的成败。我个人的体会是花在数据规整上的时间往往能数倍地节省后续调试和纠错的时间。建立一个清晰、可复用的数据规整流水线是每个数据分析师从“会用工具”到“专业高效”的必经之路。最后分享一个小技巧为每一个重要的规整步骤特别是复杂的merge或pivot写一段清晰的注释说明这个步骤的业务目的而不仅仅是技术操作三个月后当你或你的同事回头看这段代码时会感谢当初这个决定的。
返回列表