PHP前端开发

Polar 计算百分位数

百变鹏仔 2天前 #Python
文章标签 位数
问题内容

我有一个极坐标数据框,其中有一列包含日期,其他列包含价格,我想计算 252 x 3 观测值窗口中每个列的百分位数。

为此,我正在这样做:

prices = prices.sort(by=["date"])rank_cols = list(set(prices.columns).difference("date"))percentiles = (    prices.sort(by=["date"])    .set_sorted("date")    .group_by_dynamic(        index_column=["date"], every="1i", start_by="window", period="756i"    )    .agg(        [            (pl.col(col).rank() * 100.0 / pl.col(col).count()).alias(                f"{col}_percentile"            )            for col in rank_cols        ]    ))

但是抛出的异常是:

traceback (most recent call last):  file "<string>", line 6, in <module>  file "/usr/local/lib/python3.10/site-packages/polars/dataframe/group_by.py", line 1047, in agg    self.df.lazy()  file "/usr/local/lib/python3.10/site-packages/polars/lazyframe/frame.py", line 1706, in collect    return wrap_df(ldf.collect())polars.exceptions.invalidoperationerror: argument in operation 'group_by_dynamic' is not explicitly sorted- if your data is already sorted, set the sorted flag with: '.set_sorted()'.- if your data is not sorted, sort the 'expr/series/column' first.</module></string>

在代码中,我已经按照建议执行了操作,但异常仍然存在。

编辑:

根据@hericks的建议进行一些更改。

import polars as plimport pandas as pdfrom datetime import datetime, timedelta# generate 10 dates starting from todaystart_date = datetime.now().date()date_list = [start_date + timedelta(days=i) for i in range(10)]# generate random prices for each date and columndata = {    'date': date_list,    'asset_1': [float(f"{i+1}.{i+2}") for i in range(10)],    'asset_2': [float(f"{i+2}.{i+3}") for i in range(10)],    'asset_3': [float(f"{i+3}.{i+4}") for i in range(10)],}prices = pl.dataframe(data)prices = prices.cast({"date": pl.date})rank_cols = list(set(prices.columns).difference("date"))percentiles = (    prices.sort(by=["date"])    .set_sorted("date")    .group_by_dynamic(        index_column="date", every="1i", start_by="window", period="4i"    )    .agg(        [            (pl.col(col).rank() * 100.0 / pl.col(col).count()).alias(                f"{col}_percentile"            )            for col in rank_cols        ]    ))

现在我明白了

pyo3_runtime.panicexception: attempt to divide by zero

编辑2:

问题是日期的使用,我用整数更改了日期,然后就解决了问题。 (还添加了先取第一个寄存器)

import polars as plint_list = [i+1 for i in range(6)]# Generate random prices for each date and columndata = {    'int_index': int_list,    'asset_1': [1.1, 3.4, 2.6, 4.8, 7.4, 3.2],    'asset_2': [4, 7, 8, 3, 4, 5],    'asset_3': [1, 3, 10, 20, 2, 4],}# Convert the Pandas DataFrame to a Polars DataFrameprices = pl.DataFrame(data)rank_cols = list(set(prices.columns).difference("int_index"))percentiles = (    prices.sort(by="int_index")    .set_sorted("int_index")    .group_by_dynamic(        index_column="int_index", every="1i", start_by="window", period="4i"    )    .agg(        [            (pl.col(col).rank().first() * 100.0 / pl.col(col).count()).alias(                f"{col}_percentile"            )            for col in rank_cols        ]    ))

编辑3:

给出的想法是,索引 i 取索引 i、i+1、i+2、i+3 上的值,并计算寄存器 i 相对于这四个值的百分位等级。

例如,对于 asset_1 中的第一个索引 (1),示例(以及接下来的三个寄存器)为:

1.1、3.4、2.6、4.8,因此第一个寄存器的百分位数为 25

对于 asset_1,第二个索引 (2) 示例(以及接下来的三个寄存器)是:

3.4、2.6、4.8 和 7.4,因此百分位数为 50。


