def removal_effects(df, conversion_rate): removal_effects_dict = {} channels = [channel for channel in df.columns if channel not in ['Start', 'Null', 'Activation']] for channel in channels: removal_df = df.drop(channel, axis=1).drop(channel, axis=0) for column in removal_df.columns: row_sum = np.sum(list(removal_df.loc[column])) null_pct = float(1) - row_sum if null_pct != 0: removal_df.loc[column]['Null'] = null_pct removal_df.loc['Null']['Null'] = 1.0 R = removal_df[ ['Null', 'Activation']].drop(['Null', 'Activation'], axis=0) Q = removal_df.drop( ['Null', 'Activation'], axis=1).drop(['Null', 'Activation'], axis=0) I = np.identity(len(Q.columns)) N = np.linalg.inv( I - Q.to_numpy() ) removal_dot_prod = np.dot(N, R.to_numpy()) removal_cvr = pd.DataFrame(removal_dot_prod, index=R.index)[[1]].loc['Start'].values[0] removal_effect = 1 - removal_cvr / conversion_rate removal_effects_dict[channel] = removal_effect return removal_effects_dict removal_effects_dict = removal_effects(trans_matrix, activation_rate) def removal_effect_pct(removal_effects, total_activations): re_sum = np.sum(list(removal_effects.values())) return {k: (v / re_sum) * total_activations for k, v in removal_effects.items()} attributions = removal_effect_pct(removal_effects_dict, total_activations)