🌟

polarsを使った仮想通貨の時系列データの前処理と可視化まとめ(執筆途中)

2023/02/19に公開

polarsのimport

import polars as pl
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import timedelta
%matplotlib inline
plt.style.use('ggplot')

csvデータの読み込み

bitflyerの2023/1/18~2023/1/22日分の約定履歴を読み込みexec_dateをdatetime型に変換してprintで出力
この方法ではscan_csvで遅延評価しても変わらなかった。dtypeでdatetimeに変換する方法では効果があったが下記方法の方が速度が速かった。

path = "bitflyer/FX_BTC_JPY/trades/2023-01-22.csv"
date_target = "exec_date"

df = (
      pl.read_csv(path)
      .with_columns(pl.col(date_target)
                    .str
                    .strptime(pl.Datetime, strict=False))
      )
print(df[:3])

┌────────┬────────────┬──────┬────────────┬──────────┬───────────────┬──────────────┬──────────────┐
│        ┆ id         ┆ side ┆ price      ┆ size     ┆ exec_date     ┆ buy_child_or ┆ sell_child_o │
│ ---    ┆ ---        ┆ ---  ┆ ---        ┆ ---      ┆ ---           ┆ der_acceptan ┆ rder_accepta │
│ i64    ┆ i64        ┆ str  ┆ f64        ┆ f64      ┆ str           ┆ ce_id        ┆ nce_id       │
│        ┆            ┆      ┆            ┆          ┆               ┆ ---          ┆ ---          │
│        ┆            ┆      ┆            ┆          ┆               ┆ str          ┆ str          │
╞════════╪════════════╪══════╪════════════╪══════════╪═══════════════╪══════════════╪══════════════╡
│ 888999 ┆ 2427730923 ┆ SELL ┆ 2.65905e6  ┆ 0.02     ┆ 2023-01-18    ┆ JRF20230118- ┆ JRF20230118- │
│        ┆            ┆      ┆            ┆          ┆ 23:58:53.543  ┆ 235852-05515 ┆ 235853-13018 │
│        ┆            ┆      ┆            ┆          ┆               ┆ 5            ┆ 0            │
│ 888998 ┆ 2427730924 ┆ SELL ┆ 2.65905e6  ┆ 0.020716 ┆ 2023-01-18    ┆ JRF20230118- ┆ JRF20230118- │
│        ┆            ┆      ┆            ┆          ┆ 23:58:53.543  ┆ 235852-05515 ┆ 235853-11943 │
│        ┆            ┆      ┆            ┆          ┆               ┆ 5            ┆ 1            │
│ 888997 ┆ 2427730925 ┆ SELL ┆ 2.658996e6 ┆ 0.000004 ┆ 2023-01-18    ┆ JRF20230118- ┆ JRF20230118- │
│        ┆            ┆      ┆            ┆          ┆ 23:58:53.543  ┆ 235820-13432 ┆ 235853-11943 │
│        ┆            ┆      ┆            ┆          ┆               ┆ 5            ┆ 1            │
└────────┴────────────┴──────┴────────────┴──────────┴───────────────┴──────────────┴──────────────┘
961 ms ± 43.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

年月日、曜日を新たなデータとして追加

dt.day()で日付、alias()でcolumn追加できます。aliasがない場合はexec_dateに上書きされる。

df.with_columns(pl.col(date_target).dt.day().alias("day"))

