Давайте заимствуем функцию unnesting
у @ WeNYoBen , но немного изменим ее, чтобы она работала с вашими сетами.Затем расчет может быть выполнен с помощью слияния.
from itertools import chain
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: [*chain.from_iterable(df[x].to_numpy())]}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
df1 = unnesting(df, explode=['h_others'])
s = (df1.reset_index().merge(df.reset_index(),
left_on=['h_others', 's_id'],
right_on=['h_id', 's_id'])
.query('index_x != index_y')
.groupby('index_x').h_val_y.sum())
df['sum_h_others'] = s
Вывод:
s_id h_id h_val h_others sum_h_others
0 1 600 5 {700, 500} 18
1 1 700 12 {600, 500, 400} 11
2 1 500 6 {600, 700} 17
Более простой вариант - отобразить после unnesting, но применение делает это медленнее:
d = {(k1, k2): v for k1, k2, v in zip(*df[['s_id', 'h_id', 'h_val']].to_numpy().T)}
#{(1, 500): 6, (1, 600): 5, (1, 700): 12}
df['sum_h_others'] = df1[['s_id', 'h_others']].apply(tuple, 1).map(d).groupby(level=0).sum()