Что такое Pandas map, apply, applymap и pipe?

Введение

apply, applymap , map и pipe могут сбивать с толку, особенно если вы новичок в Pandas, поскольку все они кажутся довольно похожими и могут принимать функцию в качестве входных данных. Вот краткое сравнение различных методов.

Мы подробно рассмотрим каждый из них, используя следующие примеры данных.

# python version 3.9
# pandas version 1.4.1
import pandas as pd
df = pd.DataFrame({'name':['John Doe', 'Mary Re', 'Harley Me'],
                   'gender':[1,2,0],
                   'age':[80, 38, 12],
                   'height': [161.0, 173.5, 180.5],
                   'weight': [62.3, 55.7, 80.0]
                   })

  • gender : 0,1,2 относится к «неизвестно», «мужчине» и «женщине» соответственно.
  • height : in cm
  • weight : in Kg

Что такое Панды map()?

pandas.series.map сопоставляет значения Series в соответствии с функцией сопоставления ввода. Используется для замены каждого значения в серии другим значением, которое может быть получено из функции, словаря или серии.

Параметры

  • arg: сопоставление корреспонденции
  • na_action: {'Ничего', "игнорировать"}. По умолчанию Нет. Если «игнорировать», распространять значения NaN, не передавая их в соответствие сопоставления.

Возврат

  • Series

Ключевые моменты

  1. Применимо только к серии Pandas
  2. Поэлементная операция
  3. В основном используется для замены значений
  4. Параметр arg принимает сопоставление между старым значением и новым значением, которое может быть в форме (а) словаря, (б) серии и (в) функции.

Словарь

Цель состоит в том, чтобы заменить закодированный пол (0,1,2) их фактическим значением (неизвестно, мужчина, женщина).

Сначала мы определяем словарь отображения между закодированными значениями и фактическими значениями в следующей форме {previous_value_1: new_value_1, previous_value_2:new_value_2..}, затем мы применяем .map() к столбцу gender. .map() ищет ключ в словаре отображения, который соответствует кодифицированному роду, и заменяет его значением словаря.

gender_map = {0: 'Unknown', 1:'Male', 2:'Female'}
df['gender'] = df['gender'].map(gender_map)

Вывод будет Nan, если пара ключ-значение не найдена в словаре отображения. Сопоставление для {0: 'Unknown'} удалено, и вот как выглядит вывод.

gender_map = {1:'Male', 2:'Female'}
df['gender'] = df['gender'].map(gender_map)

Серии

Вместо использования словаря сопоставления мы используем серию сопоставлений. .map() ищет соответствующий индекс в ряду, который соответствует кодифицированному полу, и заменяет его значением в ряду.

index серии картирования содержит кодифицированный пол, а столбец gender содержит фактическое значение пола.

gender_map = {0: 'Unknown', 1:'Male', 2:'Female'}
s = pd.Series(gender_map) # mapping series
df['gender'] = df['gender'].map(s)

Вывод будет NaN, если сопоставление не может быть найдено в серии.

gender_map = {1:'Male', 2:'Female'}
s = pd.Series(gender_map) # mapping series
df['gender'] = df['gender'].map(s)

Функция

Мы также можем использовать функцию (или лямбду) в качестве параметра arg в .map(). Попробуем присвоить каждому человеку категорию age_group (взрослый или ребенок) с помощью лямбда-функции.

df['age_group'] = df['age'].map(lambda x: 'Adult' if x >= 21 else 'Child')

В качестве альтернативы мы можем сделать следующее.

def get_age_group(age):
    threshold = 21
    if age >= threshold:
        age_group = 'Adult'
    else:
        age_group = 'Child'
    return age_group
df['age_group'] = df['age'].map(get_age_group)

Обратите внимание, что возрастной порог был жестко запрограммирован в функции get_age_group, поскольку .map() не позволяет передавать аргументы в функцию.

Что такое Панды apply()?

.apply() применимо как к Pandas DataFrame, так и к Series. Применительно к кадрам данных .apply() может работать как по строкам, так и по столбцам.

Series.apply()

Вызов функции для значений серии. Это может быть ufunc (функция NumPy, которая применяется ко всей серии) или функция Python, которая работает только с отдельными значениями.

Параметры

  • func: функция
  • convert_dtype: логическое значение, по умолчанию True. Попробуйте найти лучший dtype для результатов поэлементной функции. Если False, оставьте как dtype=object. Обратите внимание, что dtype всегда сохраняется для некоторых dtypes массива расширений, таких как Categorical.
  • args: кортеж. Позиционные аргументы передаются в func после значения серии.
  • **kwargs: Дополнительные аргументы ключевого слова переданы в func.

Вернуть

  • Series or DataFrame
  • Если func возвращает объект Series, результатом будет DataFrame.