┌────────┬────────────┬──────┬────────────┬─────┬──────────────┬──────────────┬──────────────┬─────┐
│        ┆ id         ┆ side ┆ price      ┆ ... ┆ exec_date    ┆ buy_child_or ┆ sell_child_o ┆ day │
│ ---    ┆ ---        ┆ ---  ┆ ---        ┆     ┆ ---          ┆ der_acceptan ┆ rder_accepta ┆ --- │
│ i64    ┆ i64        ┆ str  ┆ f64        ┆     ┆ datetime[μs] ┆ ce_id        ┆ nce_id       ┆ u32 │
│        ┆            ┆      ┆            ┆     ┆              ┆ ---          ┆ ---          ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ str          ┆ str          ┆     │
╞════════╪════════════╪══════╪════════════╪═════╪══════════════╪══════════════╪══════════════╪═════╡
│ 888999 ┆ 2427730923 ┆ SELL ┆ 2.65905e6  ┆ ... ┆ 2023-01-18   ┆ JRF20230118- ┆ JRF20230118- ┆ 18  │
│        ┆            ┆      ┆            ┆     ┆ 23:58:53.543 ┆ 235852-05515 ┆ 235853-13018 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 5            ┆ 0            ┆     │
│ 888998 ┆ 2427730924 ┆ SELL ┆ 2.65905e6  ┆ ... ┆ 2023-01-18   ┆ JRF20230118- ┆ JRF20230118- ┆ 18  │
│        ┆            ┆      ┆            ┆     ┆ 23:58:53.543 ┆ 235852-05515 ┆ 235853-11943 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 5            ┆ 1            ┆     │
│ 888997 ┆ 2427730925 ┆ SELL ┆ 2.658996e6 ┆ ... ┆ 2023-01-18   ┆ JRF20230118- ┆ JRF20230118- ┆ 18  │
│        ┆            ┆      ┆            ┆     ┆ 23:58:53.543 ┆ 235820-13432 ┆ 235853-11943 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 5            ┆ 1            ┆     │
│ 888996 ┆ 2427730926 ┆ SELL ┆ 2.658996e6 ┆ ... ┆ 2023-01-18   ┆ JRF20230118- ┆ JRF20230118- ┆ 18  │
│        ┆            ┆      ┆            ┆     ┆ 23:58:53.543 ┆ 235820-13432 ┆ 235853-13453 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 5            ┆ 3            ┆     │
│ ...    ┆ ...        ┆ ...  ┆ ...        ┆ ... ┆ ...          ┆ ...          ┆ ...          ┆ ... │
│ 3      ┆ 2429121037 ┆ SELL ┆ 2.946067e6 ┆ ... ┆ 2023-01-22   ┆ JRF20230122- ┆ JRF20230122- ┆ 22  │
│        ┆            ┆      ┆            ┆     ┆ 23:59:50.707 ┆ 235950-01629 ┆ 235950-04815 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 6            ┆ 0            ┆     │
│ 2      ┆ 2429121038 ┆ SELL ┆ 2.945864e6 ┆ ... ┆ 2023-01-22   ┆ JRF20230122- ┆ JRF20230122- ┆ 22  │
...
│ 0      ┆ 2429121040 ┆ SELL ┆ 2.945656e6 ┆ ... ┆ 2023-01-22   ┆ JRF20230122- ┆ JRF20230122- ┆ 22  │
│        ┆            ┆      ┆            ┆     ┆ 23:59:52.150 ┆ 235948-01112 ┆ 235952-01113 ┆     │
│        ┆            ┆      ┆            ┆     ┆              ┆ 2            ┆ 9            ┆     │
└────────┴────────────┴──────┴────────────┴─────┴──────────────┴──────────────┴──────────────┴─────┘

複数同時に追加する場合はwith_columns()内にリストで表記する。
カラム数が増えすぎたのでselect()で新しいデータ表を作って見やすくした。
因みに
with_columns()で既存のデータに保存
select()で新しいデータ表を作成して保存

となる

df_dt = df.select(
    [
        pl.col(date_target),
        pl.col(date_target).dt.year().alias("year"),
        pl.col(date_target).dt.month().alias("month"),
        pl.col(date_target).dt.day().alias("day"),
        pl.col(date_target).dt.hour().alias("hour"),
        pl.col(date_target).dt.minute().alias("minute"),
        pl.col(date_target).dt.second().alias("second"),
        pl.col(date_target).dt.epoch().alias("epoch"),
    ]
)
))

