Я работаю над фреймом данных, это мой вклад:
df=pd.DataFrame({'A':[5,3,2],'B':[2,1,1],
'a':[2,3,1],'b':[1,3,8]})
Ожидаемый результат:
pd.DataFrame({'A':[5,3,2],'B':[2,1,1],
'a':[2,3,1],'b':[1,3,8],
'A*a':[10,9,2],'B*b':[2,3,8],
'Max':[10,9,8]})
Моя цель — умножить столбцы отдельно, например, A*a
, B*b
. Последняя интересующая меня переменная — «Max», которая возвращает максимальное значение Aa и Bb.
Обратите внимание, что у меня больше двух пар, мои реальные данные содержат десятки столбцов, поэтому мне нужно сделать это в масштабе. В идеале я хочу структурировать функцию и взять карту ({A:a,B:b,C:c...}) в качестве входных данных, запустить эту функцию в каждой строке фрейма данных и вернуть значение Max.
Я попробовал это, но не получилось:
dic = {'A':'a','B':'b'}
def score(row):
max_score=0
for i,j in dic.items():
if row[i]*row[j]>max_score:
max_score=row[i]*row[j]
return max_score
df.apply(score)
Я отредактировал свой вопрос, он должен быть заранее определен.
Ты можешь сделать:
d = {"A":"a","B":"b"}
out = pd.DataFrame(
{k+"*"+v: df[k].mul(df[v]) for k, v in d.items()}
)
out["Max"] = out.max(axis=1)
out = pd.concat([df, out], axis=1)
print(out)
A B a b A*a B*b Max
0 5 2 2 1 10 2 10
1 3 1 3 3 9 3 9
2 2 1 1 8 2 8 8
Я предлагаю два варианта, удачно названных «вариант1» и «вариант2».
Вариант 1, возможно, более читаем. Вы проходите все сопряжения и создаете новые столбцы. Затем вы создаете столбец max и возвращаете новый df.
def option1(df: pd.DataFrame, d:dict) -> pd.DataFrame:
new_cols = []
for i,j in d.items():
new_col = f'{i}{j}'
df[new_col] = df[i]*df[j]
new_cols.append(new_col)
df['max'] = df[new_cols].to_numpy().max(axis=1)
return df
Вариант 2 немного больше полагается на numpy, поэтому, если ваш набор данных большой, он может быть лучше. Он вычисляет все умножения за один раз и сразу после этого делает максимальное.
def option2(df: pd.DataFrame, d:dict) -> pd.DataFrame:
i1 = list(d)
i2 = list(d.values())
new_data = df[i1].to_numpy()*df[i2].to_numpy()
max_col = new_data.max(axis=1)
new_cols = [f'{i}{j}' for i,j in zip(i1,i2)]
df[new_cols] = new_data
df['max'] = max_col
return df
Executing both functions 1000 times here are the timings:
option1: 0.786 s
option2: 0.678 s
Кажется, что вариант 2 работает быстрее даже с этим небольшим набором данных.
Одним из вариантов может быть выполнение векторного умножения:
dic = {'A':'a','B':'b'}
# multiply keys/values
tmp = df[list(dic)].mul(df[dic.values()].values)
# update column names
tmp.columns += '*' + pd.Index(dic.values())
# compute max
tmp['max'] = tmp.max(axis=1)
out = df.join(tmp)
Выход:
A B a b A*a B*b max
0 5 2 2 1 10 2 10
1 3 1 3 3 9 3 9
2 2 1 1 8 2 8 8
Другой вариант — переименовать и groupby.prod:
dic = {'A':'a','B':'b'}
tmp = (df.rename(columns=dic).add_suffix('_prod')
.groupby(axis=1, level=0).prod()
)
out = df.join(tmp.assign(max=tmp.max(axis=1)))
Для последних версий панд:
dic = {'A':'a','B':'b'}
tmp = (df.rename(columns=dic).add_suffix('_prod')
.T.groupby(level=0).prod().T
)
out = df.join(tmp.assign(max=tmp.max(axis=1)))
Выход:
A B a b a_prod b_prod max
0 5 2 2 1 10 2 10
1 3 1 3 3 9 3 9
2 2 1 1 8 2 8 8
Если пары всегда в верхнем и нижнем регистре, это должно работать:
df.join(
pd.concat(
[df[c].mul(df[c.lower()]).rename('{}*{}'.format(c,c.lower())) for c in df.columns if c.isupper()],axis=1)
.assign(
Max = lambda x: x.max(axis=1))
Выход:
A B a b A*a B*b Max
0 5 2 2 1 10 2 10
1 3 1 3 3 9 3 9
2 2 1 1 8 2 8 8
карта предопределена? или динамически создается?