Умножить столбцы поляры числового типа на тип объекта (который поддерживает __mul__)

У меня есть следующий код.

import polars as pl

class Summary:
    def __init__(self, value: float, origin: str):
        self.value  = value
        self.origin = origin

    def __repr__(self) -> str:
        return f'Summary({self.value},{self.origin})'

    def __mul__(self, x: float | int) -> 'Summary':
        return Summary(self.value * x, self.origin)

    def __rmul__(self, x: float | int) -> 'Summary':
        return self * x

mapping = {
    'CASH':  Summary( 1, 'E'),
    'ITEM':  Summary(-9, 'A'),
    'CHECK': Summary(46, 'A'),
}

df = pl.DataFrame({'quantity': [7, 4, 10], 'type': mapping.keys(), 'summary': mapping.values()})

Кадр данных df выглядит следующим образом.

shape: (3, 3)
┌──────────┬───────┬───────────────┐
│ quantity ┆ type  ┆ summary       │
│ ---      ┆ ---   ┆ ---           │
│ i64      ┆ str   ┆ object        │
╞══════════╪═══════╪═══════════════╡
│ 7        ┆ CASH  ┆ Summary(1,E)  │
│ 4        ┆ ITEM  ┆ Summary(-9,A) │
│ 10       ┆ CHECK ┆ Summary(46,A) │
└──────────┴───────┴───────────────┘

В частности, столбец summary содержит объект класса Summary, который поддерживает умножение. Теперь я хотел бы умножить этот столбец на столбец quantity.

Однако наивный подход приводит к ошибке.

df.with_columns(pl.col('quantity').mul(pl.col('summary')).alias('qty_summary'))
SchemaError: failed to determine supertype of i64 and object

Есть ли способ умножить эти столбцы?

Обычно вам следует избегать использования столбцов типа object в вашем фрейме данных, поскольку нельзя применить встроенную операцию поляры. Будет ли возможность хранить объекты Summary как struct с полями value и origin?

Hericks 31.07.2024 15:39

Причина, по которой я пытался использовать объект Summary (который в моем реальном коде намного больше), заключается в том, что у меня есть некоторые поля, которые нужно умножать, например value, а некоторые не любят origin. Поэтому в идеале я сохраняю объект Summary, чтобы мне не приходилось вручную выбирать подмножество столбцов, которые необходимо умножить.

Phil-ZXX 31.07.2024 15:43

Я думаю, что это можно сделать с map_elements(), но я думаю, что применение ООП к кадрам данных Polars - это немного неправильное направление - вы не сможете получить выгоду от векторизации

Roman Pekar 31.07.2024 15:43

@RomanPekar Я думаю, что применить map_elements() из коробки непросто. Вам нужно будет применить его к столбцу структуры (что позволит вам получить доступ к мультипликативному коэффициенту), но все поля структуры должны иметь действительный тип поляры.

Hericks 31.07.2024 15:48

Понятно, возможно, я мог бы использовать 2 столбца struct: один для полей, которые можно умножать, а другой для полей, к которым умножение не применяется?

Phil-ZXX 31.07.2024 15:49

Вам даже не нужен столбец структуры. Просто сохраните данные в обычных столбцах и получите список имен столбцов, к которым можно применить умножение.

Hericks 31.07.2024 15:50
Почему в 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
6
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В комментариях вы упомянули, что хотели бы иметь возможность умножать определенные поля столбца Summary на другой столбец.

Возможно, вы могли бы сохранить свои данные в виде структуры:

df = pl.DataFrame({
    "type": ["CASH","ITEM","CHECK"], 
    "summary": [{'value':  1.0, 'origin': 'E'}, {'value': -9.0, 'origin': 'A'},  {'value': 46.0, 'origin': 'A'}],
    "quantity": [7,4,10]
})

┌───────┬────────────┬──────────┐
│ type  ┆ summary    ┆ quantity │
│ ---   ┆ ---        ┆ ---      │
│ str   ┆ struct[2]  ┆ i64      │
╞═══════╪════════════╪══════════╡
│ CASH  ┆ {1.0,"E"}  ┆ 7        │
│ ITEM  ┆ {-9.0,"A"} ┆ 4        │
│ CHECK ┆ {46.0,"A"} ┆ 10       │
└───────┴────────────┴──────────┘

И просто отдельно определите список полей, на которые вы хотите повлиять. Вы можете использовать with_fields() для умножения некоторых полей структуры:

fields_to_multiply = ["value"]

df.with_columns(
    pl.col.summary.struct.with_fields(
        pl.field(fields_to_multiply) * pl.col.quantity
    )
)


┌───────┬─────────────┬──────────┐
│ type  ┆ summary     ┆ quantity │
│ ---   ┆ ---         ┆ ---      │
│ str   ┆ struct[2]   ┆ i64      │
╞═══════╪═════════════╪══════════╡
│ CASH  ┆ {7.0,"E"}   ┆ 7        │
│ ITEM  ┆ {-36.0,"A"} ┆ 4        │
│ CHECK ┆ {460.0,"A"} ┆ 10       │
└───────┴─────────────┴──────────┘
Ответ принят как подходящий

Помните, что Polars спроектирован так, что вычисления выполняются на Rust, а не на Python, где они примерно в 1000 раз быстрее. Если у вас есть операции Python, которые вы хотите выполнить, вы в первую очередь теряете большую часть преимуществ использования Polars.

Но, к счастью, у Polars есть очень приятная функция, которая здесь актуальна, а именно «родная» обработка классов данных .

import polars as pl
from dataclasses import dataclass


@dataclass
class Summary:
    value: float
    origin: str

    def __mul__(self, x: float | int) -> "Summary":
        return Summary(self.value * x, self.origin)

    def __rmul__(self, x: float | int) -> "Summary":
        return self * x


mapping = {
    "CASH": Summary(1, "E"),
    "ITEM": Summary(-9, "A"),
    "CHECK": Summary(46, "A"),
}

df = pl.DataFrame(
    {
        "quantity": [7, 4, 10],
        "type": mapping.keys(),
        "summary": mapping.values(),
    }
)

df

Поскольку Summary — это dataclass, вам 1. не нужны __init__ и __repr__ (они приходят бесплатно) и 2. не нужно делать для Polars ничего особенного, чтобы их структурировать.

shape: (3, 3)
┌──────────┬───────┬────────────┐
│ quantity ┆ type  ┆ summary    │
│ ---      ┆ ---   ┆ ---        │
│ i64      ┆ str   ┆ struct[2]  │
╞══════════╪═══════╪════════════╡
│ 7        ┆ CASH  ┆ {1.0,"E"}  │
│ 4        ┆ ITEM  ┆ {-9.0,"A"} │
│ 10       ┆ CHECK ┆ {46.0,"A"} │
└──────────┴───────┴────────────┘

Теперь вы можете просто выполнять обычные операции структуры Polars:

df.with_columns(
    qty_summary=pl.struct(
        pl.col("summary").struct.field("value") * pl.col("quantity"),
        pl.col("summary").struct.field("origin"),
    )
)

Я не знал о готовой dataclass поддержке. Это потрясающе!

Hericks 31.07.2024 18:01

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