超市每天产生的销售数据就像一条永不停息的河流——订单流水、会员消费、促销活动等数据不断涌入系统。传统做法是每个月底手工统计报表,但管理层现在需要实时看到"当前累计销售额是否达标"、"哪些品类本周突然下滑"、"会员消费占比变化"等动态指标。这就引出了三个核心痛点:
第一,筛选器干扰问题。当我们在仪表板选择"饮料"品类时,其他图表中的"全店同比"指标不应该变成"饮料品类同比",但普通计算字段会受筛选条件影响。
第二,计算逻辑复用问题。每个季度都要重新计算"滚动季度累计",每次都要从零开始写公式,既容易出错又低效。
第三,时间智能缺失。像"移动3日平均"、"年度累计(YTD)"这类时间计算,需要能自动适应自然日历变化。
我在给某连锁超市做数据分析时就遇到过典型场景:市场部经理想同时看"当前月份销售额"和"对比去年同期的增长率",但筛选不同区域时,同比数据总是跟着区域筛选条件变化。后来用def函数构建的指标库完美解决了这个问题。
def函数的完整语法是:
python复制def(聚合函数, [维度列表], [过滤条件])
这就像给指标穿上三层防护服:
举个例子,计算"高单价商品销售额占比":
python复制def(SUM_AGG(销售额),[日期],[单价>100]) /
def(SUM_AGG(销售额),[日期])
这个公式中,分子只计算单价超过100的商品,分母计算全部商品,但两者都锁定在日期维度上,不受其他筛选器影响。
EARLIER的作用相当于SQL中的LAG函数,它能获取"上一行"的数据。但要注意一个关键特性:它只在def函数内部有效,且参照的是当前处理行的上下文。
比如计算"每日销售额累计":
python复制def(SUM_AGG(销售额),[日期],[日期<=EARLIER(日期)])
这里的魔法在于:对于2023-01-05这行数据,条件会变成"日期<=2023-01-05",相当于累计到当天。
处理时间序列时,DATEDELTA能实现精准位移:
python复制DATEDELTA("2023-01-01", -30) # 返回2022-12-02
DATEDELTA("2023-01-01", 90) # 返回2023-04-01
实测发现个技巧:处理月末日期时,DATEDELTA比直接加减天数更可靠。比如:
python复制DATEDELTA("2023-01-31", 1) # 正确返回2023-02-01
而简单日期加减可能会溢出到3月。
超市管理层最关心的"本月至今累计"指标,需要满足:
实现方案:
python复制DEF(SUM_AGG(销售额),
[年份,月份,日],
[年份=EARLIER(年份),
月份=EARLIER(月份),
日<=EARLIER(日)])
进阶技巧:添加周累计版本
python复制DEF(SUM_AGG(销售额),
[年份,周数,日],
[年份=EARLIER(年份),
周数=EARLIER(周数),
日<=EARLIER(日)])
传统排名在筛选时会重置序号,我们需要稳定的全品类排名:
python复制DEF(COUNTD_AGG(品类),
[品类],
[SUM_AGG(销售额)>EARLIER(SUM_AGG(销售额))])+1
这个公式的逻辑是:统计销售额比当前品类高的品类数量,+1得到排名。
实测发现需要配合:
python复制IF(ISNULL(排名值), 999, 排名值)
处理新品无历史数据的情况。
会员与非会员的消费对比需要保持计算基准一致:
python复制# 会员销售额占比
DEF(SUM_AGG(销售额),[是否会员="是"]) /
DEF(SUM_AGG(销售额),[])
# 会员客单价对比
DEF(SUM_AGG(销售额),[是否会员="是"])/
DEF(COUNTD_AGG(订单ID),[是否会员="是"]) VS
DEF(SUM_AGG(销售额),[是否会员="否"])/
DEF(COUNTD_AGG(订单ID),[是否会员="否"])
年累计(YTD)计算:
python复制DEF(SUM_AGG(销售额),
[年份,月份,日],
[年份=EARLIER(年份),
月份<=EARLIER(月份),
日<=EARLIER(日)])
移动7日平均:
python复制DEF(AVG_AGG(销售额),
[日期],
[日期<=EARLIER(日期),
日期>DATEDELTA(EARLIER(日期),-7)])
评估促销期间对比非促销期的表现:
python复制# 促销期日均销售额
DEF(SUM_AGG(销售额),[是否促销="是"])/
DEF(DATEDELTA(MAX(日期)-MIN(日期)+1,0),[])
# 非促销期日均销售额(基准值)
DEF(SUM_AGG(销售额),[是否促销="否"])/
DEF(DATEDELTA(MAX(日期)-MIN(日期)+1,0),[])
结合库存数据计算动态周转率:
python复制# 当前品类周销量
DEF(SUM_AGG(销量),[品类],[日期>DATEDELTA(TODAY(),-7)])
# 周转天数
DEF(当前库存量,[])/DEF(日均销量,[])
建议采用这样的命名规范:
code复制[业务域]_[时间粒度]_[计算类型]_V版本
例如:
sales_daily_ytd_v2
每次修改时创建新版本而非直接覆盖,方便回滚。
当指标变多时,注意:
实测案例:一个包含50+动态指标的仪表板,经过优化后加载时间从12秒降到3秒。
建议所有指标公式包含错误处理:
python复制IF(ISERROR(原公式), 0, 原公式)
对于除零错误特别处理:
python复制IF(分母=0, NULL, 分子/分母)
建议按以下顺序构建:
虽然def指标本身不受筛选影响,但可以通过:
python复制IF(ISFILTERED(品类), "当前筛选品类", "全品类")
实现智能标题提示。
设置定时检测:
python复制# 指标波动预警
ABS(今日值-预测值)/预测值 > 0.2
用颜色标记:
python复制CASE WHEN 波动率>0.2 THEN "red"
WHEN 波动率>0.1 THEN "yellow"
ELSE "green" END
在实施这套方案后,某超市的数据分析效率提升了60%,管理层决策响应速度从原来的3天缩短到实时可见。最关键的突破是当促销活动开始时,可以立即看到各门店的实时达成率,及时调整资源分配。