Pandas: как применить функцию с несколькими входами столбцов и где условие

У меня есть панды dataframe. Я хочу создать новую переменную (столбец) на основе нескольких входных данных столбца, где индекс года больше определенного значения.

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

В идеале функция должна разделить вычисление на промежуточные временные значения (не сохраняемые в df) и на несколько строк, чтобы их было легче читать. Например, можно определить: Share = (df['B']+df['C']) / (df['B']+df['C']+df['D']), а затем X = A + Share * E.

Я использовал apply ранее, чтобы применить функцию к dataframe, но в этом примере использовалась только одна переменная в качестве входных данных и не было предложения where, и я не знаю, как расширить пример.

Как мне просто сгенерировать приведенный ниже расчет X на основе A, B, C, D и E, где year >= 2020?

import numpy as np
import pandas as pd

np.random.seed(2981)

df = pd.DataFrame({
    'year' : [2018, 2019, 2020, 2021,2018, 2019, 2020, 2021,2018, 2019, 2020, 2021],
    'id'   : ['ABC','ABC','ABC','ABC','DEF','DEF','DEF','DEF','GHI','GHI','GHI','GHI'],
    'A': np.random.choice(range(100),12),
    'B': np.random.choice(range(100),12),
    'C': np.random.choice(range(100),12),
    'D': np.random.choice(range(100),12),
    'E': np.random.choice(range(100),12),
})
df = df.set_index('year')

df['X'] = np.where( df.index >= 2020,  df['A'] + (df['B']+df['C']) / (df['B']+df['C']+df['D']) * df['E'] , np.nan )
np.where выглядит нормально, я не понимаю, что вы хотите упростить.
Ynjxsjmh 06.05.2022 18:49

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

user7864386 06.05.2022 19:00

@энке. Спасибо. Приведенный выше расчет - это моя фактическая функция. Я хочу создать новую переменную X на основе приведенного выше расчета. Допустим, вычисление представляет собой A + Intermediate1 * Intermediate2, где Intermediate1 — это функция 5 переменных в df, а Intermediate2 — это функция 2 переменных в df. Я пытался избежать создания временных переменных Intermediate1 и Intermediate2, так как они не нужны. Но, может быть, это лучший способ сделать это?

brb 06.05.2022 19:04

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

user7864386 06.05.2022 19:10
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, вы должны использовать apply только в случае необходимости. Векторизованные функции будут намного быстрее, и то, как вы это написали сейчас в операторе np.where, использует их. Если вы действительно хотите сделать свой код более читабельным (за счет (вероятно, небольшого) времени и памяти), вы можете создать промежуточный столбец, а затем использовать его в операторе np.where.

df["Share"] = ( df.B + df.C ) / ( df.B + df.C + df.D )
df["X"] = ( df.A + df.Share * df.E ).where( df.index >= 2020 )

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

def my_func( year,a,b,c,d,e ):
    #This function can be longer and do more things
    return np.nan if year < 2020 else a + ( ( (b + c) / (b + c + d) ) * e )


df['X'] = df.apply( lambda x: my_func( x.name, x.A, x.B, x.C, x.D, x.E ), axis = 1 )

Обратите внимание, что для доступа к индексу строки при использовании приложения с axis = 1 вам необходимо использовать атрибут имени.

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

Ознакомьтесь с этот ответ для получения дополнительных примеров применения пользовательской функции.

Спасибо. Итак, для большого набора данных вы бы порекомендовали создать новые столбцы промежуточных значений, а затем удалить их позже? Будет ли это быстрее/эффективнее?

brb 06.05.2022 19:00

В общем, вам следует избегать использования применения (которое будет перебирать строки), если оно вам не нужно (векторизованные функции НАМНОГО быстрее). Итак, если вы можете заменить все выражение встроенными векторизованными функциями, это идеально. Если вам действительно нужно использовать apply, я бы попытался сделать функцию, которую вы применяете, максимально легкой и использовать векторизованные функции для промежуточных шагов, таких как df.B+df.C.

Leo 06.05.2022 19:04

Спасибо. Итак, если набор данных большой, а расчеты достаточно простые, вы бы порекомендовали мне сгенерировать промежуточные переменные, например, middle1 = (b+c)/(b+c+d) и т. д., и сделать это таким образом, а не применять функцию с помощью применения? Я думаю, что я спрашиваю, хочу ли я сделать это наилучшей практикой, поэтому я должен отказаться от применения или есть способ определить функцию и использовать ее векторизованным способом?

brb 06.05.2022 19:12

Вы можете изучить другие способы векторизации пользовательских функций (stackoverflow.com/questions/52673285/…), но в целом откажитесь от применения везде, где это возможно, и используйте векторизацию pandas и numpy (выполняя операции непосредственно со столбцами, используя np.where и т. д.).

Leo 06.05.2022 19:22

Спасибо. Я проверю этот ответ, чтобы лучше понять. Благодарим вас за помощь и идеи.

brb 06.05.2022 19:23

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