Ключевые моменты

  • Применимо к серии Pandas
  • Принимает функцию
  • Возможность передавать позиционные или ключевые аргументы в функцию
  • Может возвращать либо Series, либо DataFrame

Из нашего предыдущего примера мы видели, что .map() не позволяет передавать аргументы в функцию. .apply(), с другой стороны, позволяет передавать как позиционные, так и ключевые аргументы. Давайте параметризируем функцию, чтобы она принимала параметр thershold.

def get_age_group(age, threshold):
    if age >= int(threshold):
        age_group = 'Adult'
    else:
        age_group = 'Child'
    return age_group

Передайте threshold в качестве аргумента ключевого слова

# keyword argument
df['age_group'] = df['age'].apply(get_age_group, threshold = 21)

Передайте threhsold в качестве позиционного аргумента

# positional argument
df['age_group'] = df['age'].apply(get_age_group, args = (21,))

Несколько аргументов

.apply() также может принимать несколько позиционных или ключевых аргументов. Давайте разделим age на 3 age_group (ребенок, взрослый и старший) на основе нижнего и верхнего возрастного порога.

def get_age_group(age, lower_threshold, upper_threshold):
    if age >= int(upper_threshold):
        age_group = 'Senior'
    elif age <= int(lower_threshold):
        age_group = 'Child'
    else:
        age_group = 'Adult'
    return age_group

Передайте lower_threshold и upper_threshold в качестве аргументов ключевого слова

df['age_group'] = df['age'].apply(get_age_group, lower_threshold = 20, upper_threshold = 65)

Передайте lower_threshold и upper_threshold в качестве позиционных аргументов

df['age_group'] = df['age'].apply(get_age_group, args = (20,65))

Применить функцию numpy

Помимо применения функции Python (или Lamdba), .apply() также позволяет использовать функцию numpy. Например, мы можем применить numpy .ceil(), чтобы округлить height каждого человека до ближайшего целого числа.

df['height'] = df['height'].apply(np.ceil)

Вернуть серию

.apply() возвращает серию, если функция возвращает одно значение. Давайте напишем функцию для поиска фамилии человека.

def get_last_name(x):
    return x.split(' ')[-1]
type(df['name'].apply(get_last_name))
>> pandas.core.series.Series

Возврат фрейма данных

.apply() возвращает DataFrame, когда функция возвращает серию.

def get_last_name(x):
    return pd.Series(x.split(' ')[-1]) # function returns a Series
type(df['name'].apply(get_last_name))
>> pandas.core.frame.DataFrame

DataFrame.apply()

Применить функцию вдоль оси DataFrame.

Параметры

  • func: Функция для применения к каждому столбцу или строке
  • axis: Ось, вдоль которой применяется функция. axis=0 - применить функцию к каждому столбцу. axis=1 - применить функцию к каждой строке.
  • raw: определяет, передается ли строка или столбец как объект Series или ndarray: False — каждая строка или столбец передается функции как Series. True — передает объект ndarray в функцию.
  • result_type: они действуют, только когда axis=1 (столбцы):
    - 'expand': результаты, подобные списку, будут преобразованы в столбцы.
    - 'reduce': возвращает ряд, если это возможно, а не расширяется, как список. Результаты. Это противоположно «расширению».
    — «трансляция»: результаты будут транслироваться в исходную форму DataFrame, исходный индекс и столбцы будут сохранены.
  • args: кортеж. Позиционные аргументы передаются в func после значения серии.
  • **kwargs: Дополнительные аргументы ключевого слова, переданные в func.

Возврат

  • Series or DataFrame

Ключевые моменты

  • Функция может применяться либо по столбцам (axis = 0), либо по строкам (axis = 1).
  • Возможность передавать данные в виде массива Series или numpy для работы
  • Возможность передавать позиционные или ключевые аргументы в функцию

Применить функцию numpy по столбцам

Округлите рост и вес до ближайшего целого числа. Функция применяется по столбцам, как определено axis = 0. При использовании по столбцам pd.DataFrame.apply() можно применять сразу к нескольким столбцам.

df[['height', 'weight']].apply(np.round, axis = 0)

Применить лямбда-функцию построчно

Разделите имя на имя и фамилию, применив функцию split по строкам, как определено axis = 1.

df.apply(lambda x: x['name'].split(' '), axis = 1)

Мы можем разбить список на несколько столбцов, по одному элементу в каждом столбце, определив параметр result_type как expand.

df.apply(lambda x: x['name'].split(' '), axis = 1, result_type = 'expand')

Применить функцию построчно

