Следующий фрейм данных представляет базовую структуру плоского дерева, как показано ниже, где пары (id, sub-id)
и (sub-id, key)
всегда уникальны и key
всегда представляют одно и то же под одним и тем же id
.
id1
└─┬─ sub-id
│ │ └─── key1
│ │ └─── value
│ └─ sub-id2
│ └─── key1
│ └─── None
id2
└─── sub-id3
└─── key2
└─── value
без графического представления, ниже приведено определение как polars.DataFrame
df = pl.DataFrame(
{
"id": [1, 1, 2, 2, 2, 3],
"sub-id": [1, 1, 2, 3, 3, 4],
"key": ["key_1_1", "key_1_2", "key_2_1", "key_2_1", "key_2_2", "key_3"],
"value": ["value1 1", "value 1 2", None, "value 2 1", "value 2 2", "value 3"],
}
)
Тот же фрейм данных в табличном представлении:
shape: (6, 4)
┌────┬────────┬─────────┬───────────┐
│ ID │ sub-id │ key │ value │
╞════╪════════╪═════════╪═══════════╡
│ 1 │ 1 │ key_1_1 │ value 1 │
│ 1 │ 1 │ key_1_2 │ value 2 │
│ 2 │ 2 │ key_2_1 │ value 2 1 │
│ 2 │ 3 │ key_2_1 │ None │
│ 2 │ 3 │ key_2_2 │ value 2 2 │
│ 3 │ 4 │ key_3 │ value 3 │
└────┴────────┴─────────┴───────────┘
Как бы мне заполнить пробелы, как показано ниже, используя polars
. общий размер данных составляет около 100 тыс. строк.
shape: (6, 4)
┌────┬────────┬─────────┬───────────┐
│ ID │ sub-id │ key │ value │
╞════╪════════╪═════════╪═══════════╡
│ 1 │ 1 │ key_1_1 │ value 1 │
│ 1 │ 1 │ key_1_2 │ value 2 │
│ 2 │ 2 │ key_2_1 │ value 2 1 │
│ 2 │ 3 │ key_2_1 │ value 2 1 │
│ 2 │ 3 │ key_2_2 │ value 2 2 │
│ 3 │ 4 │ key_3 │ value 3 │
└────┴────────┴─────────┴───────────┘
🤔 А знаете ли вы, что...
Python популярен в анализе данных и машинном обучении с помощью библиотеки scikit-learn.
Что поможет вам добиться этого, так это выполнение forward fill operation
на основе id
и key
, которое гарантирует, что отсутствующие значения в одних и тех же группах id
и key
заполняются самым последним ненулевым значением.
Использование with_columns позволило использовать операцию forward_fill
Чтобы уточнить далее:
pl.col("value")
: выбирает столбец значений из DataFrame.
front_fill(): этот метод заполняет любые значения None последним известным ненулевым значением в указанном контексте.
over(["id", "key"]):
Это гарантирует, что операция заполнения учитывает группировку как по id
, так и по key
.
Другими словами, операция заполнения происходит только внутри каждой группы идентификаторов и ключей, поэтому значения из разных групп не мешают друг другу.
Я предоставил фрагмент кода, который поможет вам...
import polars as pl
# Define the DataFrame
df = pl.DataFrame(
{
"ID": [1, 1, 2, 2, 2, 3],
"sub-id": [1, 1, 2, 3, 3, 4],
"key": ["key_1_1", "key_1_2", "key_2_1", "key_2_1", "key_2_2", "key_3"],
"value": ["value 1", "value 2", "value 2 1", None, "value 2 2", "value 3"],
}
)
# Perform the fill operation
filled_df = df.with_columns(pl.col("value").forward_fill().over(["ID", "key"]))
# Display the result
print(filled_df)
Результаты:
shape: (6, 4)
┌─────┬────────┬─────────┬───────────┐
│ ID ┆ sub-id ┆ key ┆ value │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str ┆ str │
╞═════╪════════╪═════════╪═══════════╡
│ 1 ┆ 1 ┆ key_1_1 ┆ value 1 │
│ 1 ┆ 1 ┆ key_1_2 ┆ value 2 │
│ 2 ┆ 2 ┆ key_2_1 ┆ value 2 1 │
│ 2 ┆ 3 ┆ key_2_1 ┆ value 2 1 │
│ 2 ┆ 3 ┆ key_2_2 ┆ value 2 2 │
│ 3 ┆ 4 ┆ key_3 ┆ value 3 │
└─────┴────────┴─────────┴───────────┘
Альтернативно
Вы также можете использовать join
, чтобы добиться этого, присоединив ненулевой df к исходному df, который у вас был раньше.
Ниже приведен фрагмент кода
import polars as pl
# Define the DataFrame
df = pl.DataFrame(
{
"ID": [1, 1, 2, 2, 2, 3],
"sub-id": [1, 1, 2, 3, 3, 4],
"key": ["key_1_1", "key_1_2", "key_2_1", "key_2_1", "key_2_2", "key_3"],
"value": ["value 1", "value 2", "value 2 1", None, "value 2 2", "value 3"],
}
)
# Separate the non-null values
non_null_df = df.filter(pl.col("value").is_not_null())
# Join the original DataFrame with the non-null DataFrame
new_table_joined_df = df.join(non_null_df, on=["ID", "key"], how = "left", suffix = "_filled")
# Fill the original `value` column with the joined `value_filled` column
filled_df = new_table_joined_df.with_columns(pl.col("value").fill_null(pl.col("value_filled"))).select(df.columns)
# Display the result
print(filled_df)
Существует pl.Expr.fill_null для заполнения пропущенных значений.
В качестве значения заполнения мы используем первое ненулевое значение с теми же id
и key
. Поскольку мы предполагаем, что все значения для одних и тех же id
и key
одинаковы, разумно выбрать первое значение. Его можно построить следующим образом:
id
и key
.df.with_columns(
pl.col("value").fill_null(
pl.col("value").filter(pl.col("value").is_not_null()).first().over("id", "key")
)
)
shape: (6, 4)
┌─────┬────────┬─────────┬───────────┐
│ id ┆ sub-id ┆ key ┆ value │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str ┆ str │
╞═════╪════════╪═════════╪═══════════╡
│ 1 ┆ 1 ┆ key_1_1 ┆ value 1 │
│ 1 ┆ 1 ┆ key_1_2 ┆ value 2 │
│ 2 ┆ 2 ┆ key_2_1 ┆ value 2 1 │
│ 2 ┆ 3 ┆ key_2_1 ┆ value 2 1 │
│ 2 ┆ 3 ┆ key_2_2 ┆ value 2 2 │
│ 3 ┆ 4 ┆ key_3 ┆ value 3 │
└─────┴────────┴─────────┴───────────┘
Примечание. Мне нужно было немного адаптировать ваш код, чтобы он соответствовал примеру, который вы описали в тексте.
df = pl.DataFrame({
"id": [1, 1, 2, 2, 2, 3],
"sub-id": [1, 1, 2, 3, 3, 4],
"key": ["key_1_1", "key_1_2", "key_2_1", "key_2_1", "key_2_2", "key_3"],
"value": ["value 1", "value 2", None, "value 2 1", "value 2 2", "value 3"],
})