正确答案


我仍然有点猜测您期望的答案是什么,但您可能可以从这个答案开始

因此,考虑到您的示例数据:

import polars as pl# generate random prices for each date and columnprices = pl.dataframe({    'int_index': range(6),    'asset_1': [1.1, 3.4, 2.6, 4.8, 7.4, 3.2],    'asset_2': [4, 7, 8, 3, 4, 5],    'asset_3': [1, 3, 10, 20, 2, 4],})┌───────────┬─────────┬─────────┬─────────┐│ int_index ┆ asset_1 ┆ asset_2 ┆ asset_3 ││ ---       ┆ ---     ┆ ---     ┆ ---     ││ i64       ┆ f64     ┆ i64     ┆ i64     │╞═══════════╪═════════╪═════════╪═════════╡│ 0         ┆ 1.1     ┆ 4       ┆ 1       ││ 1         ┆ 3.4     ┆ 7       ┆ 3       ││ 2         ┆ 2.6     ┆ 8       ┆ 10      ││ 3         ┆ 4.8     ┆ 3       ┆ 20      ││ 4         ┆ 7.4     ┆ 4       ┆ 2       ││ 5         ┆ 3.2     ┆ 5       ┆ 4       │└───────────┴─────────┴─────────┴─────────┘

使用 rolling() 创建窗口,然后(与您在问题中所做的相同) - rank().first() 除以 count()、name.suffix() 为列分配新名称:

cols = pl.all().exclude('int_index')percentiles = (    prices.sort(by="int_index")    .rolling(index_column="int_index", period="4i", offset="0i", closed="left")    .agg((cols.rank().first() * 100 / cols.count()).name.suffix('_percentile')))┌───────────┬────────────────────┬────────────────────┬────────────────────┐│ int_index ┆ asset_1_percentile ┆ asset_2_percentile ┆ asset_3_percentile ││ ---       ┆ ---                ┆ ---                ┆ ---                ││ i64       ┆ f64                ┆ f64                ┆ f64                │╞═══════════╪════════════════════╪════════════════════╪════════════════════╡│ 0         ┆ 25.0               ┆ 50.0               ┆ 25.0               ││ 1         ┆ 50.0               ┆ 75.0               ┆ 50.0               ││ 2         ┆ 25.0               ┆ 100.0              ┆ 75.0               ││ 3         ┆ 66.666667          ┆ 33.333333          ┆ 100.0              ││ 4         ┆ 100.0              ┆ 50.0               ┆ 50.0               ││ 5         ┆ 100.0              ┆ 100.0              ┆ 100.0              │└───────────┴────────────────────┴────────────────────┴────────────────────┘

您还可以检查每个窗口内的内容:

(    prices.sort(by="int_index")    .rolling(index_column="int_index", period="4i", offset="0i", closed="left")    .agg(cols))┌───────────┬───────────────────┬─────────────┬───────────────┐│ int_index ┆ asset_1           ┆ asset_2     ┆ asset_3       ││ ---       ┆ ---               ┆ ---         ┆ ---           ││ i64       ┆ list[f64]         ┆ list[i64]   ┆ list[i64]     │╞═══════════╪═══════════════════╪═════════════╪═══════════════╡│ 0         ┆ [1.1, 3.4, … 4.8] ┆ [4, 7, … 3] ┆ [1, 3, … 20]  ││ 1         ┆ [3.4, 2.6, … 7.4] ┆ [7, 8, … 4] ┆ [3, 10, … 2]  ││ 2         ┆ [2.6, 4.8, … 3.2] ┆ [8, 3, … 5] ┆ [10, 20, … 4] ││ 3         ┆ [4.8, 7.4, 3.2]   ┆ [3, 4, 5]   ┆ [20, 2, 4]    ││ 4         ┆ [7.4, 3.2]        ┆ [4, 5]      ┆ [2, 4]        ││ 5         ┆ [3.2]             ┆ [5]         ┆ [4]           │└───────────┴───────────────────┴─────────────┴───────────────┘