Давайте найдем индекс массы тела (ИМТ) для каждого человека. ИМТ определяется как вес в килограммах, деленный на квадрат роста в метрах. Мы создаем UDF для расчета ИМТ и применяем UDF построчно к DataFrame. При использовании построчно pd.DataFrame.apply() может использовать значения из разных столбцов, выбирая столбцы на основе имен столбцов.

def calculate_bmi(x):
    
    bmi = x['weight'] / (x['height']/100)**2

    return bmi

df.apply(calculate_bmi, axis = 1)

Что такое Панды applymap()?

Применить функцию к DataFrame поэлементно. Этот метод применяет функцию, которая принимает и возвращает скаляр для каждого элемента DataFrame.

Параметры

  • func: функция Python, возвращает одно значение из одного значения.
  • na_action: {'Нет', 'игнорировать'}. По умолчанию Нет. Если «игнорировать», распространять значения NaN, не передавая их в соответствие сопоставления.
  • **kwargs: Дополнительные аргументы ключевого слова переданы в func.

Вернуть

  • DataFrame

Ключевые моменты

  • Применимо к Pandas DataFrame
  • Принимает функцию
  • Возможность передавать аргументы ключевого слова в функцию
  • Поэлементная операция

В следующем примере у нас есть два столбца числовых значений, над которыми мы выполнили простую арифметику.

def some_math(x, multiplier, add):
    return x * multiplier + add
df = pd.DataFrame({'A':[1,2,3], 'B':[10,20,30]})
df.applymap(some_math, multiplier = 2, add = 1)

.applymap() принимает каждое из значений в исходном DataFrame, передает его в функцию some_math как x , выполняет операции и возвращает одно значение. .applymap() также принимает аргументы ключевого слова, но не позиционные аргументы.

Что такое Панды .pipe()?

Применить цепные функции, которые ожидают Series или DataFrames.

Параметры

  • func: Функция для применения к серии/фрейму данных.
  • args: позиционные аргументы переданы в func
  • kwargs: аргументы ключевого слова переданы в func

Возврат

  • object: возвращаемый тип func

Ключевые моменты

  • Применимо к серии Pandas и DataFrame
  • Принимает функцию
  • Возможность передавать параметры для работы в качестве позиционных или ключевых аргументов.
  • Возвращает тот же объект, что и func

Как работает pipe?

.pipe() обычно используется для объединения нескольких функций. Например, у нас есть 3 функции, которые работают с DataFrame: f1, f2 и f3, каждая из которых требует DataFrame в качестве входных данных и возвращает преобразованный DataFrame.

def f1(df, arg1):
	# do something
	return # a dataframe
def f2(df, arg2):
	# do something
	return # a dataframe
def f3(df, arg3):
	# do something
	return # a dataframe
df = pd.DataFrame(..) # some dataframe

Без использования .pipe() мы бы применяли функции вложенным образом, что может выглядеть довольно нечитаемо, если есть несколько функций. Чтобы проследить последовательность выполнения функции, придется читать «изнутри». Сначала выполняется самая внутренняя функция f3, затем f2, затем f1.

f1(f2(f3(df, arg3 = arg3), arg2 = arg2), arg1 = arg1)

.pipe() позволяет избежать вложенности и позволяет связывать функции с помощью записи через точку (.), что делает его более читабельным. .pipe() также позволяет передавать как позиционные, так и ключевые аргументы и предполагает, что первый аргумент функции относится к входному DataFrame/Series.

df.pipe(f3, arg3 = arg3).pipe(f2, arg2 = arg2).pipe(f1, arg1 = arg1)

Следование последовательности выполнения функций, связанных вместе с .pipe(), более интуитивно понятно; Мы просто читаем его слева направо.

apply, map и applymap могут возвращать либо Series, либо DataFrame, либо и то, и другое. Однако pipe может возвращать любые объекты, не обязательно Series или DataFrame. Давайте взглянем на несколько примеров, используя тот же образец набора данных.

Функция ниже возвращает значение с плавающей запятой.

def find_average_weight(df):
    return df['weight'].mean()
df.pipe(find_average_weight)
>> 66.0

Функция ниже возвращает строку.

def report_average_weight(df):
    avg_weight = df['weight'].mean()
    return f'The average weight is {avg_weight}'
df.pipe(report_average_weight)
>> 'The average weight is 66.0'

Краткое содержание

В этой статье мы рассмотрели разницу между map, apply и applymap, pipe и как использовать каждый из этих методов для преобразования наших данных. Хотя мы не вдавались в подробности скорости выполнения map, apply и applymap , обратите внимание, что эти методы являются замаскированными циклами и должны использоваться только в том случае, если нет эквивалентных векторизованных операций. Как правило, векторизованные операции выполняются быстрее, чем циклы, и разница во времени выполнения становится более существенной по мере увеличения размера набора данных.