Довольно долго, но не требует циклов:
first = pd.DataFrame([['b', 15, 35, 20], ['d', 40, 35, 15]], columns = ['Category', '1', '2', '3'])
# Category 1 2 3
# 0 b 15 35 20
# 1 d 40 35 15
second = pd.DataFrame( [['a', 10 ],['b', 10],['c', 10 ],['d', 10 ], ['e', 10 ], ['f', 10 ]], columns=['Category', 'total'])
# Category total
# 0 a 10
# 1 b 10
# 2 c 10
# 3 d 10
# 4 e 10
# 5 f 10
Существует сопоставление для значений выше и ниже.если вы думаете об этом: сопоставление second['Category']
в first['Category']
можно суммировать следующим образом (second['category']
должно иметь различные значения):
second['above_mapped'] = second['Category'].shift(-1)
second['below_mapped'] = second['Category'].shift(1)
print(second)
Category total above_mapped below_mapped
0 a 10 b NaN
1 b 10 c a
2 c 10 d b
3 d 10 e c
4 e 10 f d
5 f 10 NaN e
Используя приведенную выше таблицу сопоставления, я могу создать двасловари для определения отображения:
above_map = pd.Series(second['above_mapped'].values, index=second['Category']).to_dict()
# {'a': 'b', 'b': 'c', 'c': 'd', 'd': 'e', 'e': 'f', 'f': nan}
below_map = pd.Series(second['below_mapped'].values, index=second['Category']).to_dict()
# {'a': nan, 'b': 'a', 'c': 'b', 'd': 'c', 'e': 'd', 'f': 'e'}
Я могу создать отображение между фактическими значениями для сопоставления, просто используя first
dataframe:
above_values = pd.Series(first['1'].values, index=first['Category']).to_dict()
# {'b': 15, 'd': 40}
# middle values just correspond to the 'Category' column of first
middle_values = pd.Series(first['2'].values, index=first['Category']).to_dict()
# {'b': 35, 'd': 35}
below_values = pd.Series(first['3'].values, index=first['Category']).to_dict()
# {'b': 20, 'd': 15}
Теперь сопоставляя числовые значенияsecond
с использованием трех вышеупомянутых словарей:
second['above_value_mapped'] = second['above_mapped'].map(above_values)
second['middle_value_mapped']= second['Category'].map(middle_values)
second['below_value_mapped'] = second['below_mapped'].map(below_values)
print(second)
Category total above_mapped below_mapped above_value_mapped \
0 a 10 b NaN 15.0
1 b 10 c a NaN
2 c 10 d b 40.0
3 d 10 e c NaN
4 e 10 f d NaN
5 f 10 NaN e NaN
middle_value_mapped below_value_mapped
0 NaN NaN
1 35.0 NaN
2 NaN 20.0
3 35.0 NaN
4 NaN 15.0
5 NaN NaN
Суммирование столбцов second['total']
, second['above_value_mapped']
, second['middle_value_mapped']
и second['below_value_mapped']
дает желаемое значение total
:
second.fillna(0, inplace=True)
second['new_total'] = second['total'] + second['above_value_mapped'] + second['middle_value_mapped'] +second['below_value_mapped']
second[['Category', 'new_total']]
Category new_total
0 a 25.0
1 b 45.0
2 c 70.0
3 d 45.0
4 e 25.0
5 f 10.0
Сроки
# Test dataframes
a =np.unique(pd.util.testing.rands_array(4, 10000)) #length 4 distinct strings as categories
# a.shape ~ 10000
df1 = pd.DataFrame(data=np.random.randint(1, 50, (a.shape[0], 3)),
columns=['1', '2', '3'],
index=a)
df2 = pd.DataFrame({'Category': a,
'total': [10]*a.shape[0]})
def akilat90(first, second):
second['above_mapped'] = second['Category'].shift(-1)
second['below_mapped'] = second['Category'].shift(1)
above_map = pd.Series(second['Category'].shift(-1).values, index=second['Category']).to_dict()
below_map = pd.Series(second['Category'].shift(1).values, index=second['Category']).to_dict()
above_values = pd.Series(first['1'].values, index=first['Category']).to_dict()
middle_values = pd.Series(first['2'].values, index=first['Category']).to_dict()
below_values = pd.Series(first['3'].values, index=first['Category']).to_dict()
second['above_value_mapped'] = second['above_mapped'].map(above_values)
second['middle_value_mapped']= second['Category'].map(middle_values)
second['below_value_mapped'] = second['below_mapped'].map(below_values)
second.fillna(0, inplace=True)
second['new_total'] = second['total'] + second['above_value_mapped'] + second['middle_value_mapped'] +second['below_value_mapped']
return second[['Category', 'new_total']]
def Jacquot(df1, df2):
df2 = df2.set_index('Category')
accu = np.zeros_like(df2['total'].values.ravel())
for cat in df1.index.unique():
idxs = np.where(df2.index == cat)[0]
for idx in idxs:
accu[max(idx - 1, 0) : (idx + 2)] += df1.loc[cat].values
df2['total'] += accu
return df2
%%timeit
akilat90(df1.reset_index().rename(columns = {'index':'Category'}), df2)
# 33.1 ms ± 1.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit
Jacquot(df1, df2)
# <Please check>