
1. 项目概述从两个核心指标看产品健康度做产品运营或者数据分析的朋友对DNU和DAU这两个指标肯定不陌生。每天看报表这两个数字几乎是必看的。但说实话光看两个孤零零的数字很多时候感觉就像隔靴搔痒。你知道今天DAU涨了但为什么涨是新用户带动的还是老用户更活跃了你知道DNU降了但降了多少对大盘的影响有多大这些问题单看日环比、周同比的百分比变化往往不够直观。我自己在带团队做一款内容社区APP的数据分析时就深有体会。老板每天晨会必问“我们日活怎么样新增怎么样”一开始我们就把两个折线图往PPT上一贴。时间长了我发现一个问题这两个指标是强相关的但又彼此独立。新增用户DNU是DAU的“新鲜血液”而DAU的构成里又包含了新增和留存的老用户。把两条线分开看很难一眼就看出它们之间的动态关系和此消彼长的贡献度。直到我开始用“面积图”来可视化这两个指标整个局面才清晰起来。这张图不仅能回答“是多少”更能回答“为什么是这么多”。简单来说这个项目就是教你如何用Python将DNU和DAU这两个核心运营指标绘制成一张信息量巨大的堆叠面积图。通过这张图你可以直观地看到DAU的整体规模和趋势面积图的总高度代表了DAU。DNU对DAU的每日贡献图中最底层、通常用不同颜色表示的面积部分代表了当日新增用户。老用户留存或回流的活跃情况总高度减去底层新增部分剩下的面积就代表了当日活跃的老用户即DAU - DNU。这部分可以理解为“留存活跃用户”或“活跃存量用户”。结构变化的动态过程观察底层新增和上层老用户面积随时间变宽或变窄你能立刻判断DAU的增长是依靠“拉新”还是“促活”以及策略调整后的效果。无论你是刚入门的数据分析师还是需要经常复盘产品数据的运营、产品经理掌握这个分析方法都能让你的数据洞察力提升一个档次。它把枯燥的数字变成了有故事、有结构的画面。接下来我就手把手带你从理解指标开始到用Python实现这个分析可视化案例。2. 核心指标深度解析DNU与DAU究竟是什么在动手画图之前我们必须把基础概念夯扎实。很多人对DNU和DAU的理解停留在表面这会导致后续分析走偏。2.1 DAU产品每日活力的“总刻度”DAU日活跃用户数。定义很简单在统计当日启动或使用了你产品的独立用户数。注意关键词是“独立”同一个用户一天内打开十次也只算一个DAU。它衡量的是产品在一天内的用户覆盖面和基础热度。但DAU是一个“结果性”指标也是一个“黑箱”。一个100万DAU的产品可能是由90万老用户和10万新用户构成也可能是由50万老用户和50万新用户构成。这两种构成对应的产品健康状况、运营策略重心和未来风险是天差地别的。前者可能增长乏力但用户稳定后者可能处于烧钱拉新阶段留存堪忧。只看DAU总量我们无法进行诊断。2.2 DNU增长引擎的“每日燃料”DNU每日新增用户数。通常指统计当日首次启动应用或完成核心注册流程的独立用户数。它是产品增长的源头活水是市场、渠道、投放等部门工作成果最直接的数字化体现。然而DNU同样是一个需要谨慎看待的指标。高DNU值得庆祝但更要追问来源质量新增用户来自哪里自然增长、渠道投放还是活动裂变转化成本获取这些新增用户的成本CAC是多少次日留存这些新增用户有多少在第二天还会回来即DNU的次日留存率孤立地追求高DNU没有意义甚至危险。如果新增用户留存极差那么高DNU就像往一个漏水的池子里拼命灌水DAU这个“水位”很难持续上涨且营销费用浪费严重。2.3 关系的本质DAU 新增 留存活跃这才是最关键的公式也是我们做面积图的理论基础。从定义上我们可以推导出今日DAU 今日DNU 今日活跃的老用户这里的“今日活跃的老用户”指的是在今日之前含昨日新增并且在今日依然活跃的用户。他们可能是昨日新增今日留存的也可能是更早之前新增今日回流的。因此DAU的变化可以拆解为两个动力增长动力拉力来自今日DNU。这是外生变量受市场、投放影响大。留存动力推力来自老用户的持续活跃。这是内生变量主要取决于产品本身的价值、用户体验和运营手段。面积图的美妙之处在于它将这个公式视觉化了。总面积DAU被清晰地分割为两部分你可以一眼看出在任何一天拉力和推力各自贡献了多少“力量”。当总面积上升时你能立刻分辨是底层新增变厚了还是上层老用户变厚了亦或是两者都变厚了。这种直观性是任何数字报表都无法比拟的。注意这里有一个重要的统计口径问题。在实际业务中有时“老用户”可能指“去除今日新增后的所有活跃用户”这是一个较宽的口径。更精细的分析可能会区分“次留用户”、“7日内留存用户”、“7日以上留存用户”等。我们当前案例采用最基础的二分法新增 vs. 非今日新增活跃这是最通用也最直观的起点。掌握了这个方法后你可以将其扩展为更精细的多层堆叠面积图来分析不同生命周期用户的活跃贡献。3. 案例实战用Python绘制DNU/DAU面积图理解了原理我们进入实战环节。我将用一个模拟的数据集展示从数据准备到图形绘制的完整流程。你会看到用Python实现这一切既专业又高效。3.1 环境准备与数据模拟首先确保你的Python环境安装了必要的库。我们将主要使用pandas进行数据处理matplotlib进行绘图。在Jupyter Notebook或你的Python脚本中先导入它们。import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates from datetime import datetime, timedelta # 设置中文显示和图形样式可选让图表更美观 plt.rcParams[font.sans-serif] [SimHei, Arial Unicode MS, DejaVu Sans] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 plt.style.use(seaborn-v0_8-whitegrid) # 使用seaborn的白色网格风格更清晰因为真实业务数据涉及隐私我们这里模拟一份30天的数据。在现实中你的数据可能来自数据仓库、数据库或统计平台导出。# 模拟生成30天的日期序列 date_today datetime.now().date() dates [(date_today - timedelta(daysi)).strftime(%Y-%m-%d) for i in range(29, -1, -1)] # 最近30天从旧到新 # 模拟DAU数据假设有一个缓慢上升的趋势并加上一些日常波动 np.random.seed(42) # 固定随机种子确保结果可复现 base_dau 100000 trend np.linspace(0, 20000, 30) # 线性增长趋势 fluctuation np.random.randn(30) * 5000 # 随机波动 dau_values (base_dau trend fluctuation).astype(int) # 模拟DNU数据假设新增有周期性如周末低工作日高且整体趋势平稳 # 先创建一个周期性的基础值 weekday_effect np.array([1.0, 1.0, 1.0, 1.0, 1.0, 0.7, 0.6]) # 周一到周日的相对系数 weekday_idx [pd.Timestamp(d).dayofweek for d in dates] base_nu 8000 nu_values (base_nu * np.array([weekday_effect[i] for i in weekday_idx]) np.random.randn(30) * 1000).astype(int) # 计算每日活跃的老用户数 DAU - DNU retained_dau_values dau_values - nu_values # 确保老用户数不为负虽然模拟数据一般不会但真实数据清洗时需注意 retained_dau_values np.maximum(retained_dau_values, 0) # 创建DataFrame df pd.DataFrame({ date: dates, dau: dau_values, dnu: nu_values, retained_dau: retained_dau_values }) df[date] pd.to_datetime(df[date]) # 转换为datetime类型便于绘图 print(df.head()) print(df.tail())运行这段代码你会得到一个包含四列日期、DAU、DNU、留存活跃DAU的DataFrame。打印头尾可以查看数据概况。3.2 绘制基础堆叠面积图核心的绘图部分其实非常简洁。matplotlib的stackplot函数是专门为此类图表设计的。# 创建图形和坐标轴 fig, ax plt.subplots(figsize(14, 8)) # 绘制堆叠面积图 # 参数说明 # x: 日期序列 # y: 一个列表包含要堆叠的Y值序列。注意堆叠顺序先绘制的在底部。 # labels: 对应每一层的图例标签 # colors: 自定义颜色通常用对比色区分新增和老用户 ax.stackplot(df[date], [df[dnu], df[retained_dau]], # 底层是新增上层是老用户 labels[每日新增用户 (DNU), 活跃老用户 (DAU - DNU)], colors[#FF6B6B, #4ECDC4], # 红色代表新增醒目青色代表老用户稳定 alpha0.8) # 设置一定透明度避免过于厚重 # 在面积图上方绘制DAU的总量折线用于对比和精确读数 ax.plot(df[date], df[dau], color#2C3E50, linewidth3, label日活跃用户 (DAU), markero) # 设置图形标题和坐标轴标签 ax.set_title(DNU与DAU构成分析面积图 (模拟数据), fontsize16, fontweightbold, pad20) ax.set_xlabel(日期, fontsize12) ax.set_ylabel(用户数, fontsize12) # 优化X轴日期显示避免重叠 ax.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) # 只显示月-日 ax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekdaymdates.MO)) # 每周一显示一个刻度 fig.autofmt_xdate(rotation45) # 旋转日期标签 # 添加网格和图例 ax.grid(True, whichmajor, linestyle--, linewidth0.5, alpha0.7) ax.legend(locupper left, fontsize11) # 显示图形 plt.tight_layout() plt.show()执行代码后一张清晰的堆叠面积图就生成了。你应该能看到总面积从X轴到最顶部的轮廓代表了每日的DAU。底部红色的区域代表了每日的DNU。上部青色的区域代表了每日活跃的老用户。一条深蓝色的折线穿行在面积图顶部清晰地勾勒出DAU的每日具体数值。实操心得颜色选择至关重要。我习惯用暖色、亮色如红、橙代表“输入型”、“变动大”的指标如DNU用冷色、暗色如青、蓝、绿代表“存量型”、“较稳定”的指标如留存用户。这符合大多数人的认知习惯。另外一定要加上DAU的总量折线。因为面积图堆叠后顶部轮廓的纵坐标值就是DAU加上折线可以更精确地定位每日数值尤其是在进行不同日期间对比时折线的指引作用非常明显。3.3 图表进阶优化与信息增强基础的图表已经能说明问题但我们可以做得更好让它承载更多信息也更易于解读。1. 添加关键数据点标注在DAU的峰值、谷值或者DNU有特殊变化如活动开始日的点上添加文字标注说明当时可能发生的事件。# 接续上面的绘图代码在plt.show()之前添加 # 示例标注DAU最高点和最低点 max_dau_idx df[dau].idxmax() min_dau_idx df[dau].idxmin() ax.annotate(f峰值: {df.loc[max_dau_idx, dau]:,}, xy(df.loc[max_dau_idx, date], df.loc[max_dau_idx, dau]), xytext(10, 20), textcoordsoffset points, arrowpropsdict(arrowstyle-, connectionstylearc3,rad.2), fontsize9, bboxdict(boxstyleround,pad0.3, facecolorwheat, alpha0.8)) ax.annotate(f谷值: {df.loc[min_dau_idx, dau]:,}, xy(df.loc[min_dau_idx, date], df.loc[min_dau_idx, dau]), xytext(-40, -30), textcoordsoffset points, arrowpropsdict(arrowstyle-, connectionstylearc3,rad-.2), fontsize9, bboxdict(boxstyleround,pad0.3, facecolorlightblue, alpha0.8)) # 示例如果第15天有一个拉新活动标注DNU # 假设我们模拟数据中第15天索引14是活动开始日 event_day_idx 14 ax.axvline(xdf.loc[event_day_idx, date], colorgray, linestyle:, linewidth1, alpha0.7) ax.text(df.loc[event_day_idx, date], ax.get_ylim()[1]*0.9, 活动开始, rotation90, verticalalignmenttop, fontsize9, colorgray)2. 计算并可视化贡献度百分比有时我们更关心结构比例而非绝对数值。可以在图中添加一个辅助Y轴显示DNU占DAU的百分比。# 方法创建一个双Y轴图主Y轴是用户数次Y轴是百分比 fig, ax1 plt.subplots(figsize(14, 8)) # 主Y轴绘制堆叠面积图用户数 ax1.stackplot(df[date], [df[dnu], df[retained_dau]], labels[每日新增用户 (DNU), 活跃老用户 (DAU - DNU)], colors[#FF6B6B, #4ECDC4], alpha0.8) ax1.plot(df[date], df[dau], color#2C3E50, linewidth3, label日活跃用户 (DAU), markero) ax1.set_xlabel(日期, fontsize12) ax1.set_ylabel(用户数, fontsize12, colorblack) ax1.tick_params(axisy, labelcolorblack) ax1.set_title(DNU/DAU构成分析与新增占比趋势, fontsize16, fontweightbold, pad20) # 格式化X轴 ax1.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) ax1.xaxis.set_major_locator(mdates.WeekdayLocator(byweekdaymdates.MO)) fig.autofmt_xdate(rotation45) # 创建次Y轴用于显示DNU占比 ax2 ax1.twinx() dnu_ratio (df[dnu] / df[dau] * 100).round(1) # 计算每日新增占比百分比 ax2.plot(df[date], dnu_ratio, color#9B59B6, linewidth2, linestyle--, labelDNU占比 (%), markers, markersize4) ax2.set_ylabel(DNU占比 (%), fontsize12, color#9B59B6) ax2.tick_params(axisy, labelcolor#9B59B6) ax2.set_ylim(0, max(dnu_ratio)*1.2) # 设置合适的Y轴范围 # 合并图例需要将两个轴的图例线条合并到一起 lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left, fontsize10) ax1.grid(True, whichmajor, linestyle--, linewidth0.5, alpha0.7) plt.tight_layout() plt.show()这张图的信息量就更大了。你不仅能看到绝对数量的构成还能通过紫色的虚线追踪新增用户占比的变化趋势。例如如果总面积DAU在增长但紫色虚线在下滑那就明确表示增长主要靠老用户拉动新增的贡献在减弱。4. 从图表到洞察如何分析面积图图画出来了怎么用它来指导业务呢这才是关键。下面我结合几个典型的图形模式分享我的分析思路。4.1 健康增长模式特征总面积DAU稳步上升且红色底层DNU和青色上层老用户的面积都在协同增长或者至少有一方在稳定增长另一方保持平稳。解读这是最理想的状态。说明产品不仅有能力持续获取新用户市场拉力足还能让老用户保持或提升活跃度产品推力强。新增用户占比曲线可能在一个健康的区间内例如10%-30%视产品阶段而定平稳波动。行动建议保持当前策略的连贯性。可以进一步细分DNU渠道和老用户分层找出效率最高的增长点和最忠实的用户群进行资源倾斜。4.2 依赖拉新的脆弱增长特征总面积DAU在增长但增长几乎完全由底层红色面积DNU的增厚驱动上层青色面积老用户非常薄甚至萎缩。新增占比曲线持续处于高位如50%。解读这是一种“流量依赖症”。DAU的增长是靠不断“输血”买量、拉新维持的产品自身的“造血能力”用户留存和活跃很弱。一旦市场预算收缩或渠道竞争加剧DNU下降DAU就会立刻崩塌。这在很多工具类或靠补贴拉新的产品初期很常见。行动建议立即将运营重心从“拉新”转向“留存”和“促活”。需要深入分析用户流失原因优化新用户引导流程Onboarding提升产品核心功能的使用体验和粘性。同时审视新增用户质量是否目标用户定位有偏差。4.3 存量驱动的稳健模式特征总面积DAU平稳或缓慢增长底层红色面积DNU很小且稳定上层青色面积老用户构成了DAU的绝对主体。新增占比曲线处于低位如10%。解读常见于成熟期产品或垂直社区。市场增长见顶新增有限但产品拥有非常稳固的核心用户群。DAU的波动主要取决于老用户的活跃情况。这种模式增长潜力有限但基本盘稳固抗风险能力强。行动建议深耕现有用户价值。专注于提升用户生命周期价值LTV通过精细化运营、社区建设、增值服务等方式从存量用户中挖掘更多价值。同时可以尝试探索新的、更精准的细分市场来获取高质量新增而不是盲目追求数量。4.4 衰退预警模式特征总面积DAU持续下滑。此时需要看结构情况A红、青两层面积同步萎缩。说明产品可能遇到了系统性风险如竞品强力冲击、政策影响、重大版本失误导致新老用户同时流失。情况B红色面积DNU萎缩青色面积老用户暂时稳定。说明增长渠道出了问题但产品基本盘还在。这是修复增长渠道的窗口期。情况C红色面积稳定青色面积老用户萎缩。说明新增渠道没问题但产品留不住用户问题出在留存和活跃体系上。行动建议根据不同的结构衰退特征采取不同措施。A情况需全面危机应对B情况需复盘渠道策略和投放素材C情况需紧急检查产品近期的改版、运营活动是否伤害了用户体验。实操心得看面积图一定要有“对比”的思维。不要只看一天要看一个周期周、月的趋势。同时要结合业务事件版本上线、大型活动、节假日来看。例如一次大型营销活动后图中会出现一个红色的“脉冲”紧接着要看后续几天青色部分是否也能被带动起来这能评估活动的长期价值。另外建议将这张图与“留存率曲线图”、“用户活跃时长趋势图”放在一起看能获得更立体的产品健康度诊断。5. 常见问题与实战避坑指南在实际操作中你可能会遇到一些疑问和坑。这里我整理了几个最常见的问题。5.1 数据口径不一致怎么办问题数据团队给出的DNU口径是“新增注册用户”而DAU的口径是“启动用户”。一个用户当天注册但未启动会计入DNU但不会计入DAU这就导致DAU - DNU可能为负数面积图会出现错误。解决方案这是最需要警惕的一点在绘图前必须与数据部门或BI确认指标口径。确保DNU是DAU的子集。通常一个更安全的口径是使用“新增且活跃用户”作为DNU即当日既是新增又是活跃的用户这样DAU 当日新增且活跃用户 当日活跃的老用户恒成立。如果暂时无法统一可以在计算“活跃老用户”时使用max(0, DAU - DNU)来避免负值但这只是技术修补根本还是要推动口径对齐。5.2 图表波动太大看不清趋势问题尤其是DNU可能受单日渠道投放影响剧烈波动导致面积图锯齿严重长期趋势难以观察。解决方案可以引入移动平均线来平滑数据。例如计算DAU和DNU的7日移动平均然后用移动平均后的数据来绘制面积图。这能有效过滤噪音突出宏观趋势。但要注意在汇报时需说明使用的是平滑后的数据。# 计算7日移动平均 df[dau_7d_avg] df[dau].rolling(window7, centerFalse).mean() df[dnu_7d_avg] df[dnu].rolling(window7, centerFalse).mean() df[retained_dau_7d_avg] df[dau_7d_avg] - df[dnu_7d_avg] # 然后用 *_7d_avg 列去绘制面积图5.3 想分析更细分的用户构成问题基础的二分法新/老不够用了想分析比如“次留用户”、“7日内新用户”、“沉默回流用户”等更多层次。解决方案堆叠面积图支持多层堆叠。你只需要准备多个数据序列分别代表不同分层的用户数确保它们之和等于DAU或活跃用户总数。然后按顺序传入stackplot函数即可。颜色搭配上建议使用同一色系的不同深浅来代表同一大类如新用户用对比色系代表不同大类以保证图表的可读性。5.4 如何将这份分析自动化、产品化问题每天手动跑脚本太麻烦希望能做成定时报表或集成到数据看板中。解决方案脚本自动化将上述Python脚本封装成函数或类接受日期范围等参数。使用任务调度工具如Linux的cronWindows的任务计划程序或Apache Airflow等每日定时运行将生成的图表自动保存到指定目录或发送邮件。集成到BI工具更专业的做法是在数据仓库层就建好“每日用户分层活跃”的中间表。然后直接在Tableau、FineBI、Superset等BI工具中利用其内置的“堆叠面积图”功能进行可视化配置。这样业务人员可以自己筛选日期、维度进行交互式分析。Web应用使用Dash、Streamlit等Python框架可以快速搭建一个简单的内部数据看板将绘图逻辑嵌入实现交互式查询和图表展示。5.5 面积图与瀑布图有什么区别这是一个很好的延伸问题。两者都用于展示构成和变化但侧重点不同堆叠面积图强调随时间变化的整体规模及各部分的贡献。它展示的是在每一个时间点上总量的构成情况并且可以看到各部分随时间的趋势。核心是“构成”和“趋势”。瀑布图强调从一个初始值到最终值的变化过程展示各个正负因素是如何逐步累加导致结果变化的。它常用于分析月度收入变化、利润构成等。核心是“变化过程”和“因素分解”。在我们的场景中关注的是每日活跃用户构成的连续趋势所以面积图是更合适的选择。如果你想分析“本月DAU相比上月净增了10万这10万分别由哪些因素新用户、回流用户、留存用户变化贡献”那么用瀑布图会更清晰。掌握DNU/DAU面积图的分析方法就像是获得了一副观察产品生命体征的“X光眼镜”。它不能直接给你答案但能帮你快速、直观地定位问题所在让你问出更正确的问题。下次再被问到“我们日活怎么样”时试着不再只是抛出一个数字而是展示这张图并讲述数字背后的故事——新老用户是如何共同谱写产品每日的活跃曲线的。这才是数据驱动决策真正开始的地方。