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 将被提升到已发布至少 2 年的最高版本。
- pandas 2.1 版本说明中会有一个醒目的警告,说明从 pandas 3.0 开始,PyArrow 将成为必需的依赖项。我们将在 pandas 问题追踪器上置顶一个反馈问题。版本说明中的提示将指向该问题。
- 从 pandas 2.2 开始,当用户环境中未安装 PyArrow 且导入 pandas 时,pandas 会引发一个
FutureWarning
。这将确保只引发一个警告,并且用户在需要时可以轻松地忽略它。此警告将指向该反馈问题。 - 从 pandas 3.0 开始,推断出的字符串数据的默认类型将是带有
pyarrow.string
的ArrowDtype
,而不是object
。此外,我们将推断出下面列出的所有 dtypes,而不是存储为 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],并且 Python 生态系统内外的许多项目 [^2] 正在采用 Arrow 格式或与之交互,因此将 PyArrow 设为必需的依赖项进一步表明了对 Arrow 生态系统的信心(并提高了与之的互操作性)。
即时用户好处 1:pyarrow 字符串
当前,当用户在不指定数据类型的情况下将字符串数据传递给 pandas 构造函数时,结果数据类型是 object
,与 pyarrow 字符串相比,其内存使用和性能要差得多。自从 1.2.0 起支持 pyarrow 字符串后,要求 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 object 字符串快得多。
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:嵌套数据类型
当前,如果您尝试在 pandas Series
中存储 dict
,您将再次得到可怕的 object
dtype。
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 object dtype。这意味着每个具有 PyArrow 等效类型的 dtype 都将自动推断为此类型。这包括:
- decimal
- binary
- 嵌套类型(list 或 dict 数据)
- 字符串
- time
- date
开发者好处
首先,这将简化对 pyarrow 支持的数据类型的开发,因为它将避免可选依赖项检查。
其次,它可能会移除冗余功能: - read_parquet
中的 fastparquet 引擎; - 可能简化 read_csv
逻辑(需要进一步调查); - factorization; - 日期时间/时区操作。
缺点
包含 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 容器的基础镜像)
- Python 开发版本
最后,pandas 的开发和发布需要注意 PyArrow 的开发和发布节奏。例如,在支持新发布的 Python 版本时,pandas 也需要注意 PyArrow 对该 Python 版本的 wheel 支持,然后再发布新的 pandas 版本。
常见问题
问:为什么 pandas 不能直接使用 numpy string 和 numpy void 数据类型,而非要使用 pyarrow string 和 pyarrow struct?
答:NumPy strings 尚未可用,而 pyarrow strings 已经可用。NumPy void 数据类型与 pyarrow struct 不同,无法带来与基于 Arrow 的其他数据框库相同的互操作性优势。
问:所有的 pyarrow dtypes 都准备好了吗?现在就将它们设为默认类型是否为时过早?
答:它们到 3.0 版本时很可能已经准备就绪 - 然而,我们(目前)不会将它们全部设为默认类型。例如,pd.Series([1, 2, 3])
将继续自动推断为 np.int64
。我们只会改变那些当前没有 numpy
支持的等效类型且被存储为 object
dtype 的默认设置,例如字符串和嵌套数据类型。
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/