Courses
如果您和我一样经常用 Python 做数据分析,很快就会发现读取 CSV 文件是最常见的任务之一。然而,随着数据集规模增长,这种方式可能会变得缓慢或占用大量内存。
Polars 是一个快速、现代的 Python DataFrame 库,被设计为 Pandas 的高性能替代方案。它专注于速度和低内存占用,因此比传统工具更从容地处理大型数据集。
Polars 的核心函数 pl.read_csv() 提供了将 CSV 文件加载为 DataFrame 的简洁方案,并内置解析、数据类型与内存控制等选项。
在本指南中,我将演示如何使用 Polars 的 pl.read_csv() 读取 CSV 文件、控制解析行为并处理大型数据集。如果您刚开始使用,欢迎查看我们的Introduction to Polars课程,学习如何用 Polars 处理数据并提取洞见。
pl.read_csv() 的基本用法
在继续之前,先查看我们的 Python Polars 教程,了解如何配置环境。
现在,让我们看看 pl.read_csv() 的工作方式:
# import the module
import polars as pl
# Load CSV into a Polars DataFrame
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv")
# Preview first few rows
print(fuel_data.head())
在上述示例中,Polars 读取文件并将其加载到 DataFrame 中。该函数返回一个 Polars DataFrame,这是一种表格数据结构,类似于您在 pandas 中得到的对象。
默认情况下,pl.read_csv():
-
假定第一行包含列名(
has_header=True) -
自动推断列的数据类型
-
使用逗号(
,)作为分隔符 -
将整个文件读入内存
pl.read_csv() 的常用参数
了解 pl.read_csv() 如何加载数据之后,我们来看如何使用以下参数来定制 Polars 的解析过程。
文件路径与数据源
Polars 允许从不同数据源加载数据。您可以传入本地的字符串路径、Pathlib 对象,甚至是 URL。比如,下面的代码从网络读取一个包含各国历年 GDP 的大型 CSV 文件。
# Reading from a URL
url = "https://raw.githubusercontent.com/datasets/gdp/master/data/gdp.csv"
gdp_data = pl.read_csv(url)
分隔符与定界符
并非所有 CSV 文件都使用逗号。您可以使用 separator 参数处理制表符、分号、竖线或其他字符。下面的示例展示了读取文件时如何指定分隔符。
# Reading a Semicolon-separated file
sales_data = pl.read_csv("2024_sales.csv", separator=";")
# Reading a Tab-separated file (TSV)
orders_data = pl.read_csv("all_orders.tsv", separator="\t")
表头处理
如前所述,Polars 假定数据的第一行包含列名。如果您的文件没有表头行,请使用参数 has_header=False,如下所示。Polars 会自动分配列名,如 column_1、column_2、column_3 等。
# Load file without a header
orders_data = pl.read_csv("all_orders.csv", has_header=False)
您也可以通过提供特定列名来重命名列。例如:
# Providing specific column names
sales_data = pl.read_csv("sales_April.csv", new_columns=[
"OrderDate", "OrderNumber", "ProductKey", "SalespersonKey", "Salesperson"])
编码
不同的 CSV 文件可能使用不同的文本编码。如果遇到异常字符或报错,请指定编码:
# Load CSV into a Polars DataFrame
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv", encoding="utf8")
其他常见编码选项包括 ”latin1” 和 ”utf8-lossy”,它们会适当处理无效字符。
在 Polars 中读取 CSV 时如何选择列
处理大型 CSV 文件时,您往往并不需要所有列。Polars 允许使用 columns 参数仅加载所需列。
# Load only specific columns
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv",
columns=["YEAR", "MAKE", "MODEL"]
)
在加载文件时只选择需要的列,可以避免加载不必要的数据,从而减少内存占用。这种做法也能加快读取速度并提升整体管道性能。
在 Polars CSV 中处理数据类型(模式)
Polars 是强类型库,这意味着每一列都必须有一致的数据类型,例如全为整数或全为字符串。
自动类型推断
默认情况下,Polars 会检查数据并自动确定列类型。该方法在大多数情况下表现良好,但有时可能会误判列类型,比如把 ID 当作整数而非字符串。
手动指定模式
当您希望 DataFrame 具有一致性时,应手动指定数据模式。这可确保在多个文件之间保持一致,并避免后续处理中的类型相关错误。
在大多数场景下,使用 schema_overrides 为特定列指定数据类型,同时允许 Polars 推断其余列。
import polars as pl
# Manually overriding specific data types
empl_data = pl.read_csv(
"all_employees.csv",
schema_overrides={
"id": pl.Int64,
"name": pl.String,
"age": pl.Int64,
"salary": pl.Float64
}
)
或者使用 schema 定义 DataFrame 的完整结构。
# Manually define the full schema
empl_data = pl.read_csv(
"all_employees.csv",
schema={
"id": pl.Int64,
"name": pl.String,
"age": pl.Int64,
"salary": pl.Float64
}
)
在 Polars CSV 中处理缺失值
默认情况下,Polars 会自动检测这些缺失值,并将其表示为 null。有时缺失值由特定字符串表示,如 ”NA”、”N/A” 或 ”missing”。您可以通过 null_values 定义这些标记,使其被视为 null。
# Treating "N/A" and "EMPTY" as nulls
survey_data = pl.read_csv(
"survey_results.csv",
null_values=["N/A", "EMPTY", "null"]
)
正确处理缺失值有助于确保数据类型一致、避免错误计算,从而降低分析与建模过程中的错误率。
在 Polars 中读取大型 CSV 文件
当处理超过可用内存的大型数据集时,Polars 表现尤为出色,因为它可以在不一次性加载整个文件的情况下处理数据。
使用 scan_csv() 的惰性加载
与 read_csv() 立即将数据加载到内存不同,Polars 提供了惰性方案 scan_csv(),它会构建优化后的查询计划,并仅在需要时执行操作。
在下面的示例中,pl.scan_csv 在不加载文件的情况下扫描 CSV,构建仅获取“YEAR”“MAKE”“MODEL”列且品牌为“ACURA”的查询,然后执行该查询。这样只会将过滤后的数据片段加载到内存,而不是整个文件。
# Lazily reference the file (no data loaded yet)
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv")
fuel_consumption_filtered = fuel_consumption.select(
["YEAR", "MAKE", "MODEL"]).filter(pl.col("MAKE") == "ACURA")
# Execute the query and load only the required data
fuel_consumption_acura = fuel_consumption_filtered.collect()
我建议在以下情况下使用 scan_csv():
- 处理无法舒适放入内存的大文件
- 需要应用多步转换,例如筛选、选择和聚合数据
- 希望在执行前进行查询优化
流式处理与内存效率
Polars 还允许以分块的方式进行流式处理,使峰值内存与文件大小无关而保持稳定。
对于大型 CSV 文件,先用 scan_csv() 创建惰性查询。调用 scan_csv() 时文件并不会被完全加载。
# Read data in chunks
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv",
low_memory=True)
由于上述方法仍使用 read_csv,最佳做法是将惰性执行与流式处理结合使用。
在下面的示例中,Polars 以流水线方式按批逐行处理文件,任意时刻只在内存中保留过滤后的行(“CYLINDERS” > 3)。
# Lazily reference the file (no data loaded yet)
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv")
result = fuel_consumption.filter(pl.col("CYLINDERS") > 3)
# Stream execution instead of loading everything at once
fuel_consumption_high = result.collect(engine="streaming")
与 Pandas 的性能对比
与 pandas 相比,Polars 在 CSV 读取与处理方面通常表现更佳,下一节将看到对比。
Polars read_csv vs Pandas read_csv
下表总结了在 Python 中读取 CSV 时 Polars 与 Pandas 的差异。
|
方面 |
Polars |
Pandas |
|
速度 |
因多线程与优化解析而更快 |
在大文件上较慢(多数为单线程) |
|
内存占用 |
占用更低;支持惰性 + 流式处理 |
积极加载,将整个数据集读入内存 |
|
语法 |
|
|
|
执行模型 |
支持惰性执行( |
仅支持即时(积极)执行 |
|
优化 |
内置查询优化(列裁剪、筛选) |
自动优化能力有限 |
|
最佳用例 |
大型数据集、对性能敏感的流程 |
较小数据集、快速分析 |
我建议阅读我们关于 Pandas 与 Polars 的对比,以确定哪种工具更适合您的分析需求。
从不同来源读取 CSV
正如前文所示,您可以使用 Polars 从不同来源读取 CSV 文件。上述示例中我已展示如何从本地文件与 URL 读取 CSV。
此外,Polars 可自动处理压缩的 CSV 文件。例如,以下代码从 .gzip 文件读取数据。
# Read gzip-compressed CSV
census_data = pl.read_csv("2018_census.csv.gz")
常见错误与排查
即便使用像 Polars 这样的高性能引擎,读取 CSV 文件时仍可能遇到一些问题。以下是我遇到的常见问题及其解决方法。
-
编码错误: 您可能会看到类似
UnicodeDecodeError的错误,意味着文件不是 UTF-8 编码。这在较老的文件或某些版本 Excel 导出的 CSV 中较常见。要解决此问题,请设置合适的编码,或在字符不一致或有损坏时使用“utf8-lossy”。 -
分隔符问题: 如果 DataFrame 成功加载但所有数据都挤在单列中,说明 Polars 未识别到分隔符。为解决此问题,请通过
separator参数显式设置正确的分隔符,如分号(;)、制表符(\t)或竖线(|)。 -
数据类型不正确: 有时 Polars 可能错误推断列类型,例如把数值型 ID 读成字符串,或在解析时出错。此时可使用
schema_overrides为特定列指定类型;若希望为每一列都定义类型,请使用schema。您也可以增大infer_schema_length,让 Polars 在推断模式前检查更多行。 -
大文件内存问题: 如果在加载大文件时 Python 进程崩溃,说明内存不足。要解决此问题,请从
read_csv()切换到scan_csv()进行惰性加载,以降低内存占用并提升性能。您也可以只加载数据集的部分列,或使用流式执行按块加载数据。
结语
Polars 的 read_csv() 使用简单,却足以胜任实际任务。在我看来,面对不断增长的数据集时,Polars 具有明显优势。
接下来,欢迎阅读我们关于 Polars GPU 引擎 的博文,了解其多种应用。如果您准备上手实战,我们的 Super Bowl Analytics with Polars 代码跟练将帮助您解决真实分析问题、进行计算,并将基础的机器学习技术应用于体育分析问题。
FAQs
在 Polars 中,read_csv() 与 scan_csv() 有何区别?
read_csv() 会将数据积极加载到内存,而 scan_csv() 采用惰性执行,仅在您调用 .collect() 时处理数据。
我应何时使用 scan_csv() 而不是 read_csv()?
在处理大型数据集或需要串联多步转换时使用 scan_csv(),因为它能优化执行并降低内存占用。
Polars 能只从 CSV 文件读取特定列吗?
可以,使用 read_csv() 的 columns=[...] 参数,或在惰性查询中用 scan_csv() 选择列。
如何在 Polars CSV 文件中处理缺失值?
Polars 默认将空值视为 null,您也可以通过 null_values= 定义自定义的空值标记。
Polars 是否支持压缩的 CSV 文件?
可以,Polars 支持读取 .gz 和 .zip 等压缩格式,无需手动解压。