Polars python api 中的不变性和写时复制原则

问题描述 投票:0回答:1

您好,我正在研究这个同人小说项目,该项目是 pypolars 到 R 的全功能 + 语法翻译,称为“minipolars”。

我了解 pypolars API,例如一般的 DataFrame 引发不可变行为或与“写时复制”行为相同。大多数改变 DataFrame 对象的方法都会返回一个廉价的副本。 我知道的例外是 DataFrame.extend@columns.setter。 在 R 中,大多数 API 都致力于实现严格的不可变行为。我想既支持严格不可变的行为,又支持可选的类似 pypol 的行为。 Rust-polars API 有许多可变操作+生命周期等等,但可以理解的是,它完全与性能和表达能力有关。

  • pypolars-API 中还有更多核心可变行为吗?
  • 仅具有不可变行为的 pypolars-API 会影响性能和表达能力吗?

R 库

data.table
API 有时确实会偏离不可变行为。然而,所有此类可变操作都带有前缀
set_
或使用集合运算符
:=

  • pypols 中是否有一种明显的方法来识别操作是否可变?

通过可变行为,我想到了例如在定义变量

.extend()
后执行方法
df_mutable_copy
并且仍然影响值
df_mutable_copy

import polars as pl

df1 = pl.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})
df2 = pl.DataFrame({"foo": [10, 20, 30], "bar": [40, 50, 60]})

df_copy = df1
df_copy_old_shape = df_copy.shape

df1.extend(df2)
df_copy_new_shape = df_copy.shape

#extend was a operation with mutable behaviour df_copy was affected.
df_copy_old_shape != df_copy_new_shape 

python-polars
1个回答
5
投票

大多数Python Polars API实际上是Polars惰性的包装器。

(df.with_columns(..)
   .join(..)
   .group_by()
   .select(..)

翻译为:

(df.lazy().with_columns(..).collect(no_optimization=True)
   .lazy().join(..).collect(no_optimization=True)
   .lazy().group_by().collect(no_optimization=True)
   .lazy().select(..).collect(no_optimization=True)

这意味着几乎所有表达式都在 Polars 查询引擎上运行。查询引擎本身决定是否可以就地完成操作,或者是否应该克隆数据(写入时复制)。

Polars 实际上具有“写入时复制”功能,因为它仅在数据不共享时进行复制。如果我们是唯一的所有者,我们就会就地变异。我们可以这样做是因为 Rust 有一个借用检查器,所以如果我们拥有数据并且引用计数为 1,我们就可以改变数据。

我建议您实现 R-polars API,类似于我们在 python 中所做的事情。然后所有操作都可以变得纯粹(例如返回一个新的

Series/Expr/DataFrame
)并且pollars将决定何时就地变异。

不用担心复制数据。所有数据缓冲区都包含在

Arc
中,因此我们只增加引用计数。

© www.soinside.com 2019 - 2024. All rights reserved.