当一些转化率指标发生波动时,往往需要分析原因,以转化率为例,影响转化率变化的可能因素有:
- 流量结构发生了变化,部分高转化的渠道或低转化的渠道的流量发生了较大的变化
- 部分渠道的转化发生了变化
- 新老客的结构发生了变化,如新客突然变多。
- 页面流程变更导致的问题
- 产品品类流量发生了变化
- 产品品类转化发生了变化
- …
由于涉及到维度非常的多,人工统计非常的繁琐,特整理了如下Python代码来实现自动化。
方案一:按占比分解转化率
将波动影响可以分为2种类型:
- 细分粒度下的流量结构变化
- 细分粒度下的流量转化变化
我们用以下模拟数据进行讲解:
相关计算说明:
- 支付率 = 支付单量/创建单量
- 创单单量占比 = 细分维度下的创单单量/整体创建单量
- 支付率分解 = 创建单量占比 * 支付率 = 细分维度下的支付单量/整体创建单量
- 整体影响 = 当期支付率分解 – 上期支付率分解
- 结构影响 = 当期支付率 * (当期创建单量占比 – 历史创建单量占比)
- 转化影响 = 历史创建单量占比 * (当期支付率 – 历史支付率)
Python代码实现:
import pandas as pd def compare_metrics(base_df, history_df, col_dim=None, col_cnt='', col_trgt=''): if col_dim is None: col_dim = [] df_x, df_y = history_df, base_df cr_x = df_x[col_trgt].sum() / df_x[col_cnt].sum() cr_y = df_y[col_trgt].sum() / df_y[col_cnt].sum() symb = '上升' if cr_x < cr_y else '下降' if cr_x > cr_y else '持平' df_g_x = df_x.groupby(col_dim)[[col_cnt, col_trgt]].sum().rename( columns={col_cnt: 'cnt_x', col_trgt: 'trgt_x'}).reset_index() df_g_x['cnt_x_p'] = df_g_x['cnt_x'] / df_g_x['cnt_x'].sum() df_g_x['cr_x'] = df_g_x['trgt_x'] / df_g_x['cnt_x'] df_g_y = df_y.groupby(col_dim)[[col_cnt, col_trgt]].sum().rename( columns={col_cnt: 'cnt_y', col_trgt: 'trgt_y'}).reset_index() df_g_y['cnt_y_p'] = df_g_y['cnt_y'] / df_g_y['cnt_y'].sum() df_g_y['cr_y'] = df_g_y['trgt_y'] / df_g_y['cnt_y'] df_g_x['dims'] = '' df_g_y['dims'] = '' for c in col_dim: df_g_x['dims'] = df_g_x['dims'].map(str) + "_" + df_g_x[c].map(str) df_g_y['dims'] = df_g_y['dims'].map(str) + "_" + df_g_y[c].map(str) if len(df_g_x) == len(df_g_y): df_g_x['contri_x'] = df_g_x['cnt_x_p'] * df_g_x['cr_x'] df_g_y['contri_y'] = df_g_y['cnt_y_p'] * df_g_y['cr_y'] df_g_x = pd.merge(df_g_x, df_g_y, on='dims', how='left') df_g_x['contri_dlt'] = (df_g_x['contri_y'] - df_g_x['contri_x']) df_g_x['cnt_factor'] = df_g_y['cr_y'] * (df_g_y['cnt_y_p'] - df_g_x['cnt_x_p']) df_g_x['cr_factor'] = df_g_x['cnt_x_p'] * (df_g_y['cr_y'] - df_g_x['cr_x']) df_g_x['cr_factor_p'] = df_g_x['cr_factor'] / df_g_x['contri_dlt'] df_g_x['cnt_factor_p'] = df_g_x['cnt_factor'] / df_g_x['contri_dlt'] df_re = df_g_x[ ['dims', 'cnt_x_p', 'cr_x', 'contri_x', 'cnt_y_p', 'cr_y', 'contri_y', 'contri_dlt', 'cr_factor', 'cnt_factor', 'cr_factor_p', 'cnt_factor_p', 'trgt_y', 'trgt_x', 'cnt_x', 'cnt_y']].sort_values( by='contri_dlt', ascending=False).reset_index() influence_list = [] for x in range(0, len(df_re.dims)): d = df_re.dims[x] factor = {'col_cnt': col_cnt, 'col_trgt': col_trgt, 'x_trgt_sum': df_x[col_trgt].sum(), 'y_trgt_sum': df_y[col_trgt].sum(), 'x_col_sum': df_x[col_cnt].sum(), 'y_col_sum': df_y[col_cnt].sum(), 'trgt_x': df_re.loc[df_re.dims == d, 'trgt_x'].sum(), 'trgt_y': df_re.loc[df_re.dims == d, 'trgt_y'].sum(), 'cnt_x': df_re.loc[df_re.dims == d, 'cnt_x'].sum(), 'cnt_y': df_re.loc[df_re.dims == d, 'cnt_y'].sum(), 'x_conv': cr_x, 'y_conv': cr_y, 'symb': symb, 'rk': x + 1, 'dimension': d, 'impact': df_re.loc[df_re.dims == d, 'contri_dlt'].sum(), 'structure': df_re.loc[df_re.dims == d, 'cnt_factor'].sum(), 'conversion': df_re.loc[df_re.dims == d, 'cr_factor'].sum()} influence_list.append(factor) influence_df = pd.DataFrame(influence_list) return influence_df else: print("维度不一致")
以上方案存在的问题是如果某一个转化率较低的渠道流量上升,计算出来对整体的转化率是增加的,与常规的认知不太一致。
方案二:固定历史其他维度,计算转化率差
还是以上面的的数据为例:
主要逻辑:固定历史其他维度的创建单量和支付单量,将历史中细分维度下的创单单量和支付单量更新为当期数据后计算整体创完率前后差
- 影响 = (历史整体支付单量 – 历史细分维度下支付单量 + 当期细分维度下支付单量)/(历史整体创单单量 – 历史细分维度下创建单量 + 当期细分维度下创单单量)- 历史支付率
这个方案存在一些疑问,应该固定当期的其他维度还是固定历史的其他维度。中间计算出来的值有些不一样。
- 固定历史其他维度:不考虑其他维度的近期变化
- 固定当期其他维度:计算该维度在当前场景下的影响
Python代码实现:(固定历史其他维度)
def compare_metrics(base_df, history_df, col_dim=None, col_cnt='', col_trgt=''): if col_dim is None: col_dim = [] df_x, df_y = history_df, base_df df_g_x = df_x.groupby(col_dim)[[col_cnt, col_trgt]].sum().rename( columns={col_cnt: 'cnt_x', col_trgt: 'trgt_x'}).reset_index() df_g_y = df_y.groupby(col_dim)[[col_cnt, col_trgt]].sum().rename( columns={col_cnt: 'cnt_y', col_trgt: 'trgt_y'}).reset_index() df_g_x['dims'] = '' df_g_y['dims'] = '' for c in col_dim: df_g_x['dims'] = df_g_x['dims'].map(str) + "_" + df_g_x[c].map(str) df_g_y['dims'] = df_g_y['dims'].map(str) + "_" + df_g_y[c].map(str) if len(df_g_x) == len(df_g_y): df_g_combine = pd.merge(df_g_x, df_g_y, on='dims', how='left') cnt_x = df_g_combine['cnt_x'].sum() trgt_x = df_g_combine['trgt_x'].sum() df_g_combine['impact'] = (trgt_x - df_g_combine['trgt_x'] + df_g_combine['trgt_y']) / ( cnt_x - df_g_combine['cnt_x'] + df_g_combine['cnt_y']) - trgt_x / cnt_x return df_g_combine