┌─────────────────────────┬──────┬───────┬─────┬──────┬────────┬────────┬──────────────────┐
│ exec_date               ┆ year ┆ month ┆ day ┆ hour ┆ minute ┆ second ┆ epoch            │
│ ---                     ┆ ---  ┆ ---   ┆ --- ┆ ---  ┆ ---    ┆ ---    ┆ ---              │
│ datetime[μs]            ┆ i32  ┆ u32   ┆ u32 ┆ u32  ┆ u32    ┆ u32    ┆ i64              │
╞═════════════════════════╪══════╪═══════╪═════╪══════╪════════╪════════╪══════════════════╡
│ 2023-01-21 23:58:21.487 ┆ 2023 ┆ 1     ┆ 21  ┆ 23   ┆ 58     ┆ 21     ┆ 1674345501487000 │
│ 2023-01-21 23:58:21.487 ┆ 2023 ┆ 1     ┆ 21  ┆ 23   ┆ 58     ┆ 21     ┆ 1674345501487000 │
│ 2023-01-21 23:58:22.183 ┆ 2023 ┆ 1     ┆ 21  ┆ 23   ┆ 58     ┆ 22     ┆ 1674345502183000 │
│ 2023-01-21 23:58:22.183 ┆ 2023 ┆ 1     ┆ 21  ┆ 23   ┆ 58     ┆ 22     ┆ 1674345502183000 │
│ ...                     ┆ ...  ┆ ...   ┆ ... ┆ ...  ┆ ...    ┆ ...    ┆ ...              │
│ 2023-01-22 23:59:50.707 ┆ 2023 ┆ 1     ┆ 22  ┆ 23   ┆ 59     ┆ 50     ┆ 1674431990707000 │
│ 2023-01-22 23:59:50.707 ┆ 2023 ┆ 1     ┆ 22  ┆ 23   ┆ 59     ┆ 50     ┆ 1674431990707000 │
│ 2023-01-22 23:59:52.150 ┆ 2023 ┆ 1     ┆ 22  ┆ 23   ┆ 59     ┆ 52     ┆ 1674431992150000 │
│ 2023-01-22 23:59:52.150 ┆ 2023 ┆ 1     ┆ 22  ┆ 23   ┆ 59     ┆ 52     ┆ 1674431992150000 │
└─────────────────────────┴──────┴───────┴─────┴──────┴────────┴────────┴──────────────────┘

特定の年月日や曜日だけをとりだす

filterを使ってpl.col(カラム名)で取り出せます。
例: second が 5 の行を取り出す

df_dt.filter(pl.col("second") == 5).head()

┌─────────────────────────┬──────┬───────┬─────┬──────┬────────┬────────┬──────────────────┐
│ exec_date               ┆ year ┆ month ┆ day ┆ hour ┆ minute ┆ second ┆ epoch            │
│ ---                     ┆ ---  ┆ ---   ┆ --- ┆ ---  ┆ ---    ┆ ---    ┆ ---              │
│ datetime[μs]            ┆ i32  ┆ u32   ┆ u32 ┆ u32  ┆ u32    ┆ u32    ┆ i64              │
╞═════════════════════════╪══════╪═══════╪═════╪══════╪════════╪════════╪══════════════════╡
│ 2023-01-22 00:00:05.490 ┆ 2023 ┆ 1     ┆ 22  ┆ 0    ┆ 0      ┆ 5      ┆ 1674345605490000 │
│ 2023-01-22 00:01:05.117 ┆ 2023 ┆ 1     ┆ 22  ┆ 0    ┆ 1      ┆ 5      ┆ 1674345665117000 │
│ 2023-01-22 00:01:05.383 ┆ 2023 ┆ 1     ┆ 22  ┆ 0    ┆ 1      ┆ 5      ┆ 1674345665383000 │
│ 2023-01-22 00:01:05.383 ┆ 2023 ┆ 1     ┆ 22  ┆ 0    ┆ 1      ┆ 5      ┆ 1674345665383000 │
│ 2023-01-22 00:02:05.233 ┆ 2023 ┆ 1     ┆ 22  ┆ 0    ┆ 2      ┆ 5      ┆ 1674345725233000 │
└─────────────────────────┴──────┴───────┴─────┴──────┴────────┴────────┴──────────────────┘

