Некоторые из моих столбцов в моем кадре данных Polars имеют тип dtype pl.List(pl.Struct). Я пытаюсь заменить эти столбцы, чтобы получить несколько столбцов, представляющих собой списки скалярных значений.
Вот пример столбца, который я пытаюсь изменить:
import polars as pl
df = pl.DataFrame({
"column_0": [
[{"field_1": "a", "field_2": 1}, {"field_1": "b", "field_2":2}],
[{"field_1": "c", "field_2":3}]
]
})
col_name = "column_0"
df.select(
pl.col(col_name).list.eval(
pl.element().struct.field("*")
)
)
Я ожидал, что получу что-то вроде этого:
shape: (2, 2)
┌────────────┬───────────┐
│ field_1 ┆ field_2 │
│ --- ┆ --- │
│ list[str] ┆ list[i64] │
╞════════════╪═══════════╡
│ ["a", "b"] ┆ [1, 2] │
│ ["c"] ┆ [3] │
└────────────┴───────────┘
Вместо этого я получаю только последнее поле (в данном случае «field_2»):
shape: (2, 1)
┌───────────┐
│ column_0 │
│ --- │
│ list[i64] │
╞═══════════╡
│ [1, 2] │
│ [3] │
└───────────┘
Один .list.eval()
не создаст несколько новых столбцов, но такой результат выглядит странно. (Возможно, ошибка?) Знаете ли вы количество полей в структуре? Или вы пытаетесь сделать это «динамически» для любого количества полей?
Я пытаюсь сделать это динамически. Количество полей в структуре зависит от столбца.
struct.field("field_name")
извлекает определенные поля, поэтому вы можете использовать field_name
вместо *
для достижения результата.
result = df.select([
pl.col("column_0").list.eval(pl.element().struct.field("field_1")).alias("field_1"),
pl.col("column_0").list.eval(pl.element().struct.field("field_2")).alias("field_2"),
])
Если у вас больше полей, таких как field_2, вы можете использовать обобщенную функцию:
def split_struct_column(df: pl.DataFrame, col_name: str) -> pl.DataFrame:
first_struct = df[col_name].to_list()[0][0]
field_names = list(first_struct.keys())
exprs = [
pl.col(col_name).list.eval(pl.element().struct.field(field)).alias(field)
for field in field_names
]
return df.select(exprs)
result = split_struct_column(df, "column_0")
Я получаю вывод ниже,
┌────────────┬───────────┐
│ field_1 ┆ field_2 │
│ --- ┆ --- │
│ list[str] ┆ list[i64] │
╞════════════╪═══════════╡
│ ["a", "b"] ┆ [1, 2] │
│ ["c"] ┆ [3] │
└────────────┴───────────┘
Вы можете получить доступ к именам полей через атрибут pl.DataFrame.schema и создать подходящие выражения для создания столбцов списка следующим образом.
df.with_columns(
pl.col("column_0").list.eval(
pl.element().struct.field(field.name)
).alias(field.name)
for field in df.schema["column_0"].inner.fields
)
shape: (2, 3)
┌────────────────────┬────────────┬───────────┐
│ column_0 ┆ field_1 ┆ field_2 │
│ --- ┆ --- ┆ --- │
│ list[struct[2]] ┆ list[str] ┆ list[i64] │
╞════════════════════╪════════════╪═══════════╡
│ [{"a",1}, {"b",2}] ┆ ["a", "b"] ┆ [1, 2] │
│ [{"c",3}] ┆ ["c"] ┆ [3] │
└────────────────────┴────────────┴───────────┘
Ваше решение работает и очень полезно, но можно ли не использовать цикл for? Кроме того, где вы нашли атрибуты «.inner.fields» и «.name»? Я не могу найти их в документации Polars.
@Pain Я также пытался избежать цикла, но не нашел способа обойти его. По крайней мере, небольшое утешение можно найти в том, что вы также можете получить доступ к схеме pl.LazyFrame
s. Тем более, что решение все еще актуально. Атрибуты .inner.fields
и .name
типов данных pl.Struct
и pl.Field
я нашел, вручную проверив атрибуты объекта (используя .__dir__
).
@Pain Я только что заметил, что ответ jqurious ниже полностью избегает итераций.
Вы можете распаковать списки/структуры с помощью .explode()
+ .unnest()
и снова сгруппировать строки.
(df.with_row_index()
.explode("column_0")
.unnest("column_0")
.group_by("index", maintain_order=True)
.all()
)
shape: (2, 3)
┌───────┬────────────┬───────────┐
│ index ┆ field_1 ┆ field_2 │
│ --- ┆ --- ┆ --- │
│ u32 ┆ list[str] ┆ list[i64] │
╞═══════╪════════════╪═══════════╡
│ 0 ┆ ["a", "b"] ┆ [1, 2] │
│ 1 ┆ ["c"] ┆ [3] │
└───────┴────────────┴───────────┘
Очень хорошее решение, позволяющее избежать каких-либо итераций.
Спасибо за добавление данных, но наиболее полезной формой будет пример исполняемого кода, который настраивает фрейм данных без дополнительной настройки.