资讯动态

加盟数据实战:从数据清洗到可视化的完整分析流程

发布时间:2026/10/16 12:52:58 来源:尧图企业网站定制
前阵子整理了一批某加盟信息平台上的品牌数据因为平台页面编号的原因我把整个项目命名为“1637加盟网数据实战”。这份数据初看就是一张普通的品牌信息表但顺着加盟费、门店数、品类这些字段往下挖能挖出不少关于加盟市场的有意思结论。这篇文章把这个项目从数据清洗到可视化呈现的完整过程复盘一遍重点说清楚每一步为什么这么做、图表怎么选、以及那些常规教程里不会写的坑。整个项目做下来覆盖了一个数据分析任务里最核心的闭环拿到原始数据、理字段口径、做清洗、定分析维度、写代码出图、再回头解读业务含义。不管你是刚开始学数据分析的学生还是自己想做市场调研、考察加盟项目的朋友这套处理思路都可以直接拿去套用。数据源本身不复杂不需要搭集群也不需要写复杂的工程代码一台普通电脑装好Python环境就能跑完属于典型的“轻项目重分析”练习。1. 项目概述与目标拆解1.1 这批加盟数据里到底有什么这份数据一共收集了两千多条品牌记录字段不算多但每个字段的信息量其实都不小。核心字段包括品牌名称、所属品类、加盟费、保证金、门店总数、投资预算区间、加盟年限和开放区域。实际拿到的原始数据长什么样我整理了一个匿名示例方便理解字段名示例值原始类型问题点品牌名称某奶茶连锁文本有重复收录所属品类餐饮 / 教育 / 零售文本同一品类叫法不统一加盟费5.8万 / 面议文本带单位且有非数值保证金1万 / 0文本文本与数字混存门店总数350家 / 未知文本“家”与“未知”混存投资预算10-20万文本是一个区间不是单一数值开放区域华东、华南等文本多个区域叠加这类数据在垂直信息网站上非常典型它不像标准数据库导出的Excel那么干干净净每一列都带着面向用户的展示格式。加盟费写成“5.8万”、“面议”或“10-20万”门店数写成“500”甚至手滑多写几个空格都是家常便饭。这也是我拿它当分析素材的原因之一数据清洗的练习价值足够高处理完后再看任何类似的文本型表格都会淡定很多。另外要特别注意这个数据集收录的是平台上的品牌信息不等于全行业的加盟品牌全集。平台本身有自己的入驻规则和筛选机制品牌曝光量和真实门店数之间也可能存在时间差。后面做分析结论的时候我一直提醒自己手里的数据只是市场的一个切片不能说“餐饮加盟最低门槛是五万”这种绝对话只能说“这批平台收录的餐饮品牌里加盟费大多落在这个区间”。1.2 分析目标选了哪几个维度数据拿到手以后我先没有急着打开绘图工具写代码而是花了一点时间把想回答的问题写下来。数据分析的第一步是把问题问清楚代码只是实现手段。结合这份数据能回答的问题我最终定了四个核心维度第一个是加盟费的整体分布。加盟费相当于进入一个加盟体系的门票钱它直观地反映品牌对加盟商的资金门槛也能侧面反映品牌定位是走量还是走高客单。第二个是门店规模Top20排行。门店数量是品牌扩张能力最直接的表现排个榜能看出当前哪些品牌在跑马圈地、哪些品类更受加盟商青睐。第三个是不同品类的投资门槛对比。加盟费、保证金和投资预算放在品类维度下对比能看出教育、餐饮、零售这些方向在资金要求上的差异这个是准备入局的人最关心的问题。第四个是投资预算和门店数量的交叉关系。通过散点图观察这两个变量之间有没有相关性判断高投入是否对应高规模这个交叉维度能挖掘出单看一列数据发现不了的信息。这四个维度合在一起基本回答了一个潜在加盟商会问的问题我大概要准备多少钱、选哪个品类、市面上哪些品牌跑得比较快。每个维度对应一种图表类型分布用直方图和箱线图排行用横向条形图品类对比用带统计信息的柱状图或箱线图交叉关系用散点图。这个“先定问题再选图表”的顺序很重要很多人一上来就把所有字段两两组合画一遍最后图很多但讲不出一个结论。1.3 为什么选这类网站数据做实战现在网上的数据分析教程很喜欢用经过处理的经典数据集比如鸢尾花、房价预测这些它们用起来确实顺手但有个问题数据太干净了清洗环节几乎是跳过的。真实业务场景里你拿到的数据大概率是一堆充满缺失值、单位不统一、格式混乱的表格光是把“面议”“未知”“500”这些脏数据整理成能参与计算的数值就要花不少时间。加盟网站数据在这点上特别合适。它的复杂程度处在“无聊的练习数据”和“崩溃的生产数据”之间既不至于清洗到想放弃又能让你真正体会字段口径统一的重要性。整个项目做完之后哪怕换一批别的垂直行业数据来练手流程也是通用的先看字段类型再定清洗规则然后定义指标口径接着做探索性可视化最后带着业务视角解读图表。这套流程比记一堆matplotlib绘图语法值钱得多。2. 数据清洗与字段口径统一2.1 数据来源与采集方式这批数据主要来自加盟信息平台的公开品牌页面属于公开可浏览的商业信息。采集方式上我没有直接讲什么高深的爬虫框架就是按平台的分页列表逐页抓取品牌页里的结构化字段加上一部分人工核对。实际做的时候我建议不要太依赖一次性自动化抓取因为加盟网站的品牌页模板经常会有微调今天抓的正常字段明天可能就错位了。更好的做法是先抓两页样本手工检查一遍字段对应关系再写循环抓全量。采集下来之后的数据保存为CSV文件编码选UTF-8-SIG这个细节对Windows系统尤其重要不然用Excel打开会显示乱码。后续所有清洗和分析都在这个CSV基础上做原始文件单独存一份备份任何一步操作出错都能回退这是我在数据项目里一直坚持的习惯。2.2 文本型数值字段的清洗方案原始数据里最头疼的是加盟费、保证金、投资预算这三列因为它们都是以文本形式存储的还夹杂着“万”“面议”“未知”这类内容。先说加盟费的处理逻辑原始写法清理后处理方式5.8万转为数值 5.8面议记为缺失值单独标记3-8万取区间中位值 5.510万以下取上限值的50%记为 5.0无记为缺失值这里面容易踩坑的是“面议”的处理成千上万别直接强制转成float否则会报错或者被转成NaN而NaN和数据的真实缺失在业务上是有区别的。我的做法是单独生成一列“加盟费是否有明确数值”的布尔标记这样后续既可以用有数值的样本做分布分析又能统计出有多少品牌是面议价格的盘。投资预算区间转数值是另一个细节。原始数据常常写成“10-20万”如果直接去掉“万”字得到的是字符串“10-20”还需要按分隔符拆成两个数字再取平均。为什么取中位数而不取上限或下限因为加盟投资通常是弹性预算上下限之间才是备选方案的真实投入预期中位数在后续散点图里能提供相对稳定的点位不会因为个别区间的极端值带偏整体分布。门店数清洗相对简单但还是有坑“350家”需要去掉“家”字“500”需要处理“”号“未知”要转为缺失值。这里我统一用正则表达式提取数字部分并且注意当字符串里同时出现“三”和“百”这类中文数字时要用专门映射表不过本次数据里暂时没遇到所以直接按阿拉伯数字处理就可以了。清洗后我把门店数转成了整数类型并把大于0的值作为有效样本保证后续排序统计不被缺失值干扰。2.3 缺失值与异常值的处理原则缺失值处理上我的原则是“能不填就不填填了必须做标记”。加盟费是面议的样本如果直接填充平均值会人为压缩数据的分布形态。我更倾向于先保留缺失状态在计算分布时用dropna剔除在计数时用fillna(0)或单独统计面议的数量。对门店数也是同样的逻辑“未知”和“0家”在业务上完全不同前者是没公开统计后者是刚起步必须区分开。异常值这块我遇到最典型的情况是某品牌门店数标注达到上万级别而它的加盟费反而只有很低的值这个组合放在整个数据集里显得格外扎眼。我没有直接删掉它而是先查了原始页面的记录确认是平台收录的历史品牌数据没有同步更新才决定把它标记为异常值并在散点图里用特殊颜色标注出来而不是移除样本。用一个简单表格总结清洗后的字段口径字段清洗后类型有效样本占比备注加盟费float72%其余为面议或缺失保证金float58%部分品牌不公开门店数int81%含少量异常值投资预算中位数float78%区间文本取中位值品类category99%已统一名称3. 核心分析维度与可视化设计思路3.1 加盟费分布为什么用直方图加箱线图加盟费是企业人群最关心的指标之一我首先拿它做分布分析。加盟费数据天然呈现右偏分布大量品牌的加盟费集中在低区间少数品牌收费很高如果把所有值直接丢进普通坐标轴里低区间的柱子会被压成一条细线完全看不出分布。解决办法是先画直方图看形态再叠加一张箱线图看分位数和离群点。直方图的分箱数我经过几次对比最终把数据切成30个箱子这个粒度既能看出5万以下的密集段又不至于因为箱子太多产生大量空区间。箱线图放在直方图旁边能直接读出四分位数和边缘离群点一眼就知道所谓“平均加盟费”其实被高端品牌拉高了大部分品牌的加盟费集中在下四分位数附近。这张图的业务解读是平台收录的品牌里加盟费在0到10万区间的占比超过六成10到20万是第二密集区间超过30万的品牌属于少数派。所以对普通加盟者来说加盟费门槛并不算高真正的资金压力往往集中在后续的装修、设备、原材料这些一次性投入上光看加盟费很容易被低估总成本。3.2 门店规模排行横向条形图才是首选门店数是品牌扩张规模的直接体现。做Top20排行的时候我没有用竖向柱状图而是选了横向条形图原因很实际品牌名称属于长文本动辄七八个字起放在横轴底部会旋转成斜字或者挤成一团完全没法读。横向条形图让品牌名排在纵轴从长到短整齐排列阅读顺序符合人眼从左上到右下的扫描习惯。这里还有个细节排序时要指定降序让门店数最多的品牌在图表顶部否则matplotlib默认按字母顺序排列会得到一张完全没法读的图。Top20榜单里最显眼的是奶茶类、快餐类品牌它们门店数普遍在数百家到上千家这背后是标准化程度高、门店模型轻、复制速度快的品类特性。榜单本身还能做一个衍生分析用门槛最低的前几个品牌和门店数最高的前几个品牌做交集会发现走“低加盟费加高门店数”路径的品牌数量很可观。3.3 品类对比带误差线的柱状图与分面箱线图品类维度是分析中最有信息量的切面。原始数据里的品类字段五花八门光“餐饮”就有火锅、小吃、快餐、奶茶等多个分支我先把品类做了归一化合并再计算各品类的平均加盟费和投资预算中位数。按平均加盟费排序后发现教育类品牌的平均加盟费在十几万上下远高于餐饮类的五六万零售和美容美发类居中家政服务类偏低。呈现品类对比我用了带误差线的柱状图误差线代表标准差原因是平均数只反映中心位置加盟费离散程度才是真正有价值的信号。比如教育类平均加盟费虽高但细分方向从少儿编程到职业技能培训差异很大标准差也大柱子上方的误差线会明显很长而奶茶类平均加盟费低且标准差小说明这个品类里品牌定价比较接近。这个信息在业务决策里至关重要——光看平均数做决定容易忽略同一个品类内部的巨大波动。如果你想把品类内部结构看得更细可以换成按品类分面的箱线图每个品类画一张小箱线图对比中位数、四分位数和离群点判断同一品类里的价格分层。数据量不大的时候分面箱线图几乎可以把所有信息展示在同一张图里分析时自己看非常爽。3.4 投资金额与门店数交叉分析散点图的选型逻辑我最期待的是投资预算中位数和门店数的交叉关系。理论上高投入的品牌应该能支撑更快的扩张但实际情况是加盟市场极度分散资金门槛并不直接等于品牌规模。为了准确判断相关性我先对门店数取了以10为底的对数否则少数数千家门店的大品牌会占据整个横轴其余几百家门店的品牌全挤在左边变成一坨什么趋势都看不出来。取对数后散点的分布形状清晰了很多它们沿横轴比较均匀地铺开并没有出现明显的线性上升或下降趋势计算皮尔逊相关系数也只有0.2左右属于弱相关。这个结果本身就是一个有价值的结论。它说明加盟市场的品牌扩张更多取决于单店盈利模型、管理标准化和供应链能力而不是单纯的投资金额大小。低投入高规模与高投入低规模的品牌同时存在市场并没有遵循“钱多就能扩张”的逻辑。散点图解读时还有一个容易被忽略的点记得标记异常点。门店数上万但投资预算很低的品牌会让散点图出现一个角落里孤零零的点看着像趋势外溢其实是数据质量问题的产物。我在图上把这些异常点单独标为三角形并在注释里写明原因避免别人看图时误解结论。4. 实操过程从原始表格到分析图表4.1 环境准备与工具选型整个项目是在Python环境下完成的版本用的是3.9以上。核心库是pandas、matplotlib和seaborn这三个就足够了。pandas负责数据清洗和聚合matplotlib负责绘图seaborn用来提高图表的美观度。如果你想做交互式的图表可以引入pyecharts但做过对比之后我建议第一阶段先用静态图把分析逻辑跑通再上交互图表展示不然容易把时间花在调整交互效果上而忽略了分析本身。环境准备有几个容易出问题的点一是matplotlib的中文字体显示后面专门写一节排查二是seaborn版本和matplotlib版本要匹配不然出图风格会异常三是保存图表时建议用SVG格式矢量图在报告里放大或缩小都不会糊但文件体积稍大需要提交Word文档时再转成PNG。4.2 数据读取与清洗代码实录读取CSV并做初次清洗的代码我贴的是精简版实际项目里我把每一步都输出一次shape方便及时发现异常import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import re # 解决中文显示问题 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False df pd.read_csv(1637_join_data.csv, encodingutf-8-sig) df.columns [品牌名称, 品类, 加盟费, 保证金, 门店数, 投资预算, 开放区域] print(df.shape) print(df.dtypes)清洗函数我单独抽出来这样后续扩展字段时只需要补充解析函数不用改动主流程def parse_amount(s): 解析5.8万、面议、3-8万这类文本 if not isinstance(s, str): return None s s.replace(万, ).replace(, ).strip() if 面议 in s or s 无 or s : return None if - in s: nums re.findall(r\d\.?\d*, s) if len(nums) 2: return (float(nums[0]) float(nums[1])) / 2 nums re.findall(r\d\.?\d*, s) return float(nums[0]) if nums else None def parse_store(s): 解析350家、500、未知 if not isinstance(s, str): return None if 未知 in s or s : return None nums re.findall(r\d, s) return int(nums[-1]) if nums else None df[加盟费数值] df[加盟费].apply(parse_amount) df[保证金数值] df[保证金].apply(parse_amount) df[门店数数值] df[门店数].apply(parse_store) df[投资中位数] df[投资预算].apply(parse_amount) df[有加盟费数值] df[加盟费数值].notna()清洗结束后我建议统一检查四件事各字段缺失值数量、数值范围是否合理、品类字段的种类数、是否有整行重复数据。特别是重复品牌名平台偶尔会重复收录同一品牌的不同加盟分站需要按品牌名称加品类两列做去重否则后面的Top20排行会失真。4.3 核心分析与绘图代码画图之前先做聚合。加盟费分布图和箱线图代码fig, axes plt.subplots(1, 2, figsize(14, 5)) # 直方图 moneys df[加盟费数值].dropna() axes[0].hist(moneys, bins30, edgecolorwhite, color#4C72B0) axes[0].set_title(加盟费分布直方图) axes[0].set_xlabel(加盟费万元) axes[0].set_ylabel(品牌数量) # 箱线图 axes[1].boxplot(moneys, vertTrue) axes[1].set_title(加盟费箱线图) axes[1].set_ylabel(加盟费万元) plt.tight_layout() plt.show()门店规模Top20的排行图是一张横向条形图筛选逻辑是先按门店数数值排序取前20再反转顺序画图这样最大值的品牌正好出现在顶部top20 df.dropna(subset[门店数数值]).nlargest(20, 门店数数值) top20 top20.iloc[::-1] plt.figure(figsize(10, 10)) plt.barh(top20[品牌名称], top20[门店数数值], color#55A868) plt.xlabel(门店数量家) plt.title(品牌门店规模 Top20) plt.tight_layout() plt.show()品类对比柱状图和散点图的代码在seaborn里用一行就能实现不过我会先手动聚合数据后再用matplotlib画便于精确控制# 按品类聚合 cat_stats df.groupby(品类)[加盟费数值].agg([mean, std, count]) cat_stats cat_stats.sort_values(mean) plt.figure(figsize(12, 6)) plt.errorbar(cat_stats[mean], cat_stats.index, xerrcat_stats[std], fmto, color#C44E52, ecolor#C44E52, elinewidth2, capsize4) plt.xlabel(平均加盟费万元) plt.title(不同品类的平均加盟费与标准差对比) plt.tight_layout() plt.show()散点图部分取投资中位数对数或取门店数对数我的选择是对门店数取对数后画df[门店数对数] np.log10(df[门店数数值].where(df[门店数数值] 0)) plt.figure(figsize(10, 6)) sns.scatterplot(datadf, x投资中位数, y门店数对数, hue品类, alpha0.6) plt.xlabel(投资预算中位数万元) plt.ylabel(门店数量对数刻度) plt.title(投资预算与门店数量的交叉关系) plt.tight_layout() plt.show()4.4 图表结果的业务解读直方图和箱线图出图后第一个直观结论是加盟费分布严重右偏中位数大约在5万左右而平均数被少数高价品牌拉到了8万附近。只看平均值会误以为加盟门槛很高看中位数和四分位距才更接近真实情况。箱线图右侧延伸出的离群点基本都是教育类或品牌定位偏高的项目这也是后面品类对比的重要线索。Top20门店排行图显示门店数靠前的品牌大多都有低客单、标准化程度高的特点比如奶茶、快餐、小吃它们加盟费普遍不高靠规模效应盈利。榜单里如果要挑和品类交叉分析会发现零售和家政类虽然上榜少但单店投资并不低说明这几个品类的扩张速度受运营复杂度限制。品类对比的误差线柱状图是信息量最大、也最容易讲出观点的图。教育类平均加盟费约为餐饮类的三倍但标准差也大说明实际细分方向差异很大奶茶类平均加盟费低标准差小行业定价已经趋于透明。结合门店数据能看出奶茶类品牌在高门店数和低加盟费区间高度密集这是一个典型的高周转、低门槛、快速复制模型。交叉散点图则给出了一个更冷静的视角资金投入和门店规模之间没有明确的规律。即便只看品类颜色分布也很难画出一条清晰的分界线。这说明在加盟市场里选择比努力更重要——同样的资金预算进入不同品类、选择不同品牌规模发展路径可能完全不同。做可视化分析时发现没有线性关系并不是失败相反它和找到强相关一样都是一个值得写进报告的结论。5. 常见问题与排查技巧实录5.1 中文乱码和负号显示异常matplotlib画图时最常见的中文乱码问题必须在绘图前配置字体。Windows系统直接设置SimHei基本就行macOS和Linux需要换成对应的中文字体名称比如PingFang SC或Noto Sans CJK SC。另外还要设置axes.unicode_minus为False否则坐标轴上的负号会显示成方块。如果你用的是Jupyter Notebook还要记得在代码块前面加上%matplotlib inline否则图不会自动显示。字体问题还包括保存图片后字体被替换的问题特别是在无图形界面的服务器上跑脚本时默认字体列表里没有中文字体就会出现乱码。建议在项目开始时就把字体文件路径写死不要依赖系统默认配置。具体方式是import matplotlib font_path /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf font_prop matplotlib.font_manager.FontProperties(fnamefont_path) matplotlib.rcParams[font.family] font_prop.get_name()5.2 异常值到底该删还是该标数据里有个门店数过万的异常值一开始我差点直接把它当错误数据删掉后来查原始页面发现这个品牌确实曾经开出很多门店只是平台数据长期没更新。引入时间维度后这个异常点反而成了有价值的业务信息提醒我平台数据的时效性问题。处理异常值的原则我总结为三步。先看原始记录确认是录入错误还是真实数据再看它对分析结论的影响如果只是单独一个点不会改变整体分布就保留但标记如果会影响均值、相关系数这些统计量就分别计算保留和剔除两种情况下的结果看结论是否一致。如果一个异常点能颠覆整个结论说明数据集本身太脆弱不能依赖单条记录得结论。5.3 一图一结论避免信息过载刚开始做可视化时总喜欢把所有字段塞到一张图里比如用气泡图同时表达加盟费、保证金、投资、门店数四个维度画出来花里胡哨最后谁也看不出重点。后来我给自己定了一个规矩一张图只回答一个问题附带的信息最多不要太超过三个维度。散点图用颜色区分品类、气泡大小表示门店数已经是信息量上限了再叠加尺寸和形状就会让读图成本剧增。具体到这个小项目里分布图只回答“加盟费怎么分布”排行图只回答“谁是店最多的品牌”对比图只回答“品类之间有什么差异”散点图只回答“投资与规模有没有关系”。每张图配一句结论报告结构就非常干净。5.4 别把平台切片当成全行业数据这个认知误差是很多人会掉的坑。加盟网站的收录品牌受平台销售团队拓展范围、品牌付费意愿、品牌活跃度影响天然存在选择性偏差。它不代表所有加盟品牌的全集更不代表全国所有开店数据。你在平台上看到的某个品类平均加盟费是这么多不等于线下真实加盟市场就是这样一个分布只能说明这个平台上的公开报价大致如此。分析报告里要主动声明数据来源与收集时间并说明分析结论只适用于这个样本集。这个习惯在项目里也许看不出差别但在对外汇报或写结论时别人会因为你主动标注数据边界而更信任你的分析。不做这个声明一个较真的业务同事就能用半个反问推翻整张报告。还有一点加盟费和总投入是两个概念我在分析里反复提醒自己不混淆。加盟费往往只是授权费真正的开店总投入还包括设备、装修、首批进货、房租和流动资金这些字段在平台数据里没有。文章里讨论的“投资预算中位数”是一个综合参考值和加盟费不能画等号。在最终报告里我特意把这一点写在显眼位置避免读者只盯着加盟费做决策。这个项目做下来我个人最大的收获反而不是绘图技巧而是对“数据口径”这四个字的理解变深了。所有图表之所以能讲出逻辑是因为我在清洗阶段就把加盟费、门店数、投资预算这些字段的口径统一好了。很多数据分析项目做到最后图表是画出来了结论却经不起推敲问题通常不在画图这一步而是在前面字段定义的环节。后续如果想在这个项目上继续扩展可以考虑把开放区域字段拆到省份级别做空间分布图或者加入品牌备案年限、留言热度这些字段研究品牌生命周期和加盟热度的关系。数据源本身不复杂但顺着这个方向做下去能延展的分析话题还有很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