時間ごとの動きを確認

棒グラフ表記 y軸に取引回数

plt.figure(figsize=(15,7))
plt.hist(df_dt['hour'], bins=len(df_dt['hour'].value_counts()), width=0.5)
plt.xlabel('Hour')
plt.ylabel('FX_BTC_JPY')

時間の増減

pandas同様datetime型から比較増減もできます。
例: exec_date-30日

df_dt.with_columns(
    (pl.col("exec_date") - timedelta(days=30)).alias("date_30")
)
┌──────────────┬──────┬───────┬─────┬─────┬────────┬────────┬──────────────────┬──────────────┐
│ exec_date    ┆ year ┆ month ┆ day ┆ ... ┆ minute ┆ second ┆ epoch            ┆ date_30      │
│ ---          ┆ ---  ┆ ---   ┆ --- ┆     ┆ ---    ┆ ---    ┆ ---              ┆ ---          │
│ datetime[μs] ┆ i32  ┆ u32   ┆ u32 ┆     ┆ u32    ┆ u32    ┆ i64              ┆ datetime[μs] │
╞══════════════╪══════╪═══════╪═════╪═════╪════════╪════════╪══════════════════╪══════════════╡
│ 2023-01-21   ┆ 2023 ┆ 1     ┆ 21  ┆ ... ┆ 58     ┆ 21     ┆ 1674345501487000 ┆ 2022-12-22   │
│ 23:58:21.487 ┆      ┆       ┆     ┆     ┆        ┆        ┆                  ┆ 23:58:21.487 │
│ 2023-01-21   ┆ 2023 ┆ 1     ┆ 21  ┆ ... ┆ 58     ┆ 21     ┆ 1674345501487000 ┆ 2022-12-22   │
│ 23:58:21.487 ┆      ┆       ┆     ┆     ┆        ┆        ┆                  ┆ 23:58:21.487 │
│ 2023-01-21   ┆ 2023 ┆ 1     ┆ 21  ┆ ... ┆ 58     ┆ 22     ┆ 1674345502183000 ┆ 2022-12-22   │
│ 23:58:22.183 ┆      ┆       ┆     ┆     ┆        ┆        ┆                  ┆ 23:58:22.183 │
│ 2023-01-21   ┆ 2023 ┆ 1     ┆ 21  ┆ ... ┆ 58     ┆ 22     ┆ 1674345502183000 ┆ 2022-12-22   │
│ 23:58:22.183 ┆      ┆       ┆     ┆     ┆        ┆        ┆                  ┆ 23:58:22.183 │
│ 2023-01-21   ┆ 2023 ┆ 1     ┆ 21  ┆ ... ┆ 58     ┆ 22     ┆ 1674345502183000 ┆ 2022-12-22   │
│ 23:58:22.183 ┆      ┆       ┆     ┆     ┆        ┆        ┆                  ┆ 23:58:22.183 │
└──────────────┴──────┴───────┴─────┴─────┴────────┴────────┴──────────────────┴──────────────┘

リサンプリング

約定履歴からohlcv作成

