Как я могу удалить нули в процессе плавления кадра данных Polars?

У меня есть большой фрейм данных Polars, который мне нужен melt. Этот фрейм данных содержит множество нулевых значений (по крайней мере половину). Я хочу удалить нули при плавлении кадра данных. Я уже пробовал сначала melt фрейм данных, а затем фильтровать его с помощью drop_nulls() или аналогичных подходов. Однако это слишком ресурсоемко (на машине с ОЗУ около 1 ТБ).

Есть ли способ отфильтровать набор данных уже в процессе плавления?

Любая помощь приветствуется!

Пример данных.

# in reality, this dataset has about 160k rows and columns 
# (square matrix), and is about 100GB
df = {
    "A": [None, 2, 3],
    "B": [None, None, 2],
    "C": [None, None, None], 
    "names": ["A", "B", "C"]
}
df = pl.DataFrame(df)

df.melt(id_vars = "names", variable_name = "names_2", value_name = "distance")

Выход.

shape: (9, 3)
┌───────┬─────────┬──────────┐
│ names ┆ names_2 ┆ distance │
│ ---   ┆ ---     ┆ ---      │
│ str   ┆ str     ┆ i64      │
╞═══════╪═════════╪══════════╡
│ A     ┆ A       ┆ null     │
│ B     ┆ A       ┆ null     │
│ C     ┆ A       ┆ null     │
│ A     ┆ B       ┆ 2        │
│ B     ┆ B       ┆ null     │
│ C     ┆ B       ┆ null     │
│ A     ┆ C       ┆ 3        │
│ B     ┆ C       ┆ 2        │
│ C     ┆ C       ┆ null     │
└───────┴─────────┴──────────┘

Затем это можно было бы отфильтровать (например, с помощью df = df.drop_nulls()), но я хотел бы получить желаемый результат непосредственно из расплава.

Ожидаемый результат.

shape: (3, 3)
┌───────┬─────────┬──────────┐
│ names ┆ names_2 ┆ distance │
│ ---   ┆ ---     ┆ ---      │
│ str   ┆ str     ┆ i64      │
╞═══════╪═════════╪══════════╡
│ A     ┆ B       ┆ 2        │
│ A     ┆ C       ┆ 3        │
│ B     ┆ C       ┆ 2        │
└───────┴─────────┴──────────┘

Не совсем понятно, что вы подразумеваете под «набор данных содержит около 160 тыс. строк и столбцов». Не могли бы вы уточнить, сколько столбцов имеет DataFrame?

Roman Pekar 06.08.2024 14:52

Я соответствующим образом изменил вопрос. Это квадратная матрица, поэтому она имеет одинаковое количество строк и столбцов. Я просто хочу подчеркнуть, что набор данных большой, поэтому подход «сначала плавление, затем фильтрация» не работает.

Nils R 06.08.2024 14:54

Вы пробовали вместо этого использовать LazyFrame?

BallpointBen 06.08.2024 18:36

В чем преимущество реализации LazyFrame в этом контексте? Насколько я понимаю, они не оцениваются сразу, но в конечном итоге, когда они будут выполнены, им все равно придется выполнять melt и drop_nulls последовательно, не так ли?

Nils R 06.08.2024 22:26
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете создать итерацию кадров данных, которые позже можно объединить или обработать отдельно.

pl.concat(
    df.filter(pl.col(c).is_not_null()).select(
        names = pl.lit(c),
        names_2 = pl.col.names,
        distance = pl.col(c)
    ) for c in df.schema.keys() if c != "names"
)
shape: (3, 3)
┌───────┬─────────┬──────────┐
│ names ┆ names_2 ┆ distance │
│ ---   ┆ ---     ┆ ---      │
│ str   ┆ str     ┆ i64      │
╞═══════╪═════════╪══════════╡
│ A     ┆ B       ┆ 2        │
│ A     ┆ C       ┆ 3        │
│ B     ┆ C       ┆ 2        │
└───────┴─────────┴──────────┘

Спасибо за хорошее предложение! Кажется, в принципе это работает, однако похоже, что это намного медленнее, чем функция melt, и это проблема :/ Есть идеи, как сделать это более эффективным?

Nils R 06.08.2024 22:24
Ответ принят как подходящий

Скорее всего, вы сможете сделать операцию более эффективной с точки зрения времени выполнения и потребления памяти, используя pl.LazyFrames и потоковый движок Polars.

Используя pl.LazyFrames , Melt/Unpivot и Filter / drop_nulls не будут выполняться сразу, а сначала агрегироваться в план запроса. При сборе ленивого DataFrame (т. е. материализации его в pl.DataFrame) план запроса можно оптимизировать с учетом последующих операций.

Потоковая передача позволит выполнять обработку не сразу, а пакетно, гарантируя, что обработанные пакеты не вырастут за пределы памяти.

(
    df
    # convert to pl.LazyFrame
    .lazy()
    # create query plan
    .melt(
        id_vars = "names",
        variable_name = "names_2",
        value_name = "distance"
    )
    .drop_nulls()
    # collect pl.LazyFrame while using streaming engine
    .collect(streaming=True)
)

Примечание. Предварительные тесты на моей машине дали значительные улучшения во времени выполнения и потреблении памяти.

Большое спасибо, это звучит идеально! Я еще не проверял его по существу, но на данный момент я приму ответ и отвечу на новый, если у меня возникнут более конкретные проблемы.

Nils R 12.08.2024 13:29

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