#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 )
Я понятия не имею, будет ли это полезно еще кому-нибудь, но вот, пожалуйста.