基于双因子使用Python将全国营收拆分至各城市的实现方法
全国啤酒营收按城市拆分的算法方案与Python实现
算法思路
你提出的线性拆分模型属于 带总量等式约束的双因子加权分配问题,核心是求解符合业务逻辑的权重系数,具体逻辑如下:
- 问题核心约束:所有城市计算得到的啤酒营收之和严格等于全国总营收,且两个因子的权重不能为负(消费支出越高、人均GDP越高的城市,啤酒营收理论上越高,负权重不符合消费常识)。
- 权重确定规则:如果没有城市级啤酒营收的抽样真值,可通过业务认知先确定两个因子的相对贡献占比(比如大众快消品的营收和城市总消费支出相关性更高,可给消费支出设70%权重,人均GDP设30%权重;无明确业务先验时可默认两个因子各占50%权重),再通过总量约束直接推导权重的解析解,无需复杂迭代优化。
- 如果有10个以上城市的真实啤酒营收抽样数据,可改用带等式约束的非负最小二乘拟合权重,拟合结果会更贴合实际业务规律。
- 结果校验:拆分完成后需做合理性校验,排除人口规模、消费能级差异大的城市出现营收倒挂的异常情况。
Python实现
依赖准备
仅需numpy、pandas两个基础数据处理库,如需做带样本的约束拟合,额外安装scipy即可:
pip install pandas numpy scipy
场景1:无城市级真值样本(最通用场景,严格匹配预设线性公式)
该实现严格遵循 city_beer = x * city_consumer_spending + y * city_gdp_per_capita 的公式,计算结果自然满足总和等于全国总营收的约束:
import pandas as pd import numpy as np # -------------------------- # 1. 读取数据,替换为真实数据路径 # 数据需包含三列: # city: 城市名称 # consumer_spending: 城市年度总消费支出(与全国啤酒营收单位保持一致,比如均为百万欧元) # gdp_per_capita: 城市年度人均GDP(单位与总营收统计口径对齐) # -------------------------- df = pd.read_csv("germany_city_data.csv") national_beer_total = 120000 # 替换为德国全国年度啤酒总营收的真实值 # -------------------------- # 2. 设定两个因子的相对贡献占比,可根据业务调整 # 这里默认总消费支出贡献占70%,人均GDP贡献占30% # -------------------------- spending_contrib = 0.7 gdp_contrib = 1 - spending_contrib # -------------------------- # 3. 反解线性公式的系数x、y # 推导逻辑:x/y = (spending_contrib / 消费支出均值) / (gdp_contrib / 人均GDP均值),保证两个因子的贡献占比符合设定 # 再代入约束 x*sum(消费支出) + y*sum(人均GDP) = 全国总营收,得到解析解 # -------------------------- sum_spending = df["consumer_spending"].sum() sum_gdp = df["gdp_per_capita"].sum() mean_spending = df["consumer_spending"].mean() mean_gdp = df["gdp_per_capita"].mean() # 计算系数比k = x/y k = (spending_contrib / mean_spending) / (gdp_contrib / mean_gdp) # 求解x、y x = national_beer_total / (sum_spending + sum_gdp / k) y = x / k # -------------------------- # 4. 计算每个城市的啤酒营收 # -------------------------- df["city_beer_revenue"] = x * df["consumer_spending"] + y * df["gdp_per_capita"] # 校验:总和是否等于全国总营收(浮点误差范围内可忽略) print(f"拆分后营收总和:{df['city_beer_revenue'].sum():.2f},全国总营收:{national_beer_total}") # 导出结果 df.to_csv("germany_city_beer_revenue.csv", index=False)
场景2:有部分城市啤酒营收抽样数据(拟合更精准)
如果有部分城市的真实啤酒营收数据,可通过带约束的最小二乘拟合x、y,约束条件为所有城市预测值总和等于全国总营收、权重非负:
from scipy.optimize import minimize # 假设有抽样数据的城市存在sample_revenue列,无抽样数据的该列为空 sample_df = df[~df["sample_revenue"].isna()].copy() X_sample = sample_df[["consumer_spending", "gdp_per_capita"]].values y_sample = sample_df["sample_revenue"].values # 定义损失函数:最小化抽样城市的预测误差 def loss(coef): x, y = coef y_pred = X_sample[:,0]*x + X_sample[:,1]*y return np.mean((y_pred - y_sample)**2) # 定义约束:所有城市预测值总和等于全国总营收 constraints = ({ 'type': 'eq', 'fun': lambda coef: (df["consumer_spending"]*coef[0] + df["gdp_per_capita"]*coef[1]).sum() - national_beer_total }) # 定义边界:权重非负 bounds = ((0, None), (0, None)) # 初始化权重、求解 init_coef = [x, y] # 用场景1的计算结果作为初始值,加快收敛 res = minimize(loss, init_coef, method='SLSQP', bounds=bounds, constraints=constraints) x_fit, y_fit = res.x # 计算全量城市的啤酒营收 df["city_beer_revenue_fit"] = x_fit * df["consumer_spending"] + y_fit * df["gdp_per_capita"]
注意事项
- 所有指标必须统一统计周期、统一货币单位,避免口径不一致导致结果偏差。
- 因子相对贡献占比可根据业务调整:如果高端啤酒占比较高,可适当提高人均GDP的权重;如果大众平价啤酒占比高,可提高总消费支出的权重。
- 拆分完成后建议分层校验:按城市人口规模、消费能级分层,检查同层级城市的啤酒营收是否在合理区间,避免出现小城市营收高于同类型大城市的异常。
内容的提问来源于stack exchange,提问作者Ng.Alina
相关产品推荐
相关产品推荐

