Как я узнаю, какая функция вызывается, когда я использую строку для аргумента func DataFrame.agg() ?

#python #pandas #dataframe

#python #pandas #dataframe

Вопрос:

Например, предположим, что у меня есть фрейм данных типа

 import pandas as pd
df = pd.DataFrame({'x': [1, 2, 3, 4]})
  

и я вызываю

 df.agg(func='sum')
  

Ссылается ли это на

  1. numpy.sum()
  2. DataFrame.sum()
  3. Series.sum()

Я понимаю, что под капотом они делают то же самое, но я все равно хотел бы знать, какая функция отправляется. Это где-нибудь задокументировано?

Ответ №1:

Это внутренние детали, я не думаю, что это будет задокументировано.

pandas-dev обрабатывает эти строки, т.е. 'sum' 'mean' Таким образом. У них есть сопоставление, в котором они сопоставляют функцию со своей внутренней цитонизированной реализацией этих функций.

Взято из pandas/core/base.py

 _cython_table = {
        builtins.sum: "sum",
        builtins.max: "max",
        builtins.min: "min",
        np.all: "all",
        np.any: "any",
        np.sum: "sum",
        np.nansum: "sum",
        np.mean: "mean",
        np.nanmean: "mean",
        np.prod: "prod",
        np.nanprod: "prod",
        np.std: "std",
        np.nanstd: "std",
        np.var: "var",
        np.nanvar: "var",
        np.median: "median",
        np.nanmedian: "median",
        np.max: "max",
        np.nanmax: "max",
        np.min: "min",
        np.nanmin: "min",
        np.cumprod: "cumprod",
        np.nancumprod: "cumprod",
        np.cumsum: "cumsum",
        np.nancumsum: "cumsum",
    }
  

Итак, Series.agg(sum) , Series.agg('sum') , Series.agg(np.sum) , Series.agg(np.nansum) все вызывают одну и ту же внутреннюю функцию с использованием cythonized .

Взято из pandas/core/base.py

     def _get_cython_func(self, arg: Callable) -> Optional[str]:
        """
        if we define an internal function for this argument, return it
        """
        return self._cython_table.get(arg)
  

Вы можете найти, как они обрабатывают это в pandas/core/aggregate.py , они используют getattr здесь, похоже, что cythonized funcs являются определенными атрибутами класса. Я не нашел, где, но хорошим местом для начала было бы pandas/core/generic.py взглянуть на stat_func

 def aggregate(
    obj: AggObjType,
    arg: AggFuncType,
    *args,
    **kwargs,
):
    ...
    ...
    if callable(arg):
        f = obj._get_cython_func(arg)
        if f and not args and not kwargs:
            return getattr(obj, f)(), None
   ...
   ...
  

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

1. Что здесь действительно интересно, так это то, что if x — серия pandas, np.sum(x) не использует реализацию NumPy of sum() . Скорее, он использует реализацию Pandas . Если x имеет NAN, np.sum(x) будет игнорировать их, что не так, если x бы это был массив NumPy. сегодня я выучил

2. @Ben Да, я сделал PR об этом за 2 месяца до этого, чтобы упомянуть об этом поведении в документах, объединенных с master, но запланированных для V1.2 tho. github.com/pandas-dev/pandas/pull/35042

Ответ №2:

@Ch3steR, спасибо, что помог мне увидеть свет. Однако я хочу немного подробнее остановиться на вашем ответе..

Исходный код для aggregate() включает в себя эти соответствующие строки

 def aggregate(
    obj: AggObjType,
    arg: AggFuncType,
    *args,
    **kwargs,
):

...

if isinstance(arg, str):
    return obj._try_aggregate_string_function(arg, *args, **kwargs), None
  

Затем мы отслеживаем _try_aggregate_string_function()

 def _try_aggregate_string_function(self, arg: str, *args, **kwargs):
        """
        if arg is a string, then try to operate on it:
        - try to find a function (or attribute) on ourselves
        - try to find a numpy function
        - raise
        """
        assert isinstance(arg, str)

        f = getattr(self, arg, None)
        if f is not None:
            if callable(f):
                return f(*args, **kwargs)

            # people may try to aggregate on a non-callable attribute
            # but don't let them think they can pass args to it
            assert len(args) == 0
            assert len([kwarg for kwarg in kwargs if kwarg not in ["axis"]]) == 0
            return f

        f = getattr(np, arg, None)
        if f is not None:
            if hasattr(self, "__array__"):
                # in particular exclude Window
                return f(self, *args, **kwargs)

        raise AttributeError(
            f"'{arg}' is not a valid function for '{type(self).__name__}' object"
        )
  

Итак, когда вы выполняете вызов like df.agg('foo') , Pandas сначала ищет атрибут DataFrame с именем foo , а затем ищет функцию NumPy с именем foo (предполагая foo , что она не существует как атрибут DataFrame).