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

资讯详情

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

Python大数据处理:Pandas按行读取Excel/CSV文件实战指南

Python大数据处理:Pandas按行读取Excel/CSV文件实战指南 1. 项目概述为什么我们需要按行读取Excel数据在数学建模、数据分析乃至日常的办公自动化任务中Excel文件几乎是绕不开的数据载体。无论是国赛、美赛还是企业内部的业务分析我们拿到手的原始数据十有八九是.xlsx或.csv格式。传统的做法可能是用Excel手动筛选、分列、透视但对于动辄数万行、数十列的数据或者需要重复性、自动化处理的任务手动操作不仅效率低下而且极易出错。这时Python的pandas库就成了我们的“瑞士军刀”。pd.read_excel()或pd.read_csv()一行代码就能把整个表格读入内存变成一个DataFrame这太方便了。但很多新手甚至是有一定经验的朋友会陷入一个思维定式“读取数据就是一次性把整个文件读进来。”这个想法在数据量小的时候没问题可一旦遇到几百MB甚至上GB的Excel文件内存可能瞬间告急程序直接崩溃。更常见的情况是我们并不需要文件里的所有数据。比如一个包含全年365天销售记录的表格你当前的分析只需要Q24-6月的数据或者一个宽表有上百个字段你只需要其中的三五列。“按需读取”特别是“以行的方式按需读取”就是为了解决这些问题。它意味着我们像用筷子夹菜一样只从数据“大盘子”里取出我们需要的那几“行”而不是把整个盘子端过来。这不仅能极大节省内存还能提升数据加载和后续处理的效率。对于数学建模而言赛题数据往往庞大且复杂高效、精准的数据读取是成功的第一步能为你节省大量时间让你更专注于模型构建与算法实现。接下来我将带你深入拆解如何用Python实现这种精细化的数据读取操作。2. 核心思路与工具选型为什么是Pandas以及迭代读取的奥秘要实现按行读取我们首先得理解数据在Python中是如何被加载的。pandas的read_excel或read_csv函数默认行为是调用底层的解析引擎对于Excel是openpyxl或xlrd对于CSV是Python内置的解析器将文件从头到尾扫描一遍把所有解析出来的数据组织成一个二维表格DataFrame对象全部放入内存。这就是“全量加载”。要实现“按需”思路无非两种先全量加载后切片过滤即df pd.read_excel(‘file.xlsx’)然后再用df.iloc[10:20]或df[df[‘month’] 4]来获取目标行。这种方法简单直观但前提是数据量必须小到足以被完整加载到内存中。它不符合我们处理大文件的场景。迭代式/分块加载这才是处理大数据的正道。核心思想是“化整为零”一次只读取一小部分数据比如1000行到内存处理完这一块后再读取下一块。pandas为此提供了非常优雅的支持。工具选型上pandas是毋庸置疑的首选。尽管Python标准库有csv模块第三方也有openpyxl、xlrd等库可以直接操作Excel单元格但pandas在数据处理的生态和易用性上拥有压倒性优势。它提供的分块读取接口使得按行读取变得异常简单。这里的关键参数是chunksize。当你在read_csv或read_excel函数中指定chunksize1000时函数返回的不再是一个DataFrame而是一个TextFileReader或ExcelFileReader迭代器对象。每次对这个迭代器使用next()函数或进行for循环它就会吐出包含指定行数这里是1000行的一个小DataFrame。注意对于Excel文件.xlsxread_excel函数的chunksize参数并非原生支持所有引擎。更通用、可靠的做法是使用pd.ExcelFile类先打开文件然后配合sheet_name和usecols等参数进行分片读取或者使用openpyxl的只读模式进行迭代。但对于CSV文件chunksize是完美支持的。在实际数学建模中很多数据交换格式就是CSV因此掌握这个方法至关重要。为什么选择迭代读取内存友好无论原文件多大内存中同一时刻只保留一个“块”的数据。灵活性高你可以在迭代过程中实时检查每一块数据并根据行内容决定是否保留、处理或跳过实现了真正的“按需”。可中断与恢复结合状态保存理论上可以处理任意大的文件。3. 实战演练三种按行读取模式的代码实现理论说再多不如代码看一眼。下面我们针对不同场景给出具体的代码示例。假设我们有一个名为sales_data.csv的大文件。3.1 基础模式按固定行数分块读取与处理这是最标准的用法适用于需要对整个文件进行均匀分块处理的场景比如数据清洗、特征计算的初步遍历。import pandas as pd # 定义文件路径和块大小 file_path ‘sales_data.csv’ chunk_size 5000 # 每次读取5000行 # 创建迭代器 chunk_iter pd.read_csv(file_path, chunksizechunk_size) # 初始化一个列表用于存放每个块的处理结果例如每个块的平均值 results [] # 迭代处理每一个数据块 for i, chunk in enumerate(chunk_iter): print(f”正在处理第 {i1} 个数据块形状: {chunk.shape}”) # 示例处理计算当前块中‘sales_amount’列的平均值 chunk_mean chunk[‘sales_amount’].mean() results.append(chunk_mean) # 这里可以添加任何你的处理逻辑比如过滤、计算、写入新文件等 # filtered_chunk chunk[chunk[‘region’] ‘East’] # … 其他操作 … # 所有块处理完成后整合结果 if results: overall_mean sum(results) / len(results) print(f”整个数据集‘sales_amount’的平均值基于分块计算约为: {overall_mean:.2f}”)实操心得chunksize的选择是个平衡艺术。太小如100会导致I/O操作过于频繁拖慢速度太大如10万则可能失去节省内存的意义并导致单块处理时间过长。一般建议从5000到50000之间开始尝试根据你的内存和文件I/O性能调整。在for循环内部chunk就是一个标准的、完整的DataFrame你可以对它使用任何熟悉的pandas操作。3.2 进阶模式基于行内容的条件读取很多时候我们不是均匀地要所有行而是只要满足特定条件的行。例如只要“产品类别”为“电子产品”且“销售额”大于10000的记录。我们可以在迭代过程中进行过滤。import pandas as pd file_path ‘sales_data.csv’ chunk_size 10000 output_rows [] # 用于存放最终需要的行 chunk_iter pd.read_csv(file_path, chunksizechunk_size) for chunk in chunk_iter: # 在当前块中应用过滤条件 filtered_chunk chunk[(chunk[‘product_category’] ‘Electronics’) (chunk[‘sales_amount’] 10000)] # 将过滤后的行添加到结果列表中 if not filtered_chunk.empty: # 使用 pd.concat 来累积结果比 append 到列表再合并更高效 output_rows.append(filtered_chunk) # 将所有过滤后的块合并成一个最终的DataFrame if output_rows: final_df pd.concat(output_rows, ignore_indexTrue) print(f”筛选出 {len(final_df)} 条符合条件的记录。”) # 可以将 final_df 保存到新的CSV或Excel文件 final_df.to_csv(‘filtered_electronics_high_sales.csv’, indexFalse) else: print(“未找到符合条件的记录。”)注意事项这种方法依然会读取文件的每一行但只在内存中保留符合条件的行。对于筛选条件可以过滤掉大部分数据的场景非常高效。如果最终结果final_df仍然很大需要注意合并后的内存占用。可以考虑直接将每个filtered_chunk写入到输出文件中而不是先存储在列表里。3.3 精准模式读取特定行号范围的数据这是最直接的“按行读取”。比如你只需要第1000行到第2000行假设行号从0开始的数据。pandas的read_csv提供了skiprows和nrows参数来实现。import pandas as pd file_path ‘sales_data.csv’ start_row 1000 # 想要起始的行号0-indexed end_row 2000 # 想要结束的行号包含 num_rows_to_read end_row - start_row 1 # 方法跳过前面的 start_row 行然后读取接下来的 num_rows_to_read 行 df_specific_range pd.read_csv(file_path, skiprowsrange(1, start_row 1), nrowsnum_rows_to_read) print(df_specific_range.head())重要提示skiprowsrange(1, start_row1)这里range(1, …)是因为通常CSV第一行是表头。这个参数的意思是跳过从第1行表头之后的第一行数据到第start_row行总共start_row行数据。nrowsnum_rows_to_read指定读取多少行。这种方法有一个巨大缺陷它要求文件必须是CSV这类纯文本格式并且行是定长的或至少可以通过换行符准确分割。对于Excel文件read_excel的skiprows和nrows参数虽然也存在但引擎在底层可能仍然需要解析跳过部分的内容对于超大文件效率提升有限且行为可能因引擎而异。对于精确读取Excel的某几行更推荐使用openpyxl的只读模式。使用openpyxl精确读取Excel行示例from openpyxl import load_workbook file_path ‘sales_data.xlsx’ start_row, end_row 1000, 2000 data_rows [] # 使用 read_only 模式打开这是节省内存的关键 wb load_workbook(filenamefile_path, read_onlyTrue) ws wb.active # 获取第一个工作表或通过名称获取 ws wb[‘Sheet1’] # 只迭代我们需要的行范围 for row in ws.iter_rows(min_rowstart_row, max_rowend_row, values_onlyTrue): # row 是一个元组包含该行所有单元格的值 data_rows.append(row) wb.close() # 将读取的数据转换为DataFrame需要先获取表头 # 假设表头在第一行 wb2 load_workbook(filenamefile_path, read_onlyTrue) ws2 wb2.active headers [cell.value for cell in next(ws2.iter_rows(min_row1, max_row1, values_onlyFalse))] wb2.close() import pandas as pd df_specific_range pd.DataFrame(data_rows, columnsheaders) print(df_specific_range.head())4. 性能优化与避坑指南掌握了基本方法我们来看看如何做得更快、更稳以及有哪些常见的“坑”。4.1 数据类型优化内存节省的关键pandas默认读取数据时会推断每一列的数据类型。为了“安全”它经常将数字列推断为int64或float64将字符串列推断为object。这对于小数据没问题但对于大数据这会浪费大量内存。优化策略在读取时指定dtype参数或读取后立即进行类型转换。# 方式1读取时指定需提前知道列类型 dtype_dict {‘customer_id’: ‘int32’, ‘sales_amount’: ‘float32’, ‘region’: ‘category’} chunk_iter pd.read_csv(file_path, chunksize5000, dtypedtype_dict) # 方式2读取每个块后转换更灵活 for chunk in chunk_iter: chunk[‘customer_id’] chunk[‘customer_id’].astype(‘int32’) chunk[‘region’] chunk[‘region’].astype(‘category’) # … 处理逻辑 …重点推荐category类型对于重复值很多的字符串列如国家、省份、产品类型将其转换为category类型可以节省数倍甚至数十倍的内存。4.2 列筛选只读需要的列这是最有效的优化手段之一。通过usecols参数你可以在读取时就丢弃不需要的列极大减少内存占用和后续处理时间。# 只读取‘date’ ‘product’ ‘sales’这三列 columns_to_use [‘date’ ‘product’ ‘sales’] df pd.read_csv(file_path, usecolscolumns_to_use) # 在分块读取中同样适用 chunk_iter pd.read_csv(file_path, chunksize5000, usecolscolumns_to_use)4.3 处理缺失值与异常值在分块处理时对缺失值和异常值的处理需要特别注意一致性。例如计算全局平均值时如果每块简单地删除缺失值再计算均值最后合并的结果将与整体删除缺失值后计算的结果有细微差别因为每块的样本数不同。通常有两种策略每块独立处理最后合并适用于过滤操作如删除缺失行因为过滤不影响聚合逻辑。先轻量级遍历获取全局信息再分块处理例如先快速遍历一遍所有块获取关键列的全局最小值、最大值、非空计数用于统一确定异常值阈值或插补策略。4.4 常见问题与排查内存还是爆了检查chunksize是否设置得过大尝试调小。检查单块内的操作是否在块内无意中创建了非常大的中间变量如对某列调用.apply一个返回很大对象的函数使用内存分析工具如memory_profiler定位内存消耗大的行。读取速度非常慢引擎选择对于CSV尝试engine‘c’默认C语言实现更快而不是engine‘python’。关闭不必要的推断设置low_memoryFalse让pandas在内部使用更多内存进行优化或明确指定dtype。对于确定没有混合类型的文件设置low_memoryFalse可能提速。I/O瓶颈如果文件在机械硬盘上I/O可能是瓶颈。考虑将文件移到SSD或使用更高效的存储格式如Parquet HDF5。日期时间列解析错误日期格式混乱是常见问题。建议先以字符串形式读入dtype{‘date_col’: ‘str’}然后在块内使用pd.to_datetime(chunk[‘date_col’] format‘%Y-%m-%d’ errors‘coerce’)进行统一转换errors‘coerce’会将解析失败的设为NaTNot a Time避免程序中断。分块处理后的结果合并出错确保每个块处理后的DataFrame具有相同的列结构和顺序再使用pd.concat。使用ignore_indexTrue来重置合并后的索引避免索引重复。5. 在数学建模中的综合应用案例假设你正在准备“亚太杯数学建模竞赛”赛题提供了一个巨大的CSV文件user_behavior_log.csv包含用户ID、时间戳、行为类型、页面ID、停留时长等字段。你需要分析“在促销活动期间假设是4月15日到5月15日核心产品页面page_id以‘P100’开头的用户平均停留时长”。思路与步骤数据范围界定文件巨大必须分块。我们只关心特定时间段和特定页面的数据。按需读取设计使用chunksize分块读取。在每一块中首先将timestamp列转换为日期时间类型。然后应用双重过滤timestamp在活动期间内且page_id以‘P100’开头。增量计算由于是求平均值我们可以采用增量计算法避免存储所有过滤后的数据。只需要累积“总和”与“计数”。import pandas as pd file_path ‘user_behavior_log.csv’ chunk_size 20000 start_date pd.Timestamp(‘2023-04-15’) end_date pd.Timestamp(‘2023-05-15’) total_duration 0 total_count 0 chunk_iter pd.read_csv(file_path, chunksizechunk_size, usecols[‘user_id’ ‘timestamp’ ‘page_id’ ‘duration’] # 只读需要的列 dtype{‘page_id’: ‘str’ ‘duration’: ‘float32’}) # 优化数据类型 for i, chunk in enumerate(chunk_iter): # 转换时间戳 chunk[‘timestamp’] pd.to_datetime(chunk[‘timestamp’] errors‘coerce’) # 过滤数据 mask (chunk[‘timestamp’] start_date) \ (chunk[‘timestamp’] end_date) \ (chunk[‘page_id’].str.startswith(‘P100’ naFalse)) filtered_chunk chunk.loc[mask] if not filtered_chunk.empty: # 增量计算总和与计数 total_duration filtered_chunk[‘duration’].sum() total_count len(filtered_chunk) print(f”已处理前 {(i1)*chunk_size} 行 找到 {total_count} 条有效记录。”) if total_count 0: avg_duration total_duration / total_count print(f”促销活动期间核心产品页面的平均用户停留时长为: {avg_duration:.2f} 秒”) else: print(“未找到符合条件的记录。”)这个案例展示了如何将“按行按需读取”与具体的建模需求紧密结合。通过列筛选、类型优化、条件过滤和增量计算我们能够高效、精准地从海量数据中提取出关键信息而无需担心内存限制。6. 总结与扩展建议按行按需读取Excel/CSV数据本质是一种流式处理Stream Processing思想。它打破了“数据必须全部加载才能处理”的思维定式是处理大规模数据集必备的技能。对于数学建模参赛者来说在赛题公布后的数据探查阶段快速写出这样的脚本能帮你迅速摸清数据底细节省下宝贵的时间。个人经验之谈在比赛开始读取数据阶段我通常会先写一个快速分块探查脚本打印每个块的前几行、统计每列的非空值、唯一值数量、最大值最小值对数据质量有一个全局的快速了解。这比用Excel打开一个几百万行的文件要可靠和快速得多。对于特别复杂的多Sheet Excel文件pd.ExcelFile配合sheet_name参数是更好的选择它可以让你在不重复加载文件的情况下访问不同的工作表。如果团队协作最终的数据处理脚本一定要考虑可复现性。将文件路径、关键参数如chunksize、过滤条件日期放在脚本开头的变量中并添加必要的注释。这样队友或未来的你可以轻松理解和运行你的代码。最后当你的数据操作变得非常复杂和频繁时可以考虑将数据存储到更高效的文件格式中如Parquet或Feather。这些格式支持列式存储和压缩pandas读写速度极快并且能更好地保持数据类型。你可以先用本文的方法从原始CSV/Excel中提取、清洗数据然后将其转换为Parquet格式供后续建模阶段反复快速读取。这往往是专业数据分析流程中的标准做法。
返回列表