df_ohlcv = (
    df
        .with_columns(pl.when(pl.col('side') == 'BUY').then(pl.col('size')).otherwise(0).alias('buy_size'))
        .with_columns(pl.when(pl.col('side') == 'SELL').then(pl.col('size')).otherwise(0).alias('sell_size'))
        .groupby_dynamic('exec_date', every='1m')
        .agg([
            pl.col('price').first().alias('open'),
            pl.col('price').max().alias('high'),
            pl.col('price').min().alias('low'),
            pl.col('price').last().alias('close'),
            pl.col('size').sum().alias('volume'),
            pl.col('buy_size').sum().alias('buy_vol'),
            pl.col('sell_size').sum().alias('sell_vol'),
        ])
)
shape: (5, 8)
┌────────────┬────────────┬────────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│ exec_date  ┆ open       ┆ high       ┆ low       ┆ close     ┆ volume    ┆ buy_vol   ┆ sell_vol  │
│ ---        ┆ ---        ┆ ---        ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       │
│ datetime[μ ┆ f64        ┆ f64        ┆ f64       ┆ f64       ┆ f64       ┆ f64       ┆ f64       │
│ s]         ┆            ┆            ┆           ┆           ┆           ┆           ┆           │
╞════════════╪════════════╪════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ 2023-01-18 ┆ 2.65905e6  ┆ 2.659707e6 ┆ 2.658865e ┆ 2.659704e ┆ 0.60019   ┆ 0.386514  ┆ 0.213676  │
│ 23:58:00   ┆            ┆            ┆ 6         ┆ 6         ┆           ┆           ┆           │
│ 2023-01-18 ┆ 2.659707e6 ┆ 2.6606e6   ┆ 2.657831e ┆ 2.658154e ┆ 8.7166    ┆ 2.947514  ┆ 5.769086  │
│ 23:59:00   ┆            ┆            ┆ 6         ┆ 6         ┆           ┆           ┆           │
│ 2023-01-19 ┆ 2.658686e6 ┆ 2.661062e6 ┆ 2.655583e ┆ 2.66045e6 ┆ 31.59459  ┆ 12.815024 ┆ 18.779565 │
│ 00:00:00   ┆            ┆            ┆ 6         ┆           ┆           ┆           ┆           │
│ 2023-01-19 ┆ 2.660304e6 ┆ 2.661604e6 ┆ 2.657973e ┆ 2.660257e ┆ 12.014875 ┆ 5.463205  ┆ 6.551669  │
│ 00:01:00   ┆            ┆            ┆ 6         ┆ 6         ┆           ┆           ┆           │
│ 2023-01-19 ┆ 2.65947e6  ┆ 2.663093e6 ┆ 2.657845e ┆ 2.658557e ┆ 11.309212 ┆ 6.788335  ┆ 4.520876  │
│ 00:02:00   ┆            ┆            ┆ 6         ┆ 6         ┆           ┆           ┆           │
└────────────┴────────────┴────────────┴───────────┴───────────┴───────────┴───────────┴───────────┘

csv読み込みからリサンプリングするまでワンライナーでやるなら遅延評価有りの方が良い

path = "bitflyer/FX_BTC_JPY/trades/2023-01-17_2023-02-24.csv"

df = (
      pl.scan_csv(path)
      .with_columns([pl.col("exec_date").str.strptime(pl.Datetime, strict=False),
                     pl.when(pl.col('side') == 'BUY').then(pl.col('size')).otherwise(0).alias('buy_size'),
                     pl.when(pl.col('side') == 'SELL').then(pl.col('size')).otherwise(0).alias('sell_size')])
      .groupby_dynamic('exec_date', every='1m')
      .agg([
            pl.col('price').first().alias('open'),
            pl.col('price').max().alias('high'),
            pl.col('price').min().alias('low'),
            pl.col('price').last().alias('close'),
            pl.col('size').sum().alias('volume'),
            pl.col('buy_size').sum().alias('buy_vol'),
            pl.col('sell_size').sum().alias('sell_vol'),
      ])
      .collect()
      )

スライス

pandasではdf["2023-02-01 00:00:00": "2023-02-10 00:00:00"]といった感じでスライスしてたのをpolarsでやる場合はfilterを使う。

start_date = datetime(2023, 1, 19, 00, 00, 00) # 始点
end_date = datetime(2023, 1, 20, 00, 00, 00) # 終点

# datetimeがstart_date以上 & datetimeがend_date以下
df.filter((pl.col("exec_date") >= start_date) & (pl.col("exec_date") <= end_date))

