Поляры: установите недостающее значение из другой строки

Следующий фрейм данных представляет базовую структуру плоского дерева, как показано ниже, где пары (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.


2
51
2

Ответы:

Что поможет вам добиться этого, так это выполнение forward fill operation на основе id и key, которое гарантирует, что отсутствующие значения в одних и тех же группах id и key заполняются самым последним ненулевым значением.

Использование with_columns позволило использовать операцию forward_fill

Чтобы уточнить далее:

  1. pl.col("value"): выбирает столбец значений из DataFrame. front_fill(): этот метод заполняет любые значения None последним известным ненулевым значением в указанном контексте.

  2. 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 одинаковы, разумно выбрать первое значение. Его можно построить следующим образом:

  • pl.Expr.filter и pl.Expr.is_not_null для фильтрации ненулевых значений,
  • pl.Expr.first, чтобы выбрать первое такое значение,
  • оконная функция pl.Expr.over для вычисления выражения отдельно для каждой пары 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"],
})

Интересные вопросы для изучения

Почему использование scipy.optimize.curvefit с numpy.piecewise выдает ошибки атрибутов и времени выполнения?Захватите целое число в строке и используйте его как часть регулярного выраженияПочему в Python предварительное выделение массива numpy не ограничивает его точность печати?Как добавить значения в фрейм данных на основе перехода по столбцам на основе определенного числа?Выполните поиск в CSV и добавьте значение, если найдено правильное значение в другом CSVРасширьте/сузьте линейные участки графика, чтобы разрывы оси X в пиках располагались равномерноПреобразование подмножества фрейма данных в другой фрейм данныхОбрезать фрейм данных сверху или снизу в зависимости от столбца с NAЯ хочу иметь возможность давать определенное значение в столбце строк по столбцу int в rОбработка нескольких операций над столбцами DataFrame с помощью поляров