Панды питона умножают фрейм данных на веса, которые варьируются в зависимости от категории в векторизованном виде
Моя проблема очень похожа на изложенную здесь
За исключением этого мой основной фрейм данных имеет столбец категории, как и мои веса:
df
Out[3]:
Symbol var_1 var_2 var_3 var_4 Category
Index
1903 0.000443 0.006928 0.000000 0.012375 A
1904 -0.000690 -0.007873 0.000171 0.014824 A
1905 -0.001354 0.001545 0.000007 -0.008195 C
1906 -0.001578 0.008796 -0.000164 0.015955 D
1907 -0.001578 0.008796 -0.000164 0.015955 A
1909 -0.001354 0.001545 0.000007 -0.008195 B
wgt_df
Out[4]:
Category var_1_wgt var_2_wgt var_3_wgt var_4_wgt
0 A 0.182022 0.182022 0.131243 0.182022
1 B 0.534814 0.534814 0.534814 0.534814
2 C 0.131243 0.534814 0.131243 0.182022
3 D 0.182022 0.151921 0.151921 0.131243
Я пытаюсь применить правильные веса для каждой категории, чтобы создать новый столбец df['new_var'], который является взвешенной суммой. В случае без категории, я могу преобразовать веса в массив Numpy и использовать .dot()
метод, который кажется очень быстрым. Однако я не вижу, как это сделать с моей проблемой: если я использую groupby()
на основном фрейме данных, df, я, безусловно, должен каким-то образом сделать то же самое с моим фреймом данных, wgt_df.
На самом деле, df содержит несколько миллионов строк, и мне нужно многократно повторять это вычисление, поэтому я стремлюсь найти векторизованное решение; Я мог бы иначе df.groupby('Category')
создайте набор данных для фреймов, ключом которых является категория, например wgts_dict['A'] = wgts_df[wgts_df.Category == 'A']
и применить мою точечную логику через lambda x
, хотя я также не уверен, как это сделать, так как мне нужно было бы явно указать, какой элемент группы обрабатывался в данный момент, чтобы вытащить правильный фрагмент wgts_df
,
2 ответа
Вы можете сделать групповой (выбрать по категории), а затем сделать dot()
или вы можете сделать dot()
а затем выберите по категории. Последний быстрее и проще в пандах. Обратите внимание, что данные, которые я использовал, соответствуют именам столбцов в данных и фреймах весов.
Код для dot()
и затем выберите:
df['dot'] = df[df_wgt.columns].dot(df_wgt.T).lookup(df.index, df.Category)
Шаги выполнены...
Выберите столбцы для использования с
df[df_wgt.columns]
При этом используются метки столбцов и порядок из массива данных. Это важно потому что
dot()
нужны данные в правильном порядке.Выполнение скалярного произведения против транспонированного массива данных с
.dot(df_wgt.T)
Транспонирование веса помещает их в правильную ориентацию для
.dot()
, Это делает расчет для всех весовых категорий для каждой строки данных. Это означает, что в этом случае мы делаем в четыре раза больше умножений, чем потребуется, но все же, вероятно, быстрее, чем группирование.Выберите нужный точечный продукт с помощью
.lookup(df.index, df.Category)
Используя
lookup()
мы можем собрать правильный результат для категории этой строки.
Код для выбора (групповой), а затем dot()
:
def dot(group):
category = group['Category'].iloc[0]
weights = df_wgt.loc[category].values
return pd.Series(
np.dot(group[df_wgt.columns].values, weights), index=group.index)
df['dot'] = df.groupby(['Category']).apply(dot) \
.reset_index().set_index('Index')[0]
Тестовый код:
import pandas as pd
from io import StringIO
df = pd.read_fwf(StringIO(u"""
Index var_1 var_2 var_3 var_4 Category
1903 0.000443 0.006928 0.000000 0.012375 A
1904 -0.000690 -0.007873 0.000171 0.014824 A
1905 -0.001354 0.001545 0.000007 -0.008195 C
1906 -0.001578 0.008796 -0.000164 0.015955 D
1907 -0.001578 0.008796 -0.000164 0.015955 A
1909 -0.001354 0.001545 0.000007 -0.008195 B"""),
header=1, skiprows=0).set_index(['Index'])
df_wgt = pd.read_fwf(StringIO(u"""
Category var_1 var_2 var_3 var_4
A 0.182022 0.182022 0.131243 0.182022
B 0.534814 0.534814 0.534814 0.534814
C 0.131243 0.534814 0.131243 0.182022
D 0.182022 0.151921 0.151921 0.131243"""),
header=1, skiprows=0).set_index(['Category'])
df['dot'] = df[df_wgt.columns].dot(df_wgt.T).lookup(df.index, df.Category)
print(df)
Результаты:
var_1 var_2 var_3 var_4 Category dot
Index
1903 0.000443 0.006928 0.000000 0.012375 A 0.003594
1904 -0.000690 -0.007873 0.000171 0.014824 A 0.001162
1905 -0.001354 0.001545 0.000007 -0.008195 C -0.000842
1906 -0.001578 0.008796 -0.000164 0.015955 D 0.003118
1907 -0.001578 0.008796 -0.000164 0.015955 A 0.004196
1909 -0.001354 0.001545 0.000007 -0.008195 B -0.004277
Настроить
print(df)
Out[655]:
var_1 var_2 var_3 var_4 Category
Symbol
1903 0.000443 0.006928 0.000000 0.012375 A
1904 -0.000690 -0.007873 0.000171 0.014824 A
1905 -0.001354 0.001545 0.000007 -0.008195 C
1906 -0.001578 0.008796 -0.000164 0.015955 D
1907 -0.001578 0.008796 -0.000164 0.015955 A
1909 -0.001354 0.001545 0.000007 -0.008195 B
print(w)
Out[656]:
Category var_1_wgt var_2_wgt var_3_wgt var_4_wgt
0 A 0.182022 0.182022 0.131243 0.182022
1 B 0.534814 0.534814 0.534814 0.534814
2 C 0.131243 0.534814 0.131243 0.182022
3 D 0.182022 0.151921 0.151921 0.131243
Решение
#convert Category to numerical encoding
df['C_Number'] = df.Category.apply(lambda x: ord(x.lower())-97)
#Get a dot product for each row with all category weights and the extract the weights by the category number
df['new_var'] = ((df.iloc[:,:4].values).dot(w.iloc[:,-4:].values))[np.arange(len(df)),df.C_Number]
Out[654]:
var_1 var_2 var_3 var_4 Category C_Number new_var
Symbol
1903 0.000443 0.006928 0.000000 0.012375 A 0 0.006038
1904 -0.000690 -0.007873 0.000171 0.014824 A 0 -0.001615
1905 -0.001354 0.001545 0.000007 -0.008195 C 2 -0.000595
1906 -0.001578 0.008796 -0.000164 0.015955 D 3 0.006481
1907 -0.001578 0.008796 -0.000164 0.015955 A 0 0.007300
1909 -0.001354 0.001545 0.000007 -0.008195 B 1 -0.000661