┌─────────────┬────────────┬────────────┬────────────┬────────────┬──────────┬──────────┬──────────┐
│ exec_date   ┆ open       ┆ high       ┆ low        ┆ close      ┆ volume   ┆ buy_vol  ┆ sell_vol │
│ ---         ┆ ---        ┆ ---        ┆ ---        ┆ ---        ┆ ---      ┆ ---      ┆ ---      │
│ datetime[μs ┆ f64        ┆ f64        ┆ f64        ┆ f64        ┆ f64      ┆ f64      ┆ f64      │
│ ]           ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
╞═════════════╪════════════╪════════════╪════════════╪════════════╪══════════╪══════════╪══════════╡
│ 2023-01-19  ┆ 2.658686e6 ┆ 2.659096e6 ┆ 2.656967e6 ┆ 2.659096e6 ┆ 2.12     ┆ 0.08     ┆ 2.04     │
│ 00:00:00    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-19  ┆ 2.657724e6 ┆ 2.65985e6  ┆ 2.657724e6 ┆ 2.65985e6  ┆ 1.135364 ┆ 1.105364 ┆ 0.03     │
│ 00:00:01    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-19  ┆ 2.658704e6 ┆ 2.659023e6 ┆ 2.658282e6 ┆ 2.658282e6 ┆ 0.14866  ┆ 0.065    ┆ 0.08366  │
│ 00:00:02    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-19  ┆ 2.658918e6 ┆ 2.659406e6 ┆ 2.658151e6 ┆ 2.659406e6 ┆ 0.21     ┆ 0.1      ┆ 0.11     │
│ 00:00:03    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ ...         ┆ ...        ┆ ...        ┆ ...        ┆ ...        ┆ ...      ┆ ...      ┆ ...      │
│ 2023-01-19  ┆ 2.708e6    ┆ 2.708e6    ┆ 2.708e6    ┆ 2.708e6    ┆ 0.64899  ┆ 0.64899  ┆ 0.0      │
│ 23:59:47    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-19  ┆ 2.708063e6 ┆ 2.708063e6 ┆ 2.707909e6 ┆ 2.707909e6 ┆ 0.1      ┆ 0.0      ┆ 0.1      │
│ 23:59:51    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-19  ┆ 2.708474e6 ┆ 2.708474e6 ┆ 2.708474e6 ┆ 2.708474e6 ┆ 0.020273 ┆ 0.015    ┆ 0.005273 │
│ 23:59:59    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
│ 2023-01-20  ┆ 2.708176e6 ┆ 2.708176e6 ┆ 2.708176e6 ┆ 2.708176e6 ┆ 0.02     ┆ 0.02     ┆ 0.0      │
│ 00:00:00    ┆            ┆            ┆            ┆            ┆          ┆          ┆          │
└─────────────┴────────────┴────────────┴────────────┴────────────┴──────────┴──────────┴──────────┘

バックテストか分析で複数データフレームが欲しいときの関数用意しました。
連続する日付毎に区切ることができます。
df: データフレーム
start_date: 始まりの時間
interval: 間隔
quantity: 数
dt_col: datetimeなカラム
days=intervalの部分をseconds=intervalとかに変えてもok

def df_list(df: pl.DataFrame, start_date: datetime, interval: int, quantity: int, dt_col: str=""):
    # 日付リストを生成する
    date_list = [start_date + timedelta(days=interval*i)
                for i in range(quantity)
                if start_date + timedelta(days=interval*i) <= df[dt_col].max()]

    # DataFrameリストを生成する
    return [df.filter((pl.col(dt_col).ge(start_date)) & (pl.col(dt_col).lt(end_date)))
            for start_date, end_date in [(date_list[i], date_list[i+1])
                for i in range(0, len(date_list)-1, interval)] + ([ (date_list[-2], date_list[-1]) ]
                    if len(date_list) % interval != 1 else [])]

talib

0.4.22からpl.dataframeにも対応
だがto_numpy()で変換してからの方が速い

