PDEP-10: PyArrow 作为默认字符串推断实现的必需依赖项
- 创建日期: 2023 年 4 月 17 日
- 状态: 已接受
- 讨论: #52711 #52509
- 作者: Matthew Roeschke Patrick Hoefler
- 修订版: 1
摘要
本 PDEP 提出
- 从 pandas 3.0 开始,PyArrow 将成为必需的运行时依赖项
- 从 pandas 3.0 开始,支持的 PyArrow 最低版本为 PyArrow 7 版。
- 当 PyArrow 的最低版本提升时,PyArrow 将提升至至少发布两年以上的最高版本。
- pandas 2.1 版本说明将包含一个重要警告,说明 PyArrow 将从 pandas 3.0 开始成为必需的依赖项。我们将在 pandas 问题跟踪器上固定一个反馈问题。版本说明中的说明将指向该问题。
- 从 pandas 2.2 开始,当 pandas 导入时,如果用户环境中未安装 PyArrow,pandas 将引发
FutureWarning
。这将确保只引发一个警告,并且用户可以轻松地将其静默,如果需要的话。此警告将指向反馈问题。 - 从 pandas 3.0 开始,字符串数据的默认推断类型将为
ArrowDtype
,使用pyarrow.string
而不是object
。此外,我们将推断下面列出的所有数据类型,而不是将其存储为对象。
这将为用户带来 **立竿见影的益处**,并为未来带来更多重大的益处。
背景
PyArrow 是 pandas 的一个可选依赖项,它为 pandas 提供了广泛的补充功能
- 从 pandas 0.21.0 开始,PyArrow 提供了用于 Parquet 的 I/O 读取功能
- 从 pandas 1.2.0 开始,pandas 将 PyArrow 集成到
ExtensionArray
接口中,以提供一个可选的由 PyArrow 支持的字符串数据类型。 - 从 pandas 1.4.0 开始,PyArrow 提供了用于 CSV 的 I/O 读取功能。
- 从 pandas 1.5.0 开始,pandas 提供了
ArrowExtensionArray
和ArrowDtype
来支持ExtensionArray
接口中的所有 PyArrow 数据类型。 - 从 pandas 2.0.0 开始,所有 I/O 读取器都有选择返回 PyArrow 支持的数据类型的选项,并且许多方法现在使用 PyArrow 计算函数来加速 pandas 中的 PyArrow 支持的数据,特别是字符串和日期时间类型。
截至 pandas 2.0,人们可以将 PyArrow 作为 NumPy 的替代数据表示形式,它具有以下优点:
- 所有数据类型一致的
NA
支持; - 更广泛的数据类型支持,例如
decimal
、date
和嵌套类型; - 与其他基于 Arrow 的数据框库更好的互操作性。
动机
虽然上一段中描述的所有功能目前都是可选的,但 PyArrow 已被广泛集成到 pandas 的许多领域。我们的路线图指出 pandas 努力实现更好的 Apache Arrow 互操作性 [^1],并且许多项目 [^2],无论是在 Python 生态系统内还是之外,都采用或与 Arrow 格式交互,使 PyArrow 成为必需的依赖项,这为 Arrow 生态系统提供了额外的信心信号(以及提高与它的互操作性)。
直接用户收益 1:pyarrow 字符串
目前,当用户将字符串数据传递到 pandas 构造函数中而没有指定数据类型时,结果数据类型为 object
,与 pyarrow 字符串相比,它的内存使用量和性能要差得多。由于 pyarrow 字符串支持自 1.2.0 版本起可用,因此在 3.0 中要求 pyarrow 将允许 pandas 将推断的类型默认设置为更有效的 pyarrow 字符串类型。
In [1]: import pandas as pd
In [2]: pd.Series(["a"]).dtype
# Current behavior
Out[2]: dtype('O')
# Future behavior in 3.0
Out[2]: string[pyarrow]
Dask 开发人员调查了 pyarrow 字符串的性能和内存 这里,发现它们比当前的 object
dtype 有显著改进。
小演示
import string
import random
import pandas as pd
def random_string() -> str:
return "".join(random.choices(string.printable, k=random.randint(10, 100)))
ser_object = pd.Series([random_string() for _ in range(1_000_000)])
ser_string = ser_object.astype("string[pyarrow]")\
PyArrow 支持的字符串比 NumPy 对象字符串快得多
str.len
In[1]: %timeit ser_object.str.len()
118 ms ± 260 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In[2]: %timeit ser_string.str.len()
24.2 ms ± 187 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
str.startswith
In[3]: %timeit ser_object.str.startswith("a")
136 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In[4]: %timeit ser_string.str.startswith("a")
11 ms ± 19.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
直接用户收益 2:嵌套数据类型
目前,如果您尝试将dict
存储在 pandas Series
中,您将再次获得可怕的object
数据类型。
In [6]: pd.Series([{'a': 1, 'b': 2}, {'a': 2, 'b': 99}])
Out[6]:
0 {'a': 1, 'b': 2}
1 {'a': 2, 'b': 99}
dtype: object
如果需要pyarrow
,这可以自动推断为pyarrow.struct
,这将再次带来内存和性能改进。
直接用户收益 3:互操作性
其他基于 Arrow 的数据帧库越来越受欢迎。拥有相同的内存表示将提高与它们的互操作性,因为诸如
import pandas as pd
import polars as pl
df = pd.DataFrame(
{
'a': ['one', 'two'],
'b': [{'name': 'Billy', 'age': 3}, {'name': 'Bob', 'age': 4}],
}
)
pl.from_pandas(df)
之类的操作可以是零拷贝的。使用多个数据帧库的用户将能够更容易地在它们之间切换。
未来用户收益
需要 PyArrow 将简化 pandas 中的相关开发,并可能改进 NumPy 功能,这些功能更适合 PyArrow,包括
-
避免在构造函数或索引操作期间执行 PyArrow 对象推断时 PyArrow 是否可用的运行时检查
-
将尽可能避免 NumPy 对象数据类型。这意味着每个具有 PyArrow 等效项的数据类型都会自动推断为这种类型。这包括
- 十进制
- 二进制
- 嵌套类型(列表或字典数据)
- 字符串
- 时间
- 日期
开发者收益
首先,这将简化基于 pyarrow 的数据类型的开发,因为它将避免可选的依赖项检查。
其次,它可能消除冗余功能:- read_parquet
中的 fastparquet 引擎;- 可能简化 read_csv
逻辑(需要更多调查);- 因式分解;- 日期时间/时区操作。
缺点
包含 PyArrow 自然会增加 pandas 的安装大小。例如,使用 pip 从 wheels 安装 pandas 和 PyArrow,numpy 和 pandas 需要大约 70MB
,而包含 PyArrow 需要额外的 120MB
。安装大小的增加将对在空间受限的开发或部署环境(如 AWS Lambda)中使用 pandas 产生负面影响。
此外,如果用户在没有通过 pip install
或 conda install
提供 wheels 的环境中安装 pandas,则用户在从源代码安装时还需要构建 Arrow C++ 和相关依赖项。这些环境包括
- Alpine linux(通常用作 Docker 容器的基础)
- WASM(pyodide 和 pyscript)
- Python 开发版本
最后,pandas 的开发和发布需要考虑 PyArrow 的开发和发布节奏。例如,在支持新发布的 Python 版本时,pandas 还需要在发布新版本的 pandas 之前考虑 PyArrow 对该 Python 版本的 wheel 支持。
常见问题解答
问:为什么 Pandas 不能直接使用 NumPy 字符串和 NumPy void 数据类型,而要使用 PyArrow 字符串和 PyArrow 结构体?
答:NumPy 字符串目前还不可用,而 PyArrow 字符串已经可用。NumPy void 数据类型与 PyArrow 结构体不同,无法带来与其他基于 Arrow 的数据框库相同的互操作性优势。
问:所有 PyArrow 数据类型都准备好了吗?现在将它们设置为默认值是不是太早了?
答:它们很可能在 3.0 版本中准备好 - 但是,我们还没有将它们设置为默认值。例如,pd.Series([1, 2, 3])
将继续自动推断为 np.int64
。我们只会更改当前没有 numpy
支持的等效类型且存储为 object
数据类型的默认值,例如字符串和嵌套数据类型。
PDEP-10 历史
- 2023 年 4 月 17 日:初始版本
- 2023 年 5 月 8 日:更改提案,将 PyArrow 在 Pandas 3.0 中设置为必需,而不是 2.1
[^1] https://pandas.ac.cn/docs/development/roadmap.html#apache-arrow-interoperability [^2] https://arrow.apache.ac.cn/powered_by/