Зачем нам нужны три разных способа работы в pandas?

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

Зачем нам нужны три способа работы?

(Я использую умножение для примеров)

Первый способ:

 df['a'] * 5
  

Второй способ:

 df['a'].mul(5)
  

Третий способ:

 df['a'].__mul__(5)
  

Разве не достаточно двух, не нужно mul , мне было интересно, может ли это быть как обычные способы, например, целое число

Первый способ:

 3 * 5
  

Второй способ:

 (3).__mul__(5)
  

Но в обычных базах inetger:

 (3).mul(5)
  

Сломается.

Мне просто любопытно, зачем нам столько всего в Pandas, то же самое со сложением, вычитанием и делением.

Ответ №1:

* и mul сделайте то же самое, но __mul__ по-другому.

* и mul выполните некоторые проверки перед делегированием __mul__ . Есть две вещи, о которых вы должны знать.

  1. NotImplemented

Существует специальное одноэлементное значение NotImplemented , которое возвращается классом __mul__ в тех случаях, когда он не может обработать другой операнд. Затем это говорит Python попробовать __rmul__ . Если это тоже не удается, возникает общий TypeError . Если вы используете __mul__ напрямую, вы не получите эту логику. Наблюдайте:

 class TestClass:

    def __mul__(self, other):
        return NotImplemented

TestClass() * 1
  

Вывод:

 TypeError: unsupported operand type(s) for *: 'TestClass' and 'int'
  

Сравните это с этим:

 TestClass().__mul__(1)
  

Вывод:

 NotImplemented
  

Вот почему, как правило, вам следует избегать прямого вызова методов dunder (magic): вы обходите определенные проверки, которые выполняет Python.

  1. Обработка оператора производного класса

Когда вы пытаетесь выполнить что-то вроде Base() * Derived() where Derived inherits from Base , вы ожидаете Base.__mul__(Derived()) , что вас вызовут первым. Это может создать проблемы, поскольку Derived.__mul__ , скорее всего, знает, как обращаться с такими ситуациями.

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

Наблюдайте:

 class Base:

    def __mul__(self, other):
        print('base mul')

class Derived(Base):

    def __rmul__(self, other):
        print('derived rmul')

Base() * Derived()
  

Вывод:

 derived rmul
  

Обратите внимание, что, хотя Base.__mul__ не возвращает NotImplemented и может четко обрабатывать объект типа Derived , Python даже не смотрит на него первым; он делегирует Derived.__rmul__ сразу.

Для полноты картины, есть одно различие между * и mul , в контексте pandas : mul является функцией, и поэтому может передаваться в переменной и использоваться независимо. Например:

 import pandas as pd

pandas_mul = pd.DataFrame.mul
pandas_mul(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
  

С другой стороны, это приведет к сбою:

 *(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
  

Ответ №2:

И «волшебный метод» __mul__ , и оператор * одинаковы в базовом python ( * просто вызовы __mul__ ), и, как вы указали, это способ, которым python stadarized обрабатывает вещи. Другой метод mul — это метод, который вы можете использовать для сопоставления (использования map ) и избегать использования lambda x, y: x*mul , например. Да, вы все еще можете использовать __mul__ , но обычно эти методы ( __x__ ) не предназначены для использования в качестве обычных функций, а простой mul делает код более понятным.

Итак, вам это действительно не «нужно», но приятно иметь и использовать.

Ответ №3:

Во-первых, third way ( df['a'].__mul__(5) ) никогда не следует использовать, поскольку это внутренний метод, вызываемый классом Python. Как правило, пользователи не используют ни один из методов «dunder».

Что касается двух других способов, первый способ очевиден; вы просто умножаете это. Это стандартная математика.

Второй способ становится немного более интересным. Один из примеров того, как я использовал этот метод, — это когда функция, которую вы хотите применить, является переменной.

Например:

 def pandas_math(series, func, val):
    return getattr(series, func)(val)
  

pandas_math(df['a'], 'mul', 5) даст тот же результат, df['a'].mul(5) что и, но теперь вы можете передавать mul как переменную или любую другую функцию, которую вы хотите использовать. Это намного проще, чем жестко кодировать все символы.

Комментарии:

1. Вы могли бы использовать operator модуль, если df['a'].mul бы он не существовал, так что на самом деле он не служит цели в этом отношении.

2. Верно, но по какой-то причине Pandas решил встроить его. Я понял, что главный вопрос заключается в том, «почему я должен делать df['a'].mul(5) вместо df['a']*5 ?». Аналогичный вопрос «зачем вообще использовать operator модуль?»