high=df.get_column("high").to_numpy()
low=df.get_column("low").to_numpy()
close=df.get_column("close").to_numpy()

df.with_columns(
    [
        pl.Series(talib.SMA(close, 30)).alias("SMA"),
        pl.Series(talib.KAMA(close, 30)).alias("KAMA"),
        pl.Series(talib.ATR(high, low, close, 30)).alias('ATR'),
    ]
    )

マージ

先行指標を探索する際に使う

binance = pl.DataFrame([{'datetime': datetime(2023, 2, 11, 0, 0), 'close_bi': 21616.4},
                        {'datetime': datetime(2023, 2, 11, 0, 1), 'close_bi': 21622.6},
                        {'datetime': datetime(2023, 2, 11, 0, 2), 'close_bi': 21616.2},
                        {'datetime': datetime(2023, 2, 11, 0, 3), 'close_bi': 21616.6},
                        {'datetime': datetime(2023, 2, 11, 0, 4), 'close_bi': 21615.4}])
┌─────────────────────┬──────────┐
│ datetime            ┆ close_bi │
│ ---                 ┆ ---      │
│ datetime[μs]        ┆ f64      │
╞═════════════════════╪══════════╡
│ 2023-02-11 00:00:00 ┆ 21616.4  │
│ 2023-02-11 00:01:00 ┆ 21622.6  │
│ 2023-02-11 00:02:00 ┆ 21616.2  │
│ 2023-02-11 00:03:00 ┆ 21616.6  │
│ 2023-02-11 00:04:00 ┆ 21615.4  │
└─────────────────────┴──────────┘

bitflyer = pl.DataFrame([{'datetime': datetime(2023, 2, 11, 0, 0), 'close_bf': 2846642.0},
                        {'datetime': datetime(2023, 2, 11, 0, 1), 'close_bf': 2847064.0},
                        {'datetime': datetime(2023, 2, 11, 0, 2), 'close_bf': 2846381.0},
                        {'datetime': datetime(2023, 2, 11, 0, 3), 'close_bf': 2846301.0},
                        {'datetime': datetime(2023, 2, 11, 0, 4), 'close_bf': 2846222.0}])
┌─────────────────────┬────────────┐
│ datetime            ┆ close_bf   │
│ ---                 ┆ ---        │
│ datetime[μs]        ┆ f64        │
╞═════════════════════╪════════════╡
│ 2023-02-11 00:00:00 ┆ 2.846642e6 │
│ 2023-02-11 00:01:00 ┆ 2.847064e6 │
│ 2023-02-11 00:02:00 ┆ 2.846381e6 │
│ 2023-02-11 00:03:00 ┆ 2.846301e6 │
│ 2023-02-11 00:04:00 ┆ 2.846222e6 │
└─────────────────────┴────────────┘
# left,rightはデータフレーム,onは重ねたいカラム,
# nullに前の行の値を適用する,nullに後の行の値を適用する
def pl_merge(left, right, col):
    return left.join(right ,on=col, how="left").fill_null(strategy="forward").fill_null(strategy="backward")
pl_merge(binance, bitflyer, "datetime")
┌─────────────────────┬──────────┬────────────┐
│ datetime            ┆ close_bi ┆ close_bf   │
│ ---                 ┆ ---      ┆ ---        │
│ datetime[μs]        ┆ f64      ┆ f64        │
╞═════════════════════╪══════════╪════════════╡
│ 2023-02-11 00:00:00 ┆ 21616.4  ┆ 2.846642e6 │
│ 2023-02-11 00:01:00 ┆ 21622.6  ┆ 2.847064e6 │
│ 2023-02-11 00:02:00 ┆ 21616.2  ┆ 2.846381e6 │
│ 2023-02-11 00:03:00 ┆ 21616.6  ┆ 2.846301e6 │
│ 2023-02-11 00:04:00 ┆ 21615.4  ┆ 2.846222e6 │
└─────────────────────┴──────────┴────────────┘

Discussion