我需要计算多个交易证券的横截面排名。请考虑以下pl.DataFrame
长(整齐)格式。它包含三个不同的符号,每个符号都有各自的价格,每个符号还有一个专用的(即本地)交易日历。
df = pl.DataFrame(
{
"symbol": [*["symbol1"] * 6, *["symbol2"] * 5, *["symbol3"] * 5],
"date": [
"2023-12-30", "2023-12-31", "2024-01-03", "2024-01-04", "2024-01-05", "2024-01-06",
"2023-12-30", "2024-01-03", "2024-01-04", "2024-01-05", "2024-01-06",
"2023-12-30", "2023-12-31", "2024-01-03", "2024-01-04", "2024-01-05",
],
"price": [
100, 105, 110, 115, 120, 125,
200, 210, 220, 230, 240,
3000, 3100, 3200, 3300, 3400,
],
}
)
print(df)
shape: (16, 3)
┌─────────┬────────────┬───────┐
│ symbol ┆ date ┆ price │
│ --- ┆ --- ┆ --- │
│ str ┆ str ┆ i64 │
╞═════════╪════════════╪═══════╡
│ symbol1 ┆ 2023-12-30 ┆ 100 │
│ symbol1 ┆ 2023-12-31 ┆ 105 │
│ symbol1 ┆ 2024-01-03 ┆ 110 │
│ symbol1 ┆ 2024-01-04 ┆ 115 │
│ symbol1 ┆ 2024-01-05 ┆ 120 │
│ … ┆ … ┆ … │
│ symbol3 ┆ 2023-12-30 ┆ 3000 │
│ symbol3 ┆ 2023-12-31 ┆ 3100 │
│ symbol3 ┆ 2024-01-03 ┆ 3200 │
│ symbol3 ┆ 2024-01-04 ┆ 3300 │
│ symbol3 ┆ 2024-01-05 ┆ 3400 │
└─────────┴────────────┴───────┘
第一步是使用计算周期收益pct_change
,然后使用pivot
来对齐每个日期的符号。
returns = df.drop_nulls().with_columns(
pl.col("price").pct_change(n=2).over("symbol").alias("return")
).pivot(on="symbol", index="date", values="return")
print(returns)
shape: (6, 4)
┌────────────┬──────────┬──────────┬──────────┐
│ date ┆ symbol1 ┆ symbol2 ┆ symbol3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 │
╞════════════╪══════════╪══════════╪══════════╡
│ 2023-12-30 ┆ null ┆ null ┆ null │
│ 2023-12-31 ┆ null ┆ null ┆ null │
│ 2024-01-03 ┆ 0.1 ┆ null ┆ 0.066667 │
│ 2024-01-04 ┆ 0.095238 ┆ 0.1 ┆ 0.064516 │
│ 2024-01-05 ┆ 0.090909 ┆ 0.095238 ┆ 0.0625 │
│ 2024-01-06 ┆ 0.086957 ┆ 0.090909 ┆ null │
└────────────┴──────────┴──────────┴──────────┘
下一步是用来concat_list
创建一个list
来计算每行的排名(降序,即最高回报获得排名 1)。
ranks = (
returns.with_columns(all_symbols=pl.concat_list(pl.all().exclude("date")))
.select(
pl.all().exclude("all_symbols"),
pl.col("all_symbols")
.list.eval(
pl.element().rank(descending=True, method="ordinal").cast(pl.UInt8)
)
.alias("rank"),
)
)
print(ranks)
shape: (6, 5)
┌────────────┬──────────┬──────────┬──────────┬────────────────────┐
│ date ┆ symbol1 ┆ symbol2 ┆ symbol3 ┆ rank │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ list[u8] │
╞════════════╪══════════╪══════════╪══════════╪════════════════════╡
│ 2023-12-30 ┆ null ┆ null ┆ null ┆ [null, null, null] │
│ 2023-12-31 ┆ null ┆ null ┆ null ┆ [null, null, null] │
│ 2024-01-03 ┆ 0.1 ┆ null ┆ 0.066667 ┆ [1, null, 2] │
│ 2024-01-04 ┆ 0.095238 ┆ 0.1 ┆ 0.064516 ┆ [2, 1, 3] │
│ 2024-01-05 ┆ 0.090909 ┆ 0.095238 ┆ 0.0625 ┆ [2, 1, 3] │
│ 2024-01-06 ┆ 0.086957 ┆ 0.090909 ┆ null ┆ [2, 1, null] │
└────────────┴──────────┴──────────┴──────────┴────────────────────┘
现在我们终于要开始讨论实际问题了:我
想ranks
再次取消透视并生成一个整洁的数据框。我正在寻找以下列: symbol
、、和。我考虑创建三个新列(基本上用来解包列表,但这只会创建新的行而不是列)。date
return
rank
explode
另外,我想知道我是否df
首先需要进行旋转,或者是否有更好的方法直接df
以整洁的格式对原始数据进行操作?我实际上正在寻找性能,因为df
可能有数百万行。
那么你可以简化这个过程而不需要
explode
并避免旋转和取消旋转:接下来对返回值进行排序:
并且仅选择
symbol
、date
、return
和rank
列: