从 Pandas 到 Polars 十:“Polars 表达式“是什么?

Series和表达式

最近我收到一个Polars新用户的问题:在Polars中,Series和表达式之间有什么区别?

嗯,Series是一个一维数据结构,其中所有元素都具有相同的数据类型(例如,整数、字符串等),Series可以被视为数据的列,类似于pandas 的column,它代表了数据表中的一列。

而表达式是一个函数,它作用于Series以产生一个新的Series。即表达式是从列(Series)到列的映射(或从数学角度上说是Fn(Series) -> Series),并且可以在各种上下文中使用。

所以我们使用表达式来转换Series。

在Polars中有很多表达式的例子:

  1. 最简单的例子是恒等表达式,它仅返回给定的 Series,即 pl.col("id")。恒等表达式(identity expression)在这里指的是没有改变或修改原始数据的表达式,它只是简单地返回了引用的列。
  2. 我们可以使用表达式如pl.col("id").str.to_uppercase()来转换Series的内容
  3. 我们可以使用表达式如pl.col("value") + 1在Series上进行算术运算
  4. 我们可以使用表达式如pl.col("value").sum()来聚合Series
  5. 我们可以使用表达式如pl.col("value").alias("double_value")来改变输出Series的名称
  6. 我们可以使用表达式如pl.col("value").sum().over("id")在分组上应用表达式

什么是表达式 API?

下一个问题:Polars文档经常提到的表达式API是什么?

表达式API是Polars中接受表达式作为参数的方法以及表达式本身的总称。

1. 接受表达式作为参数的方法

接受表达式作为参数的DataFrame方法示例有:

df.filter(pl.col("id") == 1)
df.select(pl.col("id").str.to_uppercase())
df.with_columns(double_value=pl.col("value") * 2)
df.groupby(pl.col("id")).agg(pl.col("value").sum())

2. 表达式本身

表达式API 的另一个组成部分是表达式本身。这些是我们用来转换Series和聚合Series的函数。

这些函数在Polars API文档中有列出。这些函数被分成几类,如:

  1. 聚合(Aggregation)用于聚合(显然)
  2. 计算(Computation)用于计算(显然)
  3. 列/名称(Columns / names)用于处理列名
  4. 窗口(Window)用于在分组上应用表达式

还有一些仅适用于特定数据类型的表达式类别,如:

  1. 字符串(Strings)用于字符串操作和匹配
  2. 时间(Temporal)用于处理日期和时间
  3. 数组或列表(Array or List)用于处理数组和列表

3. 上下文

当我们使用这些带表达式的方法时,有一个重要的概念需要理解:上下文。上下文告诉我们哪些实际数据将被用作表达式的输入。

例如,当我们执行df.filter或df.select时,我们处于选择(select)上下文。这个上下文意味着DataFrame的整列是表达式的输入。

当我们执行df.groupby时,我们处于分组(groupby)上下文。这个上下文意味着表达式的输入是具有分组列相同值的行组。

另外,在Polars中,"上下文"的概念不是直接指一个特定的编程结构或对象,应该从数据处理的视角来理解它。在Polars中,当你对一个DataFrame进行操作时,你实际上是在一个特定的数据处理环境中工作,这个环境可以理解为一种"上下文"。

以下是如何理解Polars中"上下文"的几个关键点:

  1. 数据范围:当你对一个DataFrame调用一个方法或应用一个表达式时,你正在对该DataFrame的当前状态进行操作。这个DataFrame及其内容就是你当前操作的上下文。
  2. 链式操作:Polars支持链式方法调用,这意味着你可以连续地在同一个DataFrame上应用多个操作。每一个后续的操作都是基于前一个操作的结果,从而构建了一个操作链或上下文流。

例如:

filtered_df = df.filter(pl.col('age') > 30).select(['name', 'age'])

在这个例子中,df.filter(pl.col('age') > 30)首先过滤出'age'大于30的行,然后.select(['name', 'age'])在过滤后的结果上选择'name''age'列。这两个操作都是在同一个DataFrame的上下文中进行的。

    3. 状态不变性:虽然Polars支持链式操作,但它也遵循状态不变性原则。这意味着当你对一个DataFrame应用一个操作时,原始DataFrame不会被修改;相反,会返回一个新的DataFrame,其中包含操作的结果。这种设计有助于避免在数据处理过程中引入意外的副作用或错误。
    4. 表达式和计算:在Polars中,表达式通常用于描述你希望对数据进行的操作或转换。这些表达式在DataFrame的上下文中被评估和执行。Polars的优化引擎会尝试以最高效的方式执行这些表达式,无论是通过矢量化计算还是其他技术。

总的来说,在Polars中,"上下文"可以理解为你在其上执行操作的数据集(如DataFrame)及其当前状态。你的操作(如过滤、选择、转换等)都是在这个上下文中进行的,并且通常会返回一个新的数据集作为结果。

表达式API的好处是什么?

表达式API允许您发挥Polars的全部功能。

首先,当我们在同一上下文中应用多个表达式时,Polars可以并行运行它们。

比如:

import polars as pl

# 创建一个示例DataFrame
df = pl.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David'],
    'age': [25, 35, 20, 40],
    'city': ['New York', 'Paris', 'London', 'New York']
})

# 在同一上下文中应用多个表达式
# 首先,筛选出age大于30的行
# 然后,选择name和city列
filtered_and_selected_df = (
    df
    .filter(pl.col('age') > 30)  # 第一个表达式:筛选
    .select(['name', 'city'])    # 第二个表达式:选择
)

# 显示结果
print(filtered_and_selected_df)

其次,表达式API允许您以惰性模式工作。一个表达式实际上是对Polars查询引擎的指令,指示您想做什么。在惰性模式下,您可以构建复杂的数据处理管道,然后Polars可以在执行之前应用查询优化。

最后,表达式API允许您处理大于内存的数据集。Polars可以通过分块处理数据来处理太大而无法装入内存的数据集。这被称为分块处理,是表达式API的一个关键特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值