ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

新茶饮数据解析实战:从报告PDF到门店快照、RFM分群与SKU预测

新茶饮数据解析实战:从报告PDF到门店快照、RFM分群与SKU预测 简介这份报告PDF名为《2023年新茶饮研究报告》由行业观察机构基于美团平台数据编制面向新茶饮从业者、品牌运营方、市场研究人员及餐饮赛道投资人旨在提供一份兼顾宏观规模与微观消费行为的行业参考。报告指出2023全年新茶饮消费市场规模预计达1498亿元全国门店数量超50万家饮品连锁化率整体稳定同时聚焦外卖市场呈现订单量持续增长、城市分层差异化、夏季高温销量走高以及“秋天的第一杯奶茶”等营销节点效应并总结出全时段消费、用户高频多场景等趋势。报告还从产品上新、加盟扩展、食品安全科技管理、减糖行动、ESG践行和品牌出海等角度分析了行业的社会贡献度与发展动能。资源为单个PDF文件大小约2.58MB内含目录结构与数十张数据图表可直接按章节查阅。已有65人学习下载适合需要快速建立新茶饮行业认知、撰写分析报告或制定经营策略的读者。1. 先拆报告PDF的数据语序2023新茶饮是一张可查询的表拿到一份《2023年新茶饮研究报告.pdf》多数人的第一反应是翻到结论页找趋势然后转发到工作群。但如果你的岗位是数据工程师、增长分析师或数字化产品经理这份报告PDF应该被当成一套待解析的数据字典而不是一篇通读即可的叙事文本。新茶饮行业在2023年的关键词是“万店扩张下沉、健康化成主线、供应链决定毛利”落到数据工作上就是三件事把市场总量拆成门店快照、把消费描述转成用户分群口径、把品牌动作翻译成可执行的标签与预测逻辑。这篇文章顺着报告PDF的语句结构梳理一套从解析到建模再到落地的最小方案覆盖读报告的人真正会动手做的事。2. 用门店快照表还原新茶饮2023年的市场扩张节奏报告PDF里最醒目的通常是市场规模、门店总数、增速和城市线级分布。这些数字看一遍有感知但没法回答实际问题某个品牌在一个城市的密度是否饱和、跟竞对的重叠度如何、开店节奏是先下沉还是先加密。要回答这些得先把报告里“静态总量”转成“动态切面”。2.1 先把报告PDF解析成可提取的结构拿到报告PDF我不建议直接手工抄数。2023年新茶饮报告的图表大多是柱状图和占比图文字结论反而是次要的。常见做法是先用pdfplumber把页面上带数字的区域截出来再按表格关键字做抽取。这里给一个解析模板import pdfplumber import pandas as pd pdf_path 2023年新茶饮研究报告.pdf tables [] with pdfplumber.open(pdf_path) as pdf: for page_no, page in enumerate(pdf.pages): # 只解析存在表格线的页面跳过封面和目录 extracted page.extract_tables() for table in extracted: df pd.DataFrame(table) tables.append((page_no 1, df)) # 将带“门店数量”字段的页面单独筛出 key_dfs [] for page_no, df in tables: text df.astype(str).agg( .join, axis1).str.cat(sep ) if 门店 in text and (万 in text or 家 in text): key_dfs.append((page_no, df)) for page_no, df in key_dfs[:3]: print(f--- page {page_no} ---) print(df.head(10).to_string(indexFalse))说明一下这段代码的逻辑pdfplumber按页解析表格返回的是二维列表转成DataFrame后方便按页聚合文本。筛选条件同时命中“门店”和数量单位能定位到门店数图表页。解析出来的数据先不清理因为报告里的单位混用——有的表写“万家”有的写“家”统一单位放在下一节做。需要注意的是PDF表格解析的结果往往带表头错位、合并单元格空值等问题。不要指望一次解析得到规整行列正确姿势是先落成一个原始raw_tables目录再写清洗逻辑。2.2 门店快照表的结构设计与口径解释报告之后要做的是把解析结果和公开渠道的门店数据合并成一张快照表。这里给出一张推荐结构按“时间×品牌×城市×商圈类型”四个维度建模字段名类型说明示例stat_monthstring统计月份建议每月底生成2023-12-31brandstring品牌名需做归一化喜茶、奈雪的茶、蜜雪冰城citystring城市名按行政区划成都city_tierint城市线级1~5线2districtstring商圈/行政区武侯区store_typestring门店类型标准店、GO店、旗舰店标准店store_countint该网格内门店数18competitor_countint同商圈主要竞对门店数23open_ratefloat近3个月净开店率0.12这张表的核心是网格化。报告PDF里给的“全国门店总量”只能做宏观参照真正决策要落到城市-商圈粒度。落地时门店明细数据最常见的来源是地图开放平台的POI检索按品牌关键词拉取门店坐标再通过逆地理编码映射到省市区。每月跑一次全量快照保存为store_snapshot_YYYYMM.csv供下游查询。生成快照后验证数据质量的第一条命令是检查品牌词命中率grep -o 喜茶\|奈雪\|蜜雪冰城\|茶百道\|沪上阿姨\|古茗\|霸王茶姬 store_snapshot_20231231.csv | sort | uniq -c | sort -rn如果某个品牌在报告PDF里被定义为“头部品牌”但在快照表里命中数极少通常有两种原因一种是该品牌在报告中的统计口径包含非门店形态如档口、无人零售柜另一种是POI数据没有覆盖下沉市场的乡镇网格。前者要补store_type字段后者要额外拉乡镇POI补齐。2.3 密度矩阵报告里的增速在网格上怎么计算报告PDF里的“增速”是总量同比不是决策指标。城市内门店是否饱和要看每万人门店覆盖和同商圈竞对密度。用一个简单的SQL就能把快照表变成密度矩阵SELECT brand, city, COUNT(DISTINCT district) AS area_cnt, SUM(store_count) AS total_stores, ROUND(SUM(store_count) * 10000.0 / MAX(population), 2) AS stores_per_10k FROM store_snapshot WHERE stat_month 2023-12-31 GROUP BY brand, city ORDER BY total_stores DESC LIMIT 30;这条查询把门店数叠加上城市人口数据算出每万人门店覆盖。比如茶饮品牌在一线城市的合理区间通常在 3~5 家/万人在四五线城市可能只有 1~2 家。若某个城市店数增速很快但每万人覆盖低于区间下限说明报告里的“下沉红利”在该区域仍未兑现是拓展候选地反之若高于区间上限且竞对密度也在上升新增门店大概率是零和博弈。报告PDF里的分布图不会告诉你这些因为总量叙事天然掩盖结构差异。把门店快照表建起来之后报告里那句“市场进入存量竞争”才算有了可验证的落点。3. 从消费洞察到用户分群新茶饮客户画像的计算口径报告PDF中关于消费者部分通常会写年龄分布、价格带偏好、购买频次和联名消费意愿。这些描述性结论难以直接指导运营动作因为“年轻人喜欢健康茶饮”没法直接圈选人群。落到数据工作里需要把报告里的“画像描述”翻译成一张可计算、可回刷的用户标签宽表。3.1 消费人群分析对应的数据动作是打宽表无论是从微信小程序订单、POS流水还是会员系统里取数最终都要汇总到一张用户粒度的特征表。单个订单表不能直接用于分群因为消费行为是多事件叠加出来的。宽表设计如下字段名计算逻辑用途user_id用户唯一ID关联键recency最近一次消费距今天数RFM-Rfrequency_90d近90天有效订单数RFM-Fmonetary_90d近90天实付金额总和RFM-Mavg_price近90天平均实付单价价格带分层sugar_free_ratio近90天“不另外加糖/少糖”订单占比健康偏好标签new_tea_cup_ratio近90天“纯茶/轻乳茶”品类占比品类偏好报告里说的“健康化趋势”落到字段上就是sugar_free_ratio和new_tea_cup_ratio。不要用“是否点过健康饮品”这种布尔值因为低频用户偶尔点一次也会被误判为偏好要用占比。占比类的字段建议用近90天窗口太短不稳定太长对季节新品不敏感。3.2 新茶饮RFM的阈值和代码怎么写RFM 是通用框架但新茶饮的阈值不能照搬电商。茶饮客单价低、消费频次高、流失周期短如果按电商的“30天未购”作为流失会导致沉睡用户规模过大。这里建议的基准值是R 以7天为活跃线F 以5单/月为高频线M 以30元为价格带分界。当然具体数值要按品牌客单调整先用分位数跑一版import pandas as pd import numpy as np df pd.read_csv(user_feature_90d.csv) # 阈值R 用 7/14/30 天然分段F/M 用分位数 r_bins [0, 7, 14, 30, 999] f_cut df[frequency_90d].quantile([0.3, 0.7]).values m_cut df[monetary_90d].quantile([0.3, 0.7]).values df[r_score] pd.cut(df[recency], binsr_bins, labels[4, 3, 2, 1]) df[f_score] pd.cut(df[frequency_90d], bins[-1, f_cut[0], f_cut[1], 9999], labels[1, 2, 3]) df[m_score] pd.cut(df[monetary_90d], bins[-1, m_cut[0], m_cut[1], 999999], labels[1, 2, 3]) # 分群逻辑高价值/中价值/低价值 def rfm_segment(row): r, f, m int(row[r_score]), int(row[f_score]), int(row[m_score]) if r 3 and f 2 and m 2: return 高价值活跃 elif r 2 and (f 2 or m 2): return 中价值培育 elif r 2 and f 1: return 沉默唤醒 else: return 普通用户 df[segment] df.apply(rfm_segment, axis1) print(df[segment].value_counts(normalizeTrue))这里的核心参数是r_bins和分位阈值。R分箱直接使用业务天然周期7天内算高频活跃8~14天是“一周没来”15~30天是“月内沉默”30天以上才算流失。F和M用分位数而非固定值是因为不同定位的品牌差异巨大蜜雪冰城的客单和喜茶客单差了近一倍固定阈值会误杀整体中坚用户。3.3 用户分群结果如何跟报告里的画像交叉验证分群表不能只看分布比例还要跟报告PDF里的人群描述做交叉。报告说“2023年消费者更理性”对应数据上应该是“高价格带用户占比下降、中价格带占比变大”。验证方式是直接看分群后的价格带分布SELECT segment, CASE WHEN avg_price 15 THEN 低价格带 WHEN avg_price BETWEEN 15 AND 25 THEN 中价格带 ELSE 高价格带 END AS price_tier, COUNT(*) AS user_cnt FROM user_features_with_segment GROUP BY segment, price_tier ORDER BY segment, user_cnt DESC;如果跑出来“高价值活跃”用户里高价格带占比不足两成说明这个品牌的RFM高价值定义有问题m_cut阈值需要下调或者高价值用户本质是高频低客单的“口粮茶”用户。这种情况下报告里“健康化消费升级”的结论在数据上就是不成立的。不要让报告结论指导建模方向要让分群结果跟报告结论对撞对不上就先检查数据口径。4. 私域运营的最小数据闭环把会员行为转成标签策略报告PDF里关于数字化通常会提到私域会员、复购率、客单价提升这类词。新茶饮品牌的私域建设在2023年已经非常成熟扫码点单即会员、企业微信社群做触达、小程序发券做转化。但多数品牌的标签体系停留在手动打标状态能用的数据工作是用订单行为自动打标再输出到触达工具。4.1 私域不是拉群是会员身份的统一很多品牌把私域理解成“把用户加到微信群里”但数据显示进群用户和普通用户的复购差异在排除优惠券干扰后通常只有不到5个百分点。真正有效的是把小程序点单数据、线下POS、第三方外卖平台订单通过手机号统一到一个user_profile表里。统一身份之后后续所有自动化标签才有意义。一个可落地的做法是反向ETL从订单明细表里实时算出每个用户的已购品类和偏好标签写回会员系统的扩展字段。这套链路不复杂核心是一个定时更新的“画像刷新任务”。4.2 从订单数据生成标签圈的实用脚本以下脚本是私域运营里最常见的需求圈出近30天未消费但曾经高频的用户输出成一个带手机号的CSV用于企业微信/短信召回。注意只输出“可触达且不过度打扰”的人群import pandas as pd orders pd.read_csv(orders_last_90d.csv) profile pd.read_csv(user_profile.csv) # 统计近90天消费行为 agg orders.groupby(user_id).agg( last_order_date(order_date, max), order_cnt(order_id, count), ).reset_index() # 与画像表关联拿手机号和城市 df agg.merge(profile[[user_id, mobile, city]], onuser_id, howleft) # 圈选条件近30天未购 90天内消费≥3次 df[last_order_date] pd.to_datetime(df[last_order_date]) cutoff_30 pd.Timestamp(2024-01-31) - pd.Timedelta(days30) cutoff_90 pd.Timestamp(2024-01-31) - pd.Timedelta(days90) target df[ (df[last_order_date] cutoff_30) (df[last_order_date] cutoff_90) (df[order_cnt] 3) ].copy() # 限制单城市输出量防止一次触达过度 target target.sort_values(order_cnt, ascendingFalse) target target.groupby(city).head(2000) target[[mobile, user_id]].to_csv(recall_202402.csv, indexFalse)这个脚本的关键参数在圈选条件order_cnt 3是过滤“低频非活跃用户”groupby(city).head(2000)是防止某个城市的召回量过大触发风控。召回动作在私域里不是越多越好尤其是企微群发频率过高会导致用户屏蔽或投诉单次单城市控制在2000以内是为了安全触达。注意一点脚本里的“近30天”用的是固定日期生产环境建议用asof_today替换避免因为调度失败导致数据回溯。更严谨的写法是让参数由外部传入python recall_user.py --schedule_date 2024-01-31 --recall_days 30 --freq_min 3 --city_limit 2000这样每个参数都能被业务方直接理解不需要改代码就能调整圈选逻辑。4.3 触达效果的三个核心指标标签圈选只是前半段后半段是衡量私域动作的真实增量。至少要跟踪三个指标48小时召回率、一周复购转化率、优惠券核销率。48小时召回率代表触达的打开意愿一周复购转化率代表真实的生意增量核销率代表活动设计是否合理。若一周复购转化率低于3%先查圈选人群是否过宽再看券面力度和短信文案——不要先把锅甩给标签模型。5. 新茶饮门店的SKU预测起步值先做对预测粒度报告PDF收尾通常回到供应链和产品创新。但新茶饮的供应链预测跟传统零售的差别很大杯量随时段波动、新品生命周期短、天气影响显著、区域性口味差异强。这一章给出一个可落地的起点不讨论复杂模型。5.1 SKU预测先定粒度和预测长度对单店做预测时颗粒度是“门店×SKU×日”预测长度建议1~3天而不是7天。原因是新茶饮的销售受天气和活动影响太大预测超过3天的准确率下降得非常快对订货的帮助已经不大。对于单店SKU数量较多的门店可以放弃逐SKU预测改成“按杯型×茶底”的组合维度因为同一茶底的不同SKU原物料高度重叠。5.2 指数平滑的起步参数怎么写在没有历史模型的情况下用带季节调整的Holt-Winters加一个天气修正项就够了。这里给一个最简实现适合数据量只有90天的单店场景import pandas as pd import numpy as np sales pd.read_csv(store_daily_sku_sales.csv) # 三参数指数平滑 def holt_winters_forecast(series, alpha0.4, beta0.1, gamma0.3, m7, h3): level, trend, seasonal [np.nan] * len(series), [0.0] * len(series h), [] # 初始化用前m天均值做level季节项取当天与均值的差 init_level series.head(m).mean() # 初始化季节项 seas [0.0] * m for i in range(m): idx i % m seas[idx] series.iloc[i] / init_level seasonal [s / m for s in seas] for i, y in enumerate(series): if i 0: level[i] init_level else: prev level[i - 1] if not np.isnan(level[i - 1]) else init_level level[i] alpha * (y - seasonal[i % m]) (1 - alpha) * (prev trend[i - 1]) trend[i] beta * (level[i] - prev) (1 - beta) * trend[i - 1] forecast [] last_level, last_trend level[-1], trend[-1] for step in range(1, h 1): idx (len(series) - 1 step) % m forecast.append(max(0, last_level step * last_trend seasonal[idx])) return forecast # 用周维度m7预测后3天 demo sales[sales[sku] 芝士葡萄].sort_values(sale_date) fc holt_winters_forecast(demo[qty].reset_index(dropTrue), h3) print(fc)参数说明很重要alpha是水平平滑系数新茶饮日销波动大0.4意味着更信任近期数据beta是趋势系数0.1保守一些因为单店短期趋势很容易被活动和天气带偏gamma是季节项系数0.3配合m7捕捉一周的周期效应。这个模型在数据量只有60~90天时表现稳定等积累到一年四季数据再考虑加节假日哑变量。预测完之后订货量不能直接等于预测值要叠加安全库存。新茶饮的保鲜期极短但不同物料的容忍度不同茶底原叶可以备2~3天用量鲜奶和水果要按日配送多备一天损耗率就大幅上升。这个追加步骤本质上就是给预测值按物料类型乘上不同的系数而不是统一加一个百分比。本文还有配套的精品资源点击获取
返回列表