Панды, применяющиеся с многоиндексированными столбцами

#python #pandas #dataframe #numpy

Вопрос:

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

 import pandas as pd import numpy as np  # Simple maths functions --- def add(scalars): return sum(scalars) #Adds every element in scalars def sub(scalars): return (scalars[0] - sum(scalars[1:])) #First scalar value subtracted by the rest of the scalar list def mul(scalars): return np.prod(scalars) #Multiplies every element in scalars def divi(scalars): return (scalars[0] / np.prod(scalars[1:])) #First scalar value divided by the rest product of the scalar list   
 # Create df ---  operatorList = [add, sub, mul, divi] # List of our maths functions names = ['add', 'sub', 'mul', 'divi'] # List of the names of our maths functions size = 4  tups = [('scalars', 'add', 'sc0'), ('scalars', 'add', 'sc1'), ('scalars', 'add', 'sc2'), ('scalars', 'sub', 'sc0'), ('scalars', 'sub', 'sc1'), ('scalars', 'mul', 'sc0'), ('scalars', 'mul', 'sc1'), ('scalars', 'mul', 'sc2'), ('scalars', 'divi', 'sc0'), ('scalars', 'divi', 'sc1'), ('operator', '', '')] df = pd.DataFrame(columns=pd.MultiIndex.from_tuples(tups))  df['operatorIndex'] = np.random.randint(0, len(names), size) df['operator'] = df['operatorIndex'].apply(lambda x: str(names[x]))  groupSize = 2 df['ID']=np.divmod(np.arange(len(df)),groupSize)[0] 1 df.set_index('ID', inplace=True) df.sort_index(inplace=True)  for name in names:  df.loc[(df['operator'] == name), ('scalars', name, df.columns.levels[2])] = np.random.randint(0, 10)  
 gt; df   scalars operator operatorIndex  add sub mul divi   sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1  ID  1 4 4 4 NaN NaN NaN NaN NaN NaN NaN add 0 1 NaN NaN NaN 7 7 NaN NaN NaN NaN NaN sub 1 2 NaN NaN NaN NaN NaN 3 3 3 NaN NaN mul 2 2 NaN NaN NaN 7 7 NaN NaN NaN NaN NaN sub 1  

Как я могу создать новый столбец, Evaluation который является результатом правильной математической функции для каждого столбца? Столбец operator указывает каждой строке, какую функцию необходимо применить.

Примерная цель df будет выглядеть так:

 gt; df   scalars operator operatorIndex Evaluation  add sub mul divi   sc0 sc1 sc2 sc0 sc1 sc0 sc1 sc2 sc0 sc1  ID  1 4 5 6 NaN NaN NaN NaN NaN NaN NaN add 0 15 1 NaN NaN NaN 7 2 NaN NaN NaN NaN NaN sub 1 5 2 NaN NaN NaN NaN NaN 1 2 3 NaN NaN mul 2 6 2 NaN NaN NaN 5 9 NaN NaN NaN NaN NaN sub 1 -4  

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

1. Вы задаете два вопроса в одном — пожалуйста, задайте два отдельных вопроса SO на каждый вопрос, который у вас есть. 🙂

Ответ №1:

Вот так это сработает:

 df['Evaluation'] = df['operatorIndex'].apply(lambda operatorIndex: operatorList[operatorIndex](df['scalars'][names[operatorIndex]].min()))  

Хотя это будет работать только в том случае, если у каждого оператора есть только одна запись — если операция содержит несколько строк в df, это приведет к странным результатам.

Вы можете исправить это, сохранив глобальный счетчик, который обновляется для каждой строки, и используя iloc для доступа к строке скаляров по индексу:

 rowCounter = -1 def func(operatorIndex):  global rowCounter  rowCounter  = 1  return operatorList[operatorIndex](df['scalars'][names[operatorIndex]].iloc[rowCounter])  df['Evaluation'] = df['operatorIndex'].apply(func)  

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

1. На каждого оператора будет много строк, поэтому я не могу использовать первый вариант. Я также не очень заинтересован в использовании глобального счетчика. Однако я ценю ваши усилия. Хотя я пытаюсь приспособить твою первую попытку к чему-то. У меня есть предчувствие, что apply() это связано, но у меня проблемы operatorIndex с получением одновременно и скаляров, и скаляров. Такое чувство, что мне почти пришлось бы использовать apply() внутренний другой apply()

2. Я адаптировал твой ответ. Я думаю, что теперь он полностью функционирует. Дайте мне знать, выдержит ли он проверку. Спасибо за вашу помощь 🙂

Ответ №2:

Удалось адаптировать ответ от @user17242583

 df['Evaluation'] = df.apply(lambda row: operatorList[row['operatorIndex'].values[0]](row['scalars'][names[row['operatorIndex'].values[0]]].to_numpy()) , axis=1 )  

Я понятия не имею, будет ли это полезно еще кому-нибудь, но вот, пожалуйста.