У меня есть панды 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 )
Вы должны использовать векторизованные методы везде, где это возможно, и то, что у вас уже есть, отлично подходит для работы, которую вы описываете. В зависимости от количества строк apply
может работать намного медленнее. Если вы опишите свою фактическую функцию, мы могли бы помочь вам написать ее в векторной форме.
@энке. Спасибо. Приведенный выше расчет - это моя фактическая функция. Я хочу создать новую переменную X на основе приведенного выше расчета. Допустим, вычисление представляет собой A + Intermediate1 * Intermediate2, где Intermediate1 — это функция 5 переменных в df, а Intermediate2 — это функция 2 переменных в df. Я пытался избежать создания временных переменных Intermediate1 и Intermediate2, так как они не нужны. Но, может быть, это лучший способ сделать это?
@brb вам не нужно его никуда назначать. Вы можете вычислить две промежуточные переменные, сохранить их в памяти и назначить, когда вам нужно.
Во-первых, вы должны использовать 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
вам необходимо использовать атрибут имени.
Кроме того, поскольку применение функции является относительно медленным, возможно, стоит создать столбцы, которые позаботятся о некоторых промежуточных шагах (например, суммировании нескольких столбцов и т. д.), чтобы это не нужно было делать на каждой итерации.
Ознакомьтесь с этот ответ для получения дополнительных примеров применения пользовательской функции.
Спасибо. Итак, для большого набора данных вы бы порекомендовали создать новые столбцы промежуточных значений, а затем удалить их позже? Будет ли это быстрее/эффективнее?
В общем, вам следует избегать использования применения (которое будет перебирать строки), если оно вам не нужно (векторизованные функции НАМНОГО быстрее). Итак, если вы можете заменить все выражение встроенными векторизованными функциями, это идеально. Если вам действительно нужно использовать apply, я бы попытался сделать функцию, которую вы применяете, максимально легкой и использовать векторизованные функции для промежуточных шагов, таких как df.B+df.C.
Спасибо. Итак, если набор данных большой, а расчеты достаточно простые, вы бы порекомендовали мне сгенерировать промежуточные переменные, например, middle1 = (b+c)/(b+c+d) и т. д., и сделать это таким образом, а не применять функцию с помощью применения? Я думаю, что я спрашиваю, хочу ли я сделать это наилучшей практикой, поэтому я должен отказаться от применения или есть способ определить функцию и использовать ее векторизованным способом?
Вы можете изучить другие способы векторизации пользовательских функций (stackoverflow.com/questions/52673285/…), но в целом откажитесь от применения везде, где это возможно, и используйте векторизацию pandas и numpy (выполняя операции непосредственно со столбцами, используя np.where и т. д.).
Спасибо. Я проверю этот ответ, чтобы лучше понять. Благодарим вас за помощь и идеи.
np.where
выглядит нормально, я не понимаю, что вы хотите упростить.