在熔化polars数据框后,如何不添加索引将其旋转回原始形态?
import polars as pl
df = pl.DataFrame({
'A': range(1,4),
'B': range(1,4),
'C': range(1,4),
'D': range(1,4)
})
print(df)
shape: (3, 4)
┌─────┬─────┬─────┬─────┐
│ A ┆ B ┆ C ┆ D │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═════╪═════╪═════╪═════╡
│ 1 ┆ 1 ┆ 1 ┆ 1 │
│ 2 ┆ 2 ┆ 2 ┆ 2 │
│ 3 ┆ 3 ┆ 3 ┆ 3 │
└─────┴─────┴─────┴─────┘
df_melt = df.melt(
variable_name="recipe",
value_name="revenue")
print(df_melt)
shape: (12, 2)
┌────────┬─────────┐
│ recipe ┆ revenue │
│ --- ┆ --- │
│ str ┆ i64 │
╞════════╪═════════╡
│ A ┆ 1 │
│ A ┆ 2 │
│ A ┆ 3 │
│ B ┆ 1 │
│ B ┆ 2 │
│ … ┆ … │
│ C ┆ 2 │
│ C ┆ 3 │
│ D ┆ 1 │
│ D ┆ 2 │
│ D ┆ 3 │
└────────┴─────────┘
看起来我需要添加一个索引,才能把 df_melt
转换回原来的 df
形式?难道没有办法在不添加索引的情况下,直接转换一个 polars 数据框吗?
df_melt = df_melt.with_columns(index=pl.col("revenue").cum_count().over("recipe"))
df_melt.pivot(
index='index',
columns='recipe',
values='revenue',
aggregate_function='first'
)
shape: (3, 5)
┌───────┬─────┬─────┬─────┬─────┐
│ index ┆ A ┆ B ┆ C ┆ D │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═══════╪═════╪═════╪═════╪═════╡
│ 1 ┆ 1 ┆ 1 ┆ 1 ┆ 1 │
│ 2 ┆ 2 ┆ 2 ┆ 2 ┆ 2 │
│ 3 ┆ 3 ┆ 3 ┆ 3 ┆ 3 │
└───────┴─────┴─────┴─────┴─────┘
在 R 语言中,我可以在不使用索引的情况下完成类似的 melt 和 pivot 操作,我希望在 Python 中也能实现同样的功能。
df_pandas = df.to_pandas()
library(tidyverse)
library(reticulate)
df_long <-
py$df_pandas |>
pivot_longer(
everything(),
names_to = 'recipe',
values_to = 'value'
)
df_long |>
pivot_wider(
names_from='recipe',
values_from='value'
) |>
unnest(cols = c(A,B,C,D))
2 个回答
0
你不能完全避免使用 pivot
的索引,但你可以做很多其他的事情,来实现类似 pivot
的效果,而不需要索引。其实,直接添加一个明确的索引可能更好,或者更理想的是避免使用 melt。不过,如果你必须使用 melt,这里有一个方法可以做到,而不需要添加索引。
df_melt = df.melt()
# I skip naming the melt columns since we're round tripping them away.
(
df_melt
.group_by('variable', maintain_order=True)
.agg(pl.col('value'))
.with_columns(
pl.col('value').list.to_struct()
)
.unnest('value')
.pipe(lambda zz: (
zz
.select(pl.col("^field.*$"))
.transpose(column_names=zz['variable'])
))
)
shape: (3, 4)
┌─────┬─────┬─────┬─────┐
│ A ┆ B ┆ C ┆ D │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═════╪═════╪═════╪═════╡
│ 1 ┆ 1 ┆ 1 ┆ 1 │
│ 2 ┆ 2 ┆ 2 ┆ 2 │
│ 3 ┆ 3 ┆ 3 ┆ 3 │
└─────┴─────┴─────┴─────┘
这个 pipe
确实有点尴尬,因为 transpose
没有内置的方法可以把输入的第一列转换成输出的列名。
2
看起来我需要添加一个索引,才能把
df_melt
转回到原来的df
形式?
否则,系统怎么知道 df_melt
中的 revenue=1
的条目都是在同一行,而不是说 recipe=A
的收入是 2,recipe=B
的收入是 3,等等呢?你是根据你定义的 df
来推测的,但仅从你问题中定义的 df_melt
是无法做到这一点的。
如果这是个必要条件,我建议你在使用 melt
之前,先给 df
添加一个索引,这样 df_melt
就能携带这些信息:
>>> df_melt=df.with_row_index().melt(id_vars=["index"], variable_name="recipe",value_name="revenue")
>>> df_melt
shape: (12, 3)
┌───────┬────────┬─────────┐
│ index ┆ recipe ┆ revenue │
│ --- ┆ --- ┆ --- │
│ u32 ┆ str ┆ i64 │
╞═══════╪════════╪═════════╡
│ 0 ┆ A ┆ 1 │
│ 1 ┆ A ┆ 2 │
│ 2 ┆ A ┆ 3 │
│ 0 ┆ B ┆ 1 │
│ 1 ┆ B ┆ 2 │
│ … ┆ … ┆ … │
│ 1 ┆ C ┆ 2 │
│ 2 ┆ C ┆ 3 │
│ 0 ┆ D ┆ 1 │
│ 1 ┆ D ┆ 2 │
│ 2 ┆ D ┆ 3 │
└───────┴────────┴─────────┘
然后,透视操作可以利用 index
列来重建原来的数据。