📢 转载信息
原文链接:https://www.kdnuggets.com/how-to-handle-large-datasets-in-python-even-if-youre-a-beginner
原文作者:Bala Priya C
Image by Author
# 引言
在 Python 中处理大型数据集时,一个常见的问题是:当你使用 Pandas 加载数据时,程序会变得异常缓慢甚至完全崩溃。这通常是因为你试图一次性将所有数据加载到内存中。
大多数内存问题源于你加载和处理数据的方式。通过一些实用的技巧,你可以处理远超可用内存限制的数据集。
在本文中,你将学习七种在 Python 中高效处理大型数据集的技术。我们将从简单的方法开始逐步深入,直到你完全清楚哪种方法最适合你的用例。
🔗 你可以在 GitHub 上找到代码。如果你愿意,可以运行这个 示例数据生成器 Python 脚本 来获取样本 CSV 文件,并使用代码片段进行处理。
# 1. 分块读取数据
最适合初学者的方法是分块处理数据,而不是一次性加载所有数据。
假设你有一个大型销售数据集,目标是计算总收入。以下代码演示了这种方法:
import pandas as pd # 定义块大小(每块的行数) chunk_size = 100000 total_revenue = 0 # 分块读取和处理文件 for chunk in pd.read_csv('large_sales_data.csv', chunksize=chunk_size): # 处理每个块 total_revenue += chunk['revenue'].sum() print(f"Total Revenue: ${total_revenue:,.2f}")
我们不是一次性加载全部 1000 万行,而是每次加载 10 万行。我们计算每个块的总和,并将其累加到运行总数中。无论文件有多大,你的 RAM 中最多只保留 10 万行。
何时使用: 当你需要对大型文件执行聚合(求和、计数、平均)或筛选操作时。
# 2. 只使用特定列
很多时候,你并不需要数据集中所有的列。只加载你需要的数据可以显著减少内存使用。
假设你正在分析客户数据,但只需要年龄和购买金额,而不是众多其他列:
import pandas as pd # 只加载你真正需要的列 columns_to_use = ['customer_id', 'age', 'purchase_amount'] df = pd.read_csv('customers.csv', usecols=columns_to_use) # 现在处理一个轻量得多的数据框 average_purchase = df.groupby('age')['purchase_amount'].mean() print(average_purchase)
通过指定 usecols,Pandas 只会将这三列加载到内存中。如果你的原始文件有 50 列,那么你的内存使用量就减少了大约 94%。
何时使用: 当你在加载数据之前就确切知道需要哪些列时。
# 3. 优化数据类型
默认情况下,Pandas 可能会使用比必需更多的内存。一个整数列可能被存储为 64 位,而 8 位可能就足够了。
例如,如果你正在加载一个包含产品评分(1-5 星)和用户 ID 的数据集:
import pandas as pd # 首先,让我们看看默认的内存使用情况 df = pd.read_csv('ratings.csv') print("Default memory usage:") print(df.memory_usage(deep=True)) # 现在优化数据类型 df['rating'] = df['rating'].astype('int8') # 评分范围是 1-5,所以 int8 足够了 df['user_id'] = df['user_id'].astype('int32') # 假设用户 ID 适合 int32 print("\nOptimized memory usage:") print(df.memory_usage(deep=True))
将评分列从可能的 int64(每个数字 8 字节)转换为 int8(每个数字 1 字节),我们为该列实现了 8 倍的内存减少。
常见转换包括:
int64→int8,int16, 或int32(取决于数字的范围)。float64→float32(如果你不需要极高的精度)。object→category(用于包含重复值的列)。
# 4. 使用分类数据类型
当某一列包含重复的文本值(如国家名称或产品类别)时,Pandas 会分别存储每个值。category 数据类型会只存储一次唯一值,并使用高效的代码来引用它们。
假设你正在处理一个产品库存文件,其中“类别”列只有 20 个唯一值,但在整个数据集中重复出现:
import pandas as pd df = pd.read_csv('products.csv') # 转换前检查内存 print(f"Before: {df['category'].memory_usage(deep=True) / 1024**2:.2f} MB") # 转换为 category 类型 df['category'] = df['category'].astype('category') # 转换后检查内存 print(f"After: {df['category'].memory_usage(deep=True) / 1024**2:.2f} MB") # 它仍然像正常的文本数据一样工作 print(df['category'].value_counts())
这种转换可以显著减少低基数(唯一值少)列的内存使用量。该列的功能仍然与标准文本数据类似:你可以像平常一样进行筛选、分组和排序。
何时使用: 对于任何值频繁重复的文本列(类别、州、国家/地区等)。
# 5. 读取时筛选
有时你知道你只需要数据中的一部分行。与其加载所有内容后再进行筛选,不如在加载过程中进行筛选。
例如,如果你只关心 2024 年的交易记录:
import pandas as pd # 分块读取并筛选 chunk_size = 100000 filtered_chunks = [] for chunk in pd.read_csv('transactions.csv', chunksize=chunk_size): # 在存储前筛选每个块 filtered = chunk[chunk['year'] == 2024] filtered_chunks.append(filtered) # 合并筛选后的块 df_2024 = pd.concat(filtered_chunks, ignore_index=True) print(f"Loaded {len(df_2024)} rows from 2024")
我们将分块处理与筛选结合起来。在将每个块添加到列表中之前对其进行筛选,因此我们永远不会将整个数据集保留在内存中,只保留我们实际需要的行。
何时使用: 当你基于某些条件只需要一部分行时。
# 6. 使用 Dask 进行并行处理
对于真正庞大的数据集,Dask 提供了一个类似 Pandas 的 API,但它会自动处理所有分块和并行处理。
下面是如何在一个巨大的数据集中计算某一列的平均值:
import dask.dataframe as dd # 使用 Dask 读取(它会自动处理分块) df = dd.read_csv('huge_dataset.csv') # 操作看起来和 pandas 一样 result = df['sales'].mean() # Dask 是惰性的 - compute() 才会实际执行计算 average_sales = result.compute() print(f"Average Sales: ${average_sales:,.2f}")
Dask 不会将整个文件加载到内存中。相反,它会创建一个处理数据的计划,并在调用 .compute() 时执行该计划。它甚至可以使用多个 CPU 核心来加速计算。
何时使用: 当你的数据集大到 Pandas 即使分块也无法处理,或者当你希望在不编写复杂代码的情况下实现并行处理时。
# 7. 对数据进行采样以进行探索
当你在探索或测试代码时,你不需要完整的数据集。首先加载一个样本。
假设你正在构建一个机器学习模型并希望测试预处理流程。你可以按如下方式对数据集进行采样:
import pandas as pd # 只读取前 50,000 行 df_sample = pd.read_csv('huge_dataset.csv', nrows=50000) # 或者使用 skiprows 读取随机样本 import random skip_rows = lambda x: x > 0 and random.random() > 0.01 # 保留约 1% 的行 df_random_sample = pd.read_csv('huge_dataset.csv', skiprows=skip_rows) print(f"Sample size: {len(df_random_sample)} rows")
第一种方法加载前 N 行,适用于快速探索。第二种方法随机抽取文件中的行,更适合统计分析,或者当文件排序方式使得前几行不具有代表性时。
何时使用: 在开发、测试或探索性分析阶段,运行代码前。
# 结论
处理大型数据集并不需要专家级别的技能。以下是我们讨论过的技术的快速总结:
| 技术 | 何时使用 |
|---|---|
| 分块 | 用于聚合、筛选以及处理无法放入 RAM 的数据。 |
| 列选择 | 当只需要宽数据集中的几列时。 |
| 数据类型优化 | 加载后始终执行;以节省内存。 |
| 分类类型 | 用于重复值多的文本列(类别、州等)。 |
| 读取时筛选 | 当你只需要一部分行时。 |
| Dask | 用于非常大的数据集,或当你需要并行处理时。 |
| 采样 | 在开发和探索阶段。 |
第一步是同时了解你的数据和你的任务。在大多数情况下,结合使用分块处理和智能的列选择就能解决 90% 的问题。
随着你的需求增长,可以转向更先进的工具,如 Dask,或者考虑将数据转换为更高效的文件格式,如 Parquet 或 HDF5。
现在就开始着手处理那些海量数据集吧。祝你分析愉快!
Bala Priya C 是来自印度的开发人员和技术作家。她喜欢在数学、编程、数据科学和内容创作的交叉点工作。她的兴趣和专业领域包括 DevOps、数据科学和自然语言处理。她喜欢阅读、写作、编码和咖啡!目前,她正致力于通过撰写教程、操作指南、观点文章等内容来学习并与开发者社区分享她的知识。Bala 还负责创建引人入胜的资源概述和编码教程。
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区