从 Pandas 到 Polars 十二:在延迟模式下填充时间序列的缺失值

Polars相对于Pandas的两个主要优势是,Polars具有带有查询优化的延迟模式,并且Polars可以通过其流式处理模式扩展到大于内存的数据集。利用这些优势有时需要重新考虑如何在Pandas中编写相同的操作。

一个不完整的时间序列

在这个简单的例子中,有一个时间序列,其中缺少了一些时间点。想要在这些缺失的时间点添加行,并对这些间隔进行插值。

我们定义了一个时间序列,它在2020-01-01 02:00:00时缺少一个条目。

import polars as pl

df = pl.DataFrame(
    {
        "time": [
            datetime(2020, 1, 1),
            datetime(2020, 1, 1, 1),
            datetime(2020, 1, 1, 3),
        ],
        "values": [0, 1, 3],
    })

这个DataFrame看起来像这样:

shape: (3, 2)

┌─────────────────────┬────────┐
│ time                ┆ values │
│ ---                 ┆ ---    │
│ datetime[μs]        ┆ i64    │
╞═════════════════════╪════════╡
│ 2020-01-01 00:00:00 ┆ 0      │
│ 2020-01-01 01:00:00 ┆ 1      │
│ 2020-01-01 03:00:00 ┆ 3      │
└─────────────────────┴────────┘

在立即执行模式下使用上采样来填充缺失值

我们可以在立即执行模式下使用上采样来填充缺失值,就像Pandas一样。

df.set_sorted('time').upsample('time',every='1h')

使用upsample后的输出看起来像这样:

shape: (4, 2)
┌─────────────────────┬────────┐
│ time                ┆ values │
│ ---                 ┆ ---    │
│ datetime[μs]        ┆ i64    │
╞═════════════════════╪════════╡
│ 2020-01-01 00:00:00 ┆ 0      │
│ 2020-01-01 01:00:00 ┆ 1      │
│ 2020-01-01 02:00:00 ┆ null   │
│ 2020-01-01 03:00:00 ┆ 3      │
└─────────────────────┴────────┘

Polars和Pandas在这里的主要区别在于,Polars在调用upsample之前需要列是已排序的。这是因为实现需要排序的数据,而Polars希望避免如果我们可以告诉它已经排序了的情况下进行昂贵的排序操作。

这种方法的问题是upsample是一个立即执行的操作。这意味着在执行操作之前,我们必须将整个DataFrame加载到内存中。对于小型的DataFrame来说这没问题,但对于大于内存容量的数据集来说就无法扩展了。

在延迟模式下填充缺失值

在延迟模式下填充缺失值,我们首先使用pl.date_range函数定义一个没有缺失值的时间序列DataFrame。

pl.DataFrame(
    {
        "time": pl.date_range(
            start=datetime(2020, 1, 1),
            end=datetime(2020, 1, 1, 3),
            interval="1h",
            eager=True,
        )
    })

然后,我们使用time列作为连接键,将这个DataFrame与原始DataFrame进行左连接。关键点在于,在连接它们之前,我们分别对每个DataFrame调用了lazy方法。这告诉Polars以延迟模式执行连接操作。

pl.DataFrame(
    {
        "time": pl.date_range(
            start=datetime(2020, 1, 1),
            end=datetime(2020, 1, 1, 3),
            interval="1h",
            eager=True,
        )
    }).lazy().join(df.lazy(), on="time", how="left")

如果我们使用collect来评估这段代码,我们将得到以下输出:

shape: (4, 2)
┌─────────────────────┬────────┐
│ time                ┆ values │
│ ---                 ┆ ---    │
│ datetime[μs]        ┆ i64    │
╞═════════════════════╪════════╡
│ 2020-01-01 00:00:00 ┆ 0      │
│ 2020-01-01 01:00:00 ┆ 1      │
│ 2020-01-01 02:00:00 ┆ null   │
│ 2020-01-01 03:00:00 ┆ 3      │
└─────────────────────┴────────┘

对缺失值进行插值处理

现在我们可以使用interpolate表达式对缺失值进行插值处理。

pl.DataFrame(
    {
        "time": pl.date_range(
            start=datetime(2020, 1, 1),
            end=datetime(2020, 1, 1, 3),
            interval="1h",
            eager=True,
        )
    }).lazy().join(df.lazy(), on="time", how="left").with_columns(
    pl.col("values").interpolate())

如果我们使用collect来评估这段代码,我们将得到以下输出:

shape: (4, 2)
┌─────────────────────┬────────┐
│ time                ┆ values │
│ ---                 ┆ ---    │
│ datetime[μs]        ┆ i64    │
╞═════════════════════╪════════╡
│ 2020-01-01 00:00:00 ┆ 0      │
│ 2020-01-01 01:00:00 ┆ 1      │
│ 2020-01-01 02:00:00 ┆ 2      │
│ 2020-01-01 03:00:00 ┆ 3      │
└─────────────────────┴────────┘

流式处理模式

正如我在之前的帖子中所提到的,如果我们调用explain(streaming=True)并且存在一个由PIPELINE限定的代码块,我们可以检查一个惰性查询是否会使用流式引擎。

pl.DataFrame(
    {
        "time": pl.date_range(
            start=datetime(2020, 1, 1),
            end=datetime(2020, 1, 1, 3),
            interval="1h",
            eager=True,
        )
    }).lazy().join(df.lazy(), on="time", how="left").with_columns(
    pl.col("values").interpolate()).explain(streaming=True)

在这种情况下,我们得到以下输出:

 WITH_COLUMNS:
 [col("values").interpolate()]
  --- PIPELINE
LEFT JOIN:
LEFT PLAN ON: [col("time")]
  DF ["time"]; PROJECT */1 COLUMNS; SELECTION: "None"
RIGHT PLAN ON: [col("time")]
  DF ["time", "values"]; PROJECT */2 COLUMNS; SELECTION: "None"
END LEFT JOIN  --- END PIPELINE

    DF []; PROJECT */0 COLUMNS; SELECTION: "None"

在这里,我们看到我们可以在流式模式下进行连接操作,但无法在流式模式下进行插值。插值操作对于流式模式来说是一个具有挑战性的操作,因为它可能需要来自不同批次的数据来执行插值。

将这种方法推广应用于多个时间序列

这里的例子相对简单但可以推广。例如,在我构建的机器学习时间序列预测管道中,我通常会在DataFrame中有多个时间序列,并使用一个id列来区分它们。在这种情况下,我会在与有缺失数据的时间序列进行左连接之前,对时间步长和ID进行额外的交叉连接。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
社会的进步导致人们对于学习的追求永不止境,那么追求农业信息化的方式也从单一的田地教程变成了多样化的学习方式。多样化的学习方式不仅仅是需要人们智慧的依靠,还需要能够通过软件的加持进行信息化的价值体现。软件和系统的产生,从表面上来看是方便了某一行业和某一行业的用户,其实是从本质上来说是提高了社会的进步。就拿我们常见的出行而言,滴滴出行看似是为了打车和出行的人方便,但其实通过另外一种程度上来说,可以通过软件应用的调度和发展来为社会、城市出行的发展做出巨大的贡献。我们国家从最早的中国制造业演变到现在的“智造”,就是因为有软件信息系统的价值,能够将一些智慧的因素加入到制造的过程当中,而这一点就是软件系统来改变生产和现实的需求。在计算机时代日益发展的今天,计算机网络正快速融入这个社会的每一个领域。农业的发展是社会当中一种必有可少的方式。果树在种植和培养是直接影响果农及果商的发展,但在果树的资源管理方面还是有着很大的不同,所以信息多样化的果树管理方式很重要。在传统的果树资源管理上还有着很大的约束,为此开发和设计JSP杏种质资源管理系统,该系统内容丰富多彩,用户可以在线进行果杏树的资源查询等。本文还是使用JSP的方式来进行管理的,但在系统建设过程当中也考虑了许许多多信息安全的保护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值