###Содержание:
DataFrame.apply() VS DataFrame.map();DataFrame.apply() вызывает пользовательскую функцию func вдоль оси axis;Series.apply() вызывает пользовательскую функцию func для значений Series;DataFrameGroupBy.apply() вызывает пользовательскую функцию func для объекта группировки;DataFrame.map() применяет пользовательскую функцию к DataFrame поэлементно;Series.map() заменяет каждое значение Series, значением, которое может быть получено из функции, словаря или Series.DataFrame.apply() VS DataFrame.map()Метод DataFrame.apply() применяет пользовательскую функцию func вдоль оси axis, т. е. работает со всеми значениями столбца или строки. Другими словами, пользовательская функция принимает в качестве обязательного аргумента Series - сразу все значения столбца или строки (в зависимости от аргумента axis) и что-то с ними делает.
>>> import pandas as pd >>> import numpy as np >>> df = pd.DataFrame(np.random.randn(4, 3), columns=list('abc')) >>> df # a b c # 0 0.532464 2.241091 0.688753 # 1 -0.586088 0.111004 1.007061 # 2 0.205406 1.580366 0.609771 # 3 0.968165 1.196149 -0.727974 # определяем функцию, которая принимает `Series` >>> fn = lambda ser: ser.max() - ser.min() >>> df.apply(fn) # a 1.554252 # b 2.130087 # c 1.735035 # dtype: float64
При этом метод DataFrame.apply() может также изменять каждый элемент, если передаваемая функция - является универсальной функцией numpy:
>>> df = pd.DataFrame([[4, 9]] * 3, columns=['A', 'B']) >>> df # A B # 0 4 9 # 1 4 9 # 2 4 9 # например, `np.sqrt()` >>> df.apply(np.sqrt) # A B # 0 2.0 3.0 # 1 2.0 3.0 # 2 2.0 3.0
Метод DataFrame.map() тупо применяет пользовательскую функцию к DataFrame поэлементно. Другими словами, пользовательская функция принимает в качестве обязательного аргумента только один элемент DataFrame (перебирает DataFrame поэлементно) и что то с ним делает.
>>> df = pd.DataFrame([[1, 2.12], [3.356, 4.567]]) >>> df # 0 1 # 0 1.000 2.120 # 1 3.356 4.567 >>> df.map(lambda x: len(str(x))) # 0 1 # 0 3 4 # 1 5 5
Если пользовательская функция должна одновременно работать со всеми значениями строки или столбца, то однозначно используем метод DataFrame.apply(). Например: lambda ser: ser.max() - ser.min()
Если функция должна быть применена к каждому элементу:
DataFrame, то используем метод DataFrame.apply();DataFrame, то используем метод DataFrame.map() (ранее назывался DataFrame.applymap())DataFrame.apply(func, axis=0, raw=False, result_type=None, args=(), by_row='compat', **kwargs):Метод DataFrame.apply() применяет пользовательскую функцию вдоль оси axis к каждому столбцу или строке.
Объекты, передаваемые в пользовательскую функцию, являются объектами Series, индекс которых является либо индексом DataFrame (axis=0), либо столбцами DataFrame (axis=1). По умолчанию (result_type=None) окончательный тип возвращаемого значения выводится из типа возвращаемого значения применяемой функции. В противном случае это зависит от аргумента result_type.
Принимаемые аргументы:
func: пользовательская функция, применяемая к каждому столбцу или строке.axis: ось, вдоль которой применяется функция:'index': применить функцию к каждому столбцу.'columns': применить функцию к каждой строке.raw: определяет, передается ли строка или столбец как объект Series или ndarray:False : передает в функцию каждую строку или столбец в виде серии.True : переданная функция получит объекты numpy.ndarray. Если применяется функция NumPy, то это позволит добиться гораздо большей производительности.result_type: действует только тогда, когда axis=1 (столбцы). Поведение по умолчанию (None) зависит от возвращаемого значения прикладной функции: результаты, подобные списку, будут возвращены в виде их Series. Однако, если DataFrame.apply() возвращает серию, они расширяются до столбцов.'expand' : результаты, подобные списку, будут преобразованы в столбцы.'reduce' : по возможности возвращает ряд, а не расширяет результаты, подобные списку. Это противоположно 'expand'.'broadcast': результаты будут транслироваться в исходную форму фрейма данных, исходный индекс и столбцы будут сохранены.args: кортеж с позиционными аргументами для передачи в func в дополнение к Series.by_row: имеет эффект только тогда, аргумент func не является строкой, а является списком или словарем вызываемых объектов. Если by_row='compat', то, по возможности, pandas переведет функцию в методы (например, Series().apply(np.sum) будет переведен в Series().sum()). Если это не сработает, попробует снова вызвать DataFrame.apply() с помощью by_row=True, а если это не удастся, то снова вызовет DataFrame.apply() с помощью by_row=False (обратная совместимость). Если значение равно False, то функции будут переданы всей серии сразу.**kwargs: дополнительные ключевые аргументы для передачи в func.DataFrame.apply():Пользовательские функции, изменяющие переданный объект, могут приводить к непредвиденному поведению или ошибкам и не поддерживаются.
>>> import pandas as pd >>> import numpy as np >>> df = pd.DataFrame([[4, 9]] * 3, columns=['A', 'B']) >>> df # A B # 0 4 9 # 1 4 9 # 2 4 9
Используем универсальную функцию numpy:
>>> df.apply(np.sqrt) # A B # 0 2.0 3.0 # 1 2.0 3.0 # 2 2.0 3.0
Использование групповой функции по осям axis:
# суммирование значений столбца >>> df.apply(np.sum, axis=0) # A 12 # B 27 # dtype: int64 # суммирование значений строк столбцов >>> df.apply(np.sum, axis=1) # 0 13 # 1 13 # 2 13 # dtype: int64
Если пользовательская функция возвращает список, то это приведет к Series
>>> df.apply(lambda x: [1, 2], axis=1) # 0 [1, 2] # 1 [1, 2] # 2 [1, 2] # dtype: object
Передача result_type='expand' расширит результаты в виде списка до столбцов DataFrame
>>> df.apply(lambda x: [1, 2], axis=1, result_type='expand') # 0 1 # 0 1 2 # 1 1 2 # 2 1 2
Внутри пользовательской функции можно возвращать Series, что будет аналогично передаче result_type='expand'. Результирующие имена столбцов будут индексом ряда.
>>> df.apply(lambda x: pd.Series([1, 2], index=['foo', 'bar']), axis=1) # foo bar # 0 1 2 # 1 1 2 # 2 1 2
Передача result_type='broadcast' обеспечит один и тот же результат, независимо от того, будет ли функция возвращать список или скаляр, и транслировать его вдоль оси. Результирующие имена столбцов будут исходными.
>>> df.apply(lambda x: [1, 2], axis=1, result_type='broadcast') # A B # 0 1 2 # 1 1 2 # 2 1 2
Series.apply(func, convert_dtype=_NoDefault.no_default, args=(), *, by_row='compat', **kwargs):Метод Series.apply() вызывает пользовательскую функцию func для значений Series. Если func возвращает объект Series, то результатом будет DataFrame.
Принимаемые аргументы:
func: функция может быть функцией |NumPy|, которая применяется ко всему Series или функция Python, которая работает только с одиночными значениями.convert_dtype: находит лучший dtype для результатов поэлементных функций. Если False, то оставляет dtype=object. Аргумент устарел, начиная с версии 2.1.0. Если необходимо применить convert_dtype=False, то нужно выполнить ser.astype(object).apply().args: кортеж с позиционными аргументами для передачи в func после значений Series.by_row: имеет эффект только тогда, аргумент func не является строкой, а является списком или словарем вызываемых объектов. Если by_row='compat', то, по возможности, pandas переведет функцию в методы (например, Series().apply(np.sum) будет переведен в Series().sum()). Если это не сработает, попробует снова вызвать Series.apply() с помощью by_row=True, а если это не удастся, то снова вызовет Series.apply() с помощью by_row=False (обратная совместимость). Если значение равно False, то функции будут переданы всей серии сразу.**kwargs: дополнительные ключевые аргументы для передачи в func.Series.apply():Пользовательские функции, изменяющие переданный объект, могут приводить к непредвиденному поведению или ошибкам и не поддерживаются.
Создадим серию с летними температурами для городов.
>>> import pandas as pd >>> s = pd.Series([20, 21, 12], ... index=['London', 'New York', 'Helsinki']) >>> s # London 20 # New York 21 # Helsinki 12 # dtype: int64
Возведем значения в квадрат, определив пользовательскую функцию и передав ее в качестве аргумента.
>>> def square(x): ... return x ** 2 >>> s.apply(square) # London 400 # New York 441 # Helsinki 144 # dtype: int64
Возвести значения в квадрат, можно анонимной функцией lambda.
>>> s.apply(lambda x: x ** 2) # London 400 # New York 441 # Helsinki 144 # dtype: int64
Определим пользовательскую функцию, которой требуются дополнительные позиционные аргументы, и передадим эти дополнительные аргументы с помощью ключевого аргумента args.
>>> def subtract_custom_value(x, custom_value): ... return x - custom_value >>> s.apply(subtract_custom_value, args=(5,)) # London 15 # New York 16 # Helsinki 7 # dtype: int64
Определим пользовательскую функцию, которая принимает ключевые аргументы и передает эти аргументы для применения.
>>> def add_custom_values(x, **kwargs): ... for month in kwargs: ... x += kwargs[month] ... return x >>> s.apply(add_custom_values, june=30, july=20, august=25) # London 95 # New York 96 # Helsinki 87 # dtype: int64
Используем функцию из библиотеки |NumPy|.
>>> s.apply(np.log) # London 2.995732 # New York 3.044522 # Helsinki 2.484907 # dtype: float64
DataFrameGroupBy.apply(func, *args, **kwargs):Метод DataFrameGroupBy.apply() применяет функцию func по группам и объединяет результаты.
Функция, передаваемая в DataFrameGroupBy.apply(), должна принимать в качестве первого аргумента DataFrame и возвращать DataFrame, Series или скаляр. Затем DataFrameGroupBy.apply() позаботится об объединении результатов в один DataFrame или Series. Кроме того, вызываемый объект может принимать позиционные *args и ключевые **kwargs аргументы.
Таким образом, apply является очень гибким методом группировки.
Хотя DataFrameGroupBy.apply() - очень гибкий метод, недостатком является то, что его использование может быть немного медленнее, чем использование более конкретных методов, таких как .agg() или .transform(). Pandas предлагает широкий спектр методов, которые будут намного быстрее, чем использование .apply().
DataFrameGroupBy.apply():>>> import pandas as pd df = pd.DataFrame({'A': 'a a b'.split(), 'B': [1,2,3], 'C': [4,6,5]}) >>> g = df.groupby('A', group_keys=False)
Пример 1. Функция принимает DataFrame в качестве аргумента и возвращает DataFrame. Метод apply объединяет результаты для каждой группы в новый DataFrame:
>>> g[['B', 'C']].apply(lambda x: x / x.sum()) # B C # 0 0.333333 0.4 # 1 0.666667 0.6 # 2 1.000000 1.0
Пример 2. Функция принимает DataFrame в качестве аргумента и возвращает Series. Метод apply объединяет результаты для каждой группы в новый DataFrame.
>>> g[['B', 'C']].apply(lambda x: x.astype(float).max() - x.min()) # B C # A # a 1.0 2.0 # b 0.0 0.0
Пример 3. Функция принимает DataFrame в качестве аргумента и возвращает скаляр. Метод apply объединяет результаты для каждой группы в Series, включая соответствующую установку индекса:
>>> g.apply(lambda x: x.C.max() - x.B.min()) # A # a 5 # b 2 # dtype: int64
DataFrame.map(func, na_action=None, **kwargs):Метод DataFrame.map() применяет пользовательскую функцию к DataFrame поэлементно. Этот метод применяет функцию func, которая принимает и возвращает скаляр для каждого элемента DataFrame.
Принимаемые аргументы:
func - функция Python, которая принимает и возвращает скаляр для каждого элемента DataFramena_action=None - если na_action='ignore', то распространяет значения NaN, не передавая их в func.**kwargs - дополнительные ключевые аргументы для передачи в func.DataFrame.map():>>> df = pd.DataFrame([[1, 2.12], [3.356, 4.567]]) >>> df # 0 1 # 0 1.000 2.120 # 1 3.356 4.567 >>> df.map(lambda x: len(str(x))) # 0 1 # 0 3 4 # 1 5 5
Как и в Series.map(), значения NA можно игнорировать:
>>> df_copy = df.copy() >>> df_copy.iloc[0, 0] = pd.NA >>> df_copy.map(lambda x: len(str(x)), na_action='ignore') # 0 1 # 0 NaN 4 # 1 5.0 5
Обратите внимание, что часто существует векторизованная версия функции, которая работает намного быстрее. Например, возведение каждого число в квадрат поэлементно.
# в этом случае лучше избегать `map`. >>> df.map(lambda x: x**2) # 0 1 # 0 1.000000 4.494400 # 1 11.262736 20.857489 # векторизованная версия функции >>> df ** 2 # 0 1 # 0 1.000000 4.494400 # 1 11.262736 20.857489
Series.map(arg, na_action=None):Метод Series.map() используется для замены каждого значения в Series другим значением, которое может быть получено из функции, словаря dict или Series.
Принимаемые аргументы:
arg - функция, словарь dict или Series.na_action=None - если na_action='ignore', то распространяет значения NaN, не передавая их в func.Внимание. Когда
argявляется словарем, то значения в последовательностях, которых нет в словаре (как ключей), преобразуются вNaN. НО, если словарь является подклассомdict, который определяет__missing__(предоставляет метод для значений по умолчанию), то используется это значение по умолчанию, а неNaN.
DataFrame.map():>>> s = pd.Series(['cat', 'dog', np.nan, 'rabbit']) >>> s # 0 cat # 1 dog # 2 NaN # 3 rabbit # dtype: object
Метод Series.map() принимает dict или Series. Значения, которые не найдены в словаре, преобразуются в NaN, если словарь не имеет значения по умолчанию (например, collections.defaultdict):
>>> s.map({'cat': 'kitten', 'dog': 'puppy'}) # 0 kitten # 1 puppy # 2 NaN # 3 NaN # dtype: object
Аргумент arg также принимает функцию:
>>> s.map('I am a {}'.format) # 0 I am a cat # 1 I am a dog # 2 I am a nan # 3 I am a rabbit # dtype: object
Чтобы избежать применения функции к отсутствующим значениям (и сохранить их как NaN), можно использовать na_action='ignore'.
>>> s.map('I am a {}'.format, na_action='ignore') # 0 I am a cat # 1 I am a dog # 2 NaN # 3 I am a rabbit # dtype: object