У меня есть фрейм данных Polars с большим количеством повторяющихся данных, которые я хотел бы объединить.
Вход:
shape: (3, 2)
┌─────┬──────┐
│ id ┆ data │
│ --- ┆ --- │
│ i64 ┆ str │
╞═════╪══════╡
│ 1 ┆ a │
│ 1 ┆ b │
│ 1 ┆ c │
└─────┴──────┘
Мое текущее (нерабочее) решение:
df = pl.DataFrame({'id': [1, 1, 1], 'data': ['a', 'b', 'c']})
df = df.join(df.select('id', 'data'), on='id')
Выход:
shape: (9, 3)
┌─────┬──────┬────────────┐
│ id ┆ data ┆ data_right │
│ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str │
╞═════╪══════╪════════════╡
│ 1 ┆ a ┆ a │
│ 1 ┆ b ┆ a │
│ 1 ┆ c ┆ a │
│ 1 ┆ a ┆ b │
│ 1 ┆ b ┆ b │
│ 1 ┆ c ┆ b │
│ 1 ┆ a ┆ c │
│ 1 ┆ b ┆ c │
│ 1 ┆ c ┆ c │
└─────┴──────┴────────────┘
Желаемый результат:
shape: (1, 4)
┌─────┬────────┬────────┬────────┐
│ id ┆ data_1 ┆ data_2 ┆ data_3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ str │
╞═════╪════════╪════════╪════════╡
│ 1 ┆ a ┆ b ┆ c │
└─────┴────────┴────────┴────────┘
Кажется, что самосоединение — это способ получить таблицу со всеми нужными мне столбцами, но я не уверен, как написать самосоединение, которое включало бы несколько столбцов, а не просто кучу строк, состоящих всего из двух, и циклическое выполнение. через самообъединения кажется неправильным, поскольку он быстро увеличивается в размерах. Это не проблема Polars, но я работаю в Python-Polars.
🤔 А знаете ли вы, что...
Синтаксис Python известен своей простотой и читаемостью.
Следующее дает вам df в нужном вам формате:
import polars as pl
df = (
pl.DataFrame({"id": [1, 1, 1], "data": ["a", "b", "c"]})
.group_by("id")
.agg(pl.col("data"))
.with_columns(structure=pl.col("data").list.to_struct())
.unnest("structure")
.drop("data")
)
print(df)
"""
┌─────┬─────────┬─────────┬─────────┐
│ id ┆ field_0 ┆ field_1 ┆ field_2 │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ str │
╞═════╪═════════╪═════════╪═════════╡
│ 1 ┆ a ┆ b ┆ c │
└─────┴─────────┴─────────┴─────────┘
"""
Вы можете использовать with_columns и group_by в столбце «id» и объединять столбец «данные» в списки. Теперь вы можете создавать новые столбцы, выбирая элементы из этих списков с помощью list.get()
.
Наконец, вы можете использовать n_unique для подсчета уникальных значений.
# df
df = pl.DataFrame({"id": [1, 1, 1], "data": ["a", "b", "c"]})
# group by 'ids' and aggregate 'data' into lists
grouped = df.group_by("id").agg(pl.col("data").alias("data_list"))
# create columns by selecting elements from the list
result = grouped.with_columns(
[
pl.col("data_list").list.get(i).alias(f"data_{i+1}")
for i in range(df["data"].n_unique())
]
).drop("data_list")
print(result)
┌─────┬────────┬────────┬────────┐
│ id ┆ data_1 ┆ data_2 ┆ data_3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ str │
╞═════╪════════╪════════╪════════╡
│ 1 ┆ a ┆ b ┆ c │
└─────┴────────┴────────┴────────┘
У преобразования «длинное в широкое» есть имя: .pivot()
Вы можете генерировать новые имена столбцов, используя «номер строки» для каждой группы.
df.with_columns(
row_number = pl.int_range(pl.len()).over("id"),
name = pl.format("data_{}", pl.int_range(pl.len()).over("id") + 1)
)
shape: (5, 4)
┌─────┬──────┬────────────┬────────┐
│ id ┆ data ┆ row_number ┆ name │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ i64 ┆ str │
╞═════╪══════╪════════════╪════════╡
│ 1 ┆ a ┆ 0 ┆ data_1 │
│ 1 ┆ b ┆ 1 ┆ data_2 │
│ 1 ┆ c ┆ 2 ┆ data_3 │
│ 2 ┆ d ┆ 0 ┆ data_1 │
│ 2 ┆ e ┆ 1 ┆ data_2 │
└─────┴──────┴────────────┴────────┘
Мы используем pl.format() для префикса data_
и добавляем 1.
(df.with_columns(name = pl.format("data_{}", pl.int_range(pl.len()).over("id") + 1))
.pivot("name", index = "id")
)
shape: (2, 4)
┌─────┬────────┬────────┬────────┐
│ id ┆ data_1 ┆ data_2 ┆ data_3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ str │
╞═════╪════════╪════════╪════════╡
│ 1 ┆ a ┆ b ┆ c │
│ 2 ┆ d ┆ e ┆ null │
└─────┴────────┴────────┴────────┘