【重磅】Pandas 2.0发布!更快的速度更低的内存占用!
Pandas 是一个用于操作数据的 Python 库,在 Python 开发人员中非常流行。尤其在数据科学和机器学习领域中,Pandas已经成为不可或缺的基础库。
4月3日,Pandas 2.0正式发布。2.0是Pandas的一个里程碑式的版本。回顾Pandas的历史,从诞生时到 1.0 花了超过十年的时间,1.0的发布标志着Pandas DataFrame API 趋于稳定;时隔3年(Pandas 1.0于2020年正式发布)后,Pandas 2.0更多是在性能和可用性上进行了提升。具体改进包括:
- 使用 pip extras 安装可选的依赖项
- 更快的计算速度和更低的内存占用
- 索引可以保存 NumPy 数字类型
- 写时复制优化
Pandas 2.0的改进
使用 pip extras 安装可选的依赖项
pip extras
是指在使用 Python 包管理器 pip
安装 Python 包时可以选择安装的附加功能或依赖项。这些可选依赖项通常用于提供默认情况下不需要但对某些用户可能有用的额外功能。包维护者在包的 setup.py
或 pyproject.toml
文件中定义了这些“附加功能”。
要安装具有附加功能的软件包,可以使用以下命令:
pip install package_name[extra1,extra2,...]
例如,安装Pandas 2.0时我们可以指定其附加功能:
pip install "pandas[performance, aws]>=2.0.0"
pip extras
的引入解决了很多过去安装Pandas的痛点:
- 可选性:extras 提供了一种选择性安装依赖项的方式,这意味着用户可以根据自己的需求来决定是否需要安装附加功能。这使得安装过程更加灵活。
- 减小包的大小:通过将一些附加功能的依赖项设置为可选的,可以减小 Python 包的大小。这有助于避免不必要的依赖项安装,从而节省存储空间和带宽。
- 提高兼容性:有些额外功能可能需要特定的操作系统或库版本。将这些功能作为 extras 提供,可以确保在没有这些特定依赖项的环境中,包的核心功能仍然可以正常工作。
- 简化依赖管理:将某些功能的依赖项设置为 extras,可以简化依赖管理,使得用户在安装和更新包时更容易处理和维护依赖项。
Pandas 2.0提供了 [all, performance, computation, fss, aws, gcp, excel, parquet, feather, hdf5, spss, postgresql, mysql, sql-other, html, xml, plot, output_formatting, clipboard, compression, test]
扩展,具体内容请参考安装指南。
更快的计算速度和更低的内存占用
Pandas最大的改进是引入PyArrow作为可选后端,从而获得了更快的计算速度和更低的内存占用。
PyArrow是 Apache Arrow C++接口的Python绑定。Apache Arrow 是一种开源的、跨语言的内存数据处理平台。Apache Arrow 旨在通过提供高效的数据结构和 API 来提高数据处理任务的性能,以便在大数据和高性能计算(HPC)环境中使用。得益于Apache Arrow的诸多优势,采用PyArrow作为后端的Pandas 2.0性能得到显著提升。下面是一组常见操作对比:
操作 | NumPy用时 | Arrow用时 | 速度提升 |
---|---|---|---|
read parquet (50Mb) | 141 ms | 87 ms | 1.6x |
mean (int64) | 2.03 ms | 1.11 ms | 1.8x |
mean (float64) | 3.56 ms | 1.73 ms | 2.1x |
endswith (string) | 471 ms | 14.9 ms | 31.6x |
Pandas 2.0做了很好的向下兼容,2.0的发布没有破坏1.0的API。默认情况下,pandas 将继续使用Numpy作为后端,数据类型也是原始类型。例如:
import pandas
pandas.Series([1, 2, 3, 4])
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64
pandas.Series(['foo', 'bar', 'foobar'])
# 0 foo
# 1 bar
# 2 foobar
# dtype: object
上面的代码和输出跟Pandas 1.0一摸一样。我们可以通过dtype
参数将后端切换为PyArrow。
pandas.Series([1, 2, 3, 4], dtype='int64[pyarrow]')
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64[pyarrow]
pandas.Series(['foo', 'bar', 'foobar'], dtype='string[pyarrow]')
# 0 foo
# 1 bar
# 2 foobar
# dtype: string
在读取数据时,我们可以通过dtype_backend
选项告诉Pandas使用哪种后端。当该选项设为numpy_nullable
时,将返回nullable dtypes
类型的DataFrame;如果设为pyarrow
,则会返回ArrowDtype
类型的DataFrame。
索引可以保存 NumPy 数字类型
索引可以保存 NumPy 数字类型让Pandas 2.0能够更好的索引、更快的访问和计算。
Pandas 1.0 的 Index 仅支持int64
、uint64
和 float64
3种类型;Pandas 2.0 允许 Index 保存任何 NumPy 数字数据类型,包括 int8
、int16
、int32
、int64
、uint8
、uint16
、uint32
、uint64
、float32
和 float64
。
因此,以前创建 64 位索引的操作现在可以创建具有较低位大小的索引,例如 32 位索引。
pd.Index([1, 2, 3], dtype=np.int8)
# Index([1, 2, 3], dtype='int8')
pd.Index([1, 2, 3], dtype=np.uint16)
# Index([1, 2, 3], dtype='uint16')
pd.Index([1, 2, 3], dtype=np.float32)
# Index([1.0, 2.0, 3.0], dtype='float32')
写时复制优化
写时复制优化有点类似Spark的做法,它是一种内存优化技术,用于提高性能并减少处理大型数据集时的内存使用量。当你复制 pandas 对象(如 DataFrame 或 Series)时,pandas 不会立即创建数据的新副本,而是创建对原始数据的引用并推迟创建新副本,直到你以某种方式修改数据。
这意味着如果你有相同数据的多个副本,它们都可以引用相同的内存,直到你对其中一个进行更改。 这样可以显着减少内存使用并提高性能,因为不需要创建不必要的数据副本。
总的来说,写时复制是一种强大的优化技术,可以帮助程序更高效地处理大型数据集,并且占用更少的内存。
为什么要用Arrow
在使用Pandas 2.0时,我建议大家都选择 PyArrow 作为后端。因为 PyArrow 提供了几个关键特性,包括:
- 高效的内存列式数据表示:Arrow 使用列式内存布局,与基于行的布局相比,这种布局在分析处理方面更为高效。这种布局实现了更快的数据访问,并提高了缓存利用率,从而提高了整体性能。
- 零拷贝数据共享:Arrow 允许在进程和语言之间进行零拷贝数据共享,这最小化了数据序列化和反序列化开销。这一特性在涉及多种语言和系统的大数据处理管道中尤为有用。
- 与语言无关的数据结构:Arrow 定义了一种可以在不同编程语言之间使用的标准化数据结构,使得在 Python、Java、R 和 C++ 等语言之间共享数据变得更容易。
- 与其他库和系统的互操作性:
pyarrow
与 Pandas 和 NumPy 等流行的 Python 数据处理库以及 Apache Parquet、Apache Spark 和 Apache Hadoop 等数据处理框架实现了无缝集成。 - GPU 支持:Arrow 通过像 NVIDIA 的 RAPIDS 这样的库支持基于 GPU 的计算,使用户能够为数据处理任务利用 GPU 的计算能力。
除了上述底层特性带来带来的优势,PyArrow 还会带来一下直接优点:
规范缺失值
Pandas用标签方法表示缺失值:
- 数值类型:NaN
- 对象类型:None
其中,None是一个Python对象,所以不能作为任何NumPy/Pandas数组类型的缺失值,只能用于'object'数组类型(即由python对象构成的数组)。
Pandas 处理缺失值的方法是将数字转换为浮点数(如果还没有),并使用 NaN
作为缺失值。 比如:
s1 = pandas.Series([1, 2, 3, 4])
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64
s1 = pandas.Series([1, 2, 3, None])
# 0 1.0
# 1 2.0
# 2 3.0
# 3 NaN
# dtype: float64
上面的代码可以看到,当存在缺失值时,Pandas将所有数值都转换为浮点数。将整数值转换为浮点表示法以支持缺失值同样不理想,并且有副作用。其中之一是Pandas中任意两个缺失值不相等(np.nan != np.nan)。
此外,在将数组添加到 Pandas 之后,Pandas 可以为缺失值添加自己的数据类型。 这些是通过使用两个数组而不是一个数组来实现的。 主数组以与没有缺失值相同的方式表示数据。 但是还有一个额外的布尔数组,它指示主数组中的哪些值存在并且需要考虑,哪些不存在且必须忽略。
而Apache Arrow 内存中缺失值是统一的规范标准。 通过使用 Arrow,Pandas 能够更方便地处理缺失值,而无需为每种数据类型实现自己的缺失值版本。
高性能
Apache Arrow 提供了一个内存中的列数据结构,它具有几个主要优点:
- 随机访问时间复杂度为$O(1)$
- 内置原生向量优化
- 系统间数据转换快速高效
随机访问时间复杂度为 $O(1)$,是因为数据被格式化为面向列的数据结构。
因为Arrow的执行引擎利用了现代处理器中包含的 SIMD 操作。 这意味着,任何处理 Arrow 数据结构的算法都将非常快。
系统之间的数据交换非常快速和高效,是因为 Arrow 避免了开销巨大的数据序列化。
这些特性让Pandas 2.0采用Arrow比采用NumPy性能提升巨大。
互操作性
就像 CSV 是独立于项目的文件格式一样,Arrow 也是一种独立于程序的格式。这点可能不是那么直观,因为Arrow是一种内存格式,而不是文件格式。但即便它没有关联的文件扩展名,你也可以将其视为可以由实现了其开放规范的不同消费者访问的数据。这让不同程序之间共享数据(相对)简单且标准。 而且它可以以极快且内存高效的方式完成,因为两个程序可以真正共享相同的数据(相同的内存,而不必为每个程序制作副本)。
总结
总的来说,得益于 PyArrow的引入,Pandas 2.0 实现了更快、更节省内存的操作。
有关新 Pandas 2.0 的更多信息,请阅读官方文档。