Polars简明基础教程六:什么是Polars的“DataFrame(数据框)_下”

 Apache Arrow

经典的Pandas DataFrame将数据存储在Numpy数组中。而在Polars中,数据被存储在Arrow表中。

我们可以通过调用to_arrow来查看这个Arrow表——这是一个廉价的操作,因为它只是查看底层数据。

df.to_arrow()

pyarrow.Table
survived: int64
pclass: int64
sex: large_string
age: double
sibsp: int64
parch: int64
fare: double
embarked: large_string
class: large_string
who: large_string
adult_male: bool
deck: large_string
embark_town: large_string
alive: large_string
alone: bool
----
survived: [[0,1,1,1,0,...,0,1,0,1,0]]
pclass: [[3,1,3,1,3,...,2,1,3,1,3]]
sex: [["male","female","female","female","male",...,"male","female","female","male","male"]]
age: [[22,38,26,35,35,...,27,19,null,26,32]]
sibsp: [[1,1,0,1,0,...,0,0,1,0,0]]
parch: [[0,0,0,0,0,...,0,0,2,0,0]]
fare: [[7.25,71.2833,7.925,53.1,8.05,...,13,30,23.45,30,7.75]]
embarked: [["S","C","S","S","S",...,"S","S","S","C","Q"]]
class: [["Third","First","Third","First","Third",...,"Second","First","Third","First","Third"]]
who: [["man","woman","woman","woman","man",...,"man","woman","woman","man","man"]]
...

Arrow表是Arrow数组的集合——这些是一维向量,是基本的数据存储。我们可以通过在Series上调用to_arrow来查看某列的Arrow数组。

df["age"].to_arrow()


[
  22,
  38,
  26,
  35,
  35,
  null,
  54,
  2,
  27,
  14,
  ...
  33,
  22,
  28,
  25,
  39,
  27,
  19,
  null,
  26,
  32
]

Apache Arrow是什么?

Apache Arrow是一个开源的跨语言项目,用于在内存中存储表格数据。

Apache Arrow 是一个跨平台的开发库,用于在内存中高效处理大型数据集。它最初由 Databricks 开发,并于 2016 年捐赠给 Apache 软件基金会。Arrow 的主要目标是在不同的系统之间提供高性能的数据交换和处理。

以下是 Apache Arrow 的一些关键特点:

  1. 列式存储:Arrow 使用列式存储格式,这意味着每一列数据都连续存储在内存中。这种格式非常适合批量数据处理,因为它能够利用现代 CPU 的缓存行为来提高数据访问效率。

  2. 零拷贝读取:Arrow 支持零拷贝读取,这意味着数据可以在不复制的情况下被多个进程共享。这有助于减少数据传输过程中的开销。

  3. 类型安全:Arrow 数据结构是类型安全的,这意味着数据类型在内存中是明确指定的。这对于避免运行时错误和提高性能非常重要。

  4. 跨语言兼容性:Arrow 支持多种编程语言,包括 C++、Java、Python、JavaScript、Rust 和更多。这意味着使用不同语言编写的程序可以共享相同的数据格式,而不需要额外的转换步骤。

  5. 高性能矢量化操作:Arrow 提供了用于矢量化操作的库函数,这些函数可以在整个数据集上并行执行,从而显著提高数据处理的速度。

  6. 支持多种数据源:Arrow 可以与多种数据源集成,包括关系数据库、NoSQL 存储系统、文件系统等。

  7. Parquet 文件格式集成:Arrow 与 Parquet 文件格式紧密集成,使得 Arrow 可以高效地读写 Parquet 文件。

  8. 用于大数据处理的生态系统集成:Arrow 被多个大数据处理框架和工具所采用,例如 Apache Spark、Pandas、Dask、Vaex、Polars 和 Flink。

Apache Arrow是:

  •  一个关于数据如何在内存中表示的规范
  • 一组在不同语言中实现该规范的库 

Polars使用了Rust库[Arrow2]中的Arrow规范实现。

 为什么Polars使用Apache Arrow?

当人们意识到专为科学计算设计的Numpy数组并不是表格数据的最佳数据存储方式时,Apache Arrow项目应运而生。

Apache Arrow 在数据科学和大数据领域变得越来越流行,因为它提高了数据处理的效率,并简化了不同系统之间的数据交换。

Arrow允许:

  • 无需复制即可共享数据(称为“零拷贝”)
  • 更快的向量化计算
  • 分块处理大于内存的数据
  • 缺失数据的一致表示 

总体而言,由于Arrow,Polars可以更快且更少地使用内存来处理数据。

 Apache Arrow的缺点是什么?

Arrow的设计优化了对一维列的操作,而Numpy的设计优化了对多维数组的操作。这种权衡意味着与Numpy相比,使用Arrow数据进行某些类型的操作会更慢:

  •  转换数据帧
  • 在数据帧上进行矩阵乘法/线性代数运算 

对于这种需要按行和列访问数据的用例,转换为Numpy数组可能更快(请参阅关于转换的讲座)。

 那么Polars DataFrame和Arrow数据之间的关系是什么?

Polars DataFrame持有对Arrow表的引用,该表又持有对Arrow数组的引用。我们可以将Polars DataFrame视为一个轻量级对象,它指向轻量级的Arrow表,该表又指向重量级的Arrow数组(重量级是因为它们持有实际数据)。

这种分离的结构意味着我们可以对便宜的DataFrame包装器进行更改,而不复制(或复制最小量)任何数据,因为所有实际数据都存储在Arrow数组中。这种设计允许Polars提供快速且内存高效的数据处理功能。

df_shape = (1_000_000,100)
df_polars = pl.DataFrame(
    np.random.standard_normal(df_shape)
)

df_polars.shape

删除列

我们将查看从Polars DataFrame中删除一个列需要多长时间。我们使用IPython的timeit模块来比较性能(我们将在课程后面的部分中学习更多关于timeit的知识)。

%%timeit -n1 -r3

df_polars.drop("column_0")

Polars执行此操作非常快(比传统的Pandas快得多)。这是因为Polars只是创建了一个新的DataFrame对象(这是一个廉价的操作),该对象指向除了column_0之外的所有Arrow数组。基本上,Polars只是遍历列名列表来执行此操作!

重命名列

当我们更改DataFrame的某些不影响列中实际数据的部分时,会产生类似的效果。例如,如果我们重命名一个列...

%%timeit -n1 -r3

df_polars.rename({"column_0":"a"})

Polars再次以非常快的速度完成此操作,因为它只是更新列名并检查列名是否仍然唯一。

克隆DataFrame

或者,如果我们通过克隆来创建一个新的DataFrame...

%%timeit -n1 -r3

df_polars.clone()

在这种情况下,Polars创建了一个新的DataFrame对象,它指向相同的Arrow表。

更新克隆的DataFrame

尽管新的和旧的DataFrame最初指向相同的Arrow表,但我们不需要担心对其中一个所做的更改会影响另一个。

如果我们对其中一个DataFrame(比如新的DataFrame)中的值进行修改,那么新的DataFrame将:

  •  将已更改列中的数据复制到新的Arrow数组中
  • 创建一个新的Arrow表,该表指向更新后的Arrow数组以及未更改的Arrow数组 

因此,现在我们有了:

  •  两个DataFrame,它们指向:
  • 两个Arrow表,它们指向:
  • 对于未更改的列,是相同的Arrow数组;对于已更改的列,是不同的Arrow数组 

通过这种方式,我们创建了一个新的DataFrame,但只需要复制已更改列中的数据。在这个示例中,我们更改了第一行中的第一个值,我们可以看到对新的DataFrame的更改不会影响旧的DataFrame。

df_polars2 = df_polars.clone()
df_polars2[0,0] = 1000
df_polars2[0,0]

在原始的DataFrame中,我们仍然保留着原始的值。

df_polars[0,0]

练习

在练习中,您将加深以下内容的理解:

  1. - 获取DataFrame的数据类型(dtypes)
  2. - 获取Series的数据类型(dtypes)

 练习 1

这个DataFrame的数据类型(dtypes)是什么?

df = pl.DataFrame({'a':[0,1,2],'b':[0,1,2.0]})

df<blank>

练习 2

通过选择df的a列来创建一个Series

df = pl.DataFrame({'a':[0,1,2],'b':[0,1,2.0]})

 df<blank>

a的数据类型是什么?

b的数据类型是什么?

解决方案

 练习 1 的解决方案

这个DataFrame的数据类型(dtypes)是什么?

df = pl.DataFrame({'a':[0,1,2],'b':[0,1,2.0]})
df.schema

练习 2 的解决方案

通过选择df的a列来创建一个Series

df = pl.DataFrame({'a':[0,1,2],'b':[0,1,2.0]})
s = df["a"]
s

s”有一个 64 位整数数据类型(dtype)

s2 = df["b"]

s2

s2 有一个 64 位浮点数据类型(dtype)

Polars简明基础教程系列

Polars简明基础教程一:Polars快速入门

Polars简明基础教程二:懒惰模式 1:引入懒惰模式

Polars简明基础教程三:懒惰模式 1:引入懒惰模式(续)

Polars简明基础教程四:懒惰模式 2:评估查询

Polars简明基础教程五:什么是Polars的“DataFrame(数据框)_上”

Polars简明基础教程六:什么是Polars的“DataFrame(数据框)_下”

Polars简明基础教程七:Series 和 DataFrame 以及它们之间的转换_A

Polars简明基础教程八:Series 和 DataFrame 以及它们之间的转换_B

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值