Замените ячейку в столбце на основе ячейки в другом столбце в полярном DataFrame

Обратите внимание на следующее pl.DataFrame:

import polars as pl

df = pl.DataFrame(
    {
        "symbol": ["s1", "s1", "s2", "s2"],
        "signal": [0, 1, 2, 0],
        "trade": [None, 1, None, -1],
    }
)

shape: (4, 3)
┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ null  │
│ s2     ┆ 0      ┆ -1    │
└────────┴────────┴───────┘

Теперь мне нужно сгруппировать фрейм данных по symbol и проверить, не равна ли первая строка в каждой группе в столбце signal 0 (нулю). Если оно равно True, мне нужно заменить соответствующую ячейку в столбце trade значением в ячейке signal.

Вот что я на самом деле ищу:

shape: (4, 3)
┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ 2     │ <- copy value from the ``signal`` column
│ s2     ┆ 0      ┆ -1    │
└────────┴────────┴───────┘

Будет ли здесь всегда отсутствовать значение столбца trade?

Hericks 26.08.2024 16:24

Не обязательно.

Andi 26.08.2024 16:29

Хорошо, хорошо. Я просто спросил, потому что иначе можно было бы упростить с помощью pl.Expr.fill_null.

Hericks 26.08.2024 16:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
3
57
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Для этого можно использовать конструкцию когда-то-иначе.

  • Мы создаем условие, которое оценивается как True ровно для первых строк (создавайте индекс на лету, используя pl.int_range) в каждой группе с signal, не равным 0.
  • В зависимости от этого условия мы выбираем значение в столбце signal или trade.
df.with_columns(
    trade=pl.when(
        pl.col("signal") != 0,
        pl.int_range(pl.len()) == 0,
    ).then("signal").otherwise("trade").over("symbol")
)
shape: (4, 3)
┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ 2     │
│ s2     ┆ 0      ┆ -1    │
└────────┴────────┴───────┘

Это умное решение! Мне было интересно, можем ли мы работать с first() вместо создания индекса на лету?

Andi 26.08.2024 16:56

@ Энди Нет, насколько я знаю. Мы могли бы использовать pl.Expr.first, если бы хотели указать значение первой строки. Однако мы хотели бы поставить условие для текущей обработки первой строки (например, «нахождение» в первой строке).

Hericks 26.08.2024 17:59

@Andi Если у вас есть индексный столбец или любой столбец, содержащий уникальные значения в каждой группе symbol, вы можете заменить условие на pl.col("index") == pl.col("index").first(). В приведенном выше решении мы сами создаем индекс и сравниваем его с первым значением нашего индекса, которое всегда равно 0.

Hericks 26.08.2024 18:02

Это звучит разумно. Спасибо!

Andi 26.08.2024 20:37
df.with_columns(
    trade = pl.when(
        pl.int_range(pl.len()) == 0,
        pl.col.signal != 0
    ).then(
        pl.col.signal
    ).otherwise(
        pl.col.trade
    ).over("symbol")
)

┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ 2     │
│ s2     ┆ 0      ┆ -1    │
└────────┴────────┴───────┘

Использование cum_count над символом и , когда:

df.with_columns(
    trade=pl.when(
        pl.cum_count('symbol').over('symbol').eq(1),
        pl.col('signal') != 0,
    )
    .then('signal')
    .otherwise('trade')
)

Выход:

┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ 2     │
│ s2     ┆ 2      ┆ -2    │
└────────┴────────┴───────┘

Использование pl.cum_count работает только в том случае, если trade гарантированно отсутствует в первой строке каждой группы symbol.

Hericks 26.08.2024 16:28

@Hericks, действительно, он отличается от панд, тогда pl.cum_count('symbol').over('symbol').eq(1) все будет в порядке?

mozway 26.08.2024 16:29

Да, в пандах это будет работать, поскольку cum_count создает индекс строки. В полярах он подсчитывает ненулевые элементы. Поскольку мы не можем быть уверены, что первый элемент в каждой группе (не) равен нулю, его нельзя использовать здесь. Вместо этого pl.int_range(pl.len()) можно использовать для создания индекса строки.

Hericks 26.08.2024 16:32

Думаю, эквивалент groupby.cumcount — это int_range.over ;)

mozway 26.08.2024 16:32

Вы можете использовать .is_first_distinct() для идентификации нужных строк.

df.with_columns(pl.col.symbol.is_first_distinct().alias("checkme"))
shape: (4, 4)
┌────────┬────────┬───────┬─────────┐
│ symbol ┆ signal ┆ trade ┆ checkme │
│ ---    ┆ ---    ┆ ---   ┆ ---     │
│ str    ┆ i64    ┆ i64   ┆ bool    │
╞════════╪════════╪═══════╪═════════╡
│ s1     ┆ 0      ┆ null  ┆ true    │
│ s1     ┆ 1      ┆ 1     ┆ false   │
│ s2     ┆ 2      ┆ null  ┆ true    │
│ s2     ┆ 0      ┆ -1    ┆ false   │
└────────┴────────┴───────┴─────────┘

И используйте как часть логики «когда/то»:

df.with_columns(
    pl.when(
       pl.col.symbol.is_first_distinct(),
       pl.col.signal != 0
    )
    .then("signal")
    .otherwise("trade")
    .alias("trade")
)
shape: (4, 3)
┌────────┬────────┬───────┐
│ symbol ┆ signal ┆ trade │
│ ---    ┆ ---    ┆ ---   │
│ str    ┆ i64    ┆ i64   │
╞════════╪════════╪═══════╡
│ s1     ┆ 0      ┆ null  │
│ s1     ┆ 1      ┆ 1     │
│ s2     ┆ 2      ┆ 2     │
│ s2     ┆ 0      ┆ -1    │
└────────┴────────┴───────┘

это хорошо.

Roman Pekar 26.08.2024 18:33

Другие вопросы по теме

Похожие вопросы

Python/win32crypt: «Доступ запрещен» при попытке сохранить сертификат в хранилище Windows «локального компьютера», но работает с хранилищем «текущий пользователь»
Как перебирать вложенные списки различной длины в цикле Ansible?
Изменение имени строки кадра данных Python Pandas
Недостаточно памяти для перезагрузки: torch.OutOfMemoryError: CUDA недостаточно памяти
Создайте столбцы 0–1 на основе другого фрейма данных с датами начала и окончания событий
Pandas .loc возвращает серию или плавает непоследовательно
Ошибочные результаты прокрутки pandas с временным окном, сгруппированным по кадру данных, импортированному из BigQuery
Как разграничить текст, хранящийся в переменной, для создания кадра данных в Python?
Как я могу настроить белое пустое пространство вокруг таблицы Matplotlib, чтобы оно соответствовало размеру таблицы?
Пытаюсь оптимизировать реализацию ARPLS