首页 / 资讯中心 / 文章详情

二手房数据分析项目实战:从脏CSV到可视化结论的完整链路

二手房数据分析项目实战:从脏CSV到可视化结论的完整链路 ★ FEATURED ARTICLE
简介这是一套面向高校学生与Python初学者的二手房数据分析完整项目资料适用于毕业设计、期末大作业与课程设计等高分场景。资源包含完整源码、文档说明与PPT资料代码注释详尽新手也能快速理解并部署运行。压缩包共157个文件约48.05MB其中18个py脚本承载数据清洗与分析逻辑18个csv文件提供原始及清洗后的二手房数据15个html与11个js构成可视化展示界面另有65个png截图、docx文档与pptx演示文稿辅助说明整体结构清晰、便于按模块查阅。目前已有127人学习下载。项目经过严格调试可直接运行读者能获得一套可复用的数据分析流程、清晰的目录组织与配套讲解材料既能作为课程作业提交也能作为数据分析入门练手案例具有较高的实际应用与参考价值。1. 二手房数据分析项目从一堆脏 CSV 到能讲清楚的结论中间差了什么二手房数据分析这个方向很多人第一次接触是在课程设计或者毕业设计里。拿到一份从链家或安居客爬下来的 CSV打开一看字段乱七八糟价格有的写「暂无数据」面积带「㎡」后缀楼层写成「中楼层/共18层」小区名字里还夹着空格和括号。直接df.describe()跑一下出来的均值毫无意义。这个项目标题里的「完整源码文档说明PPT资料」本质上要交付的不是一个模型多牛的预测系统而是一条从原始数据到可视化结论的完整链路外加能拿去答辩的材料。它适合两类人一类是刚学完 pandas 和 matplotlib、需要一个完整项目练手的入门者另一类是要交课程设计、需要一套能跑通且讲得出逻辑的代码和文档的人。核心难点不在算法在数据清洗和字段工程这部分做扎实了后面的分析和图表才有说服力。2. 数据获取与清洗二手房 CSV 里那些必须处理的脏字段2.1 数据从哪来、字段长什么样常见做法是用 requests BeautifulSoup 抓取公开房源列表页或者直接用现成的 CSV 数据集。不管哪种来源二手房数据的字段通常包括小区名称、户型、面积、朝向、楼层、装修情况、挂牌价、单价、区域、关注人数等。这里有个血泪经验不同城市、不同时间抓下来的字段名和格式差异很大不要指望一套清洗脚本通吃。我一般会先做一件事——把原始 CSV 用pd.read_csv读进来打印df.columns和df.head(20)肉眼确认每个字段的实际内容长什么样再决定怎么处理。import pandas as pd import numpy as np # 读取原始数据注意编码问题二手房数据常见 utf-8 和 gbk 两种 df pd.read_csv(ershoufang_raw.csv, encodingutf-8) # 先看字段名和前几行确认实际内容 print(df.columns.tolist()) print(df.head(20).to_string()) # 查看每列缺失情况 print(df.isnull().sum()) print(df.dtypes)这段代码的作用是先摸清数据底细。encoding参数很关键如果报UnicodeDecodeError换成gbk或gb18030再试。head(20)比head()更有用因为前 5 行可能是表头残留或者特殊样本看 20 行能发现更多格式问题。isnull().sum()让你知道哪些列缺失严重如果某一列缺失超过 60%考虑直接丢弃而不是硬填。2.2 价格、面积、楼层字段的清洗逻辑二手房数据里最典型的脏字段是价格和面积。价格常见形式有「350万」「350」「暂无数据」「暂无报价」面积常见「89.5㎡」「89.5平米」「89.5」。楼层更麻烦「中楼层/共18层」「低楼层/共6层」「地下室」都有。清洗思路是先用正则提取数字再统一单位最后处理异常值。import re def parse_price(x): 提取价格数字统一为万元 if pd.isna(x): return np.nan x str(x) # 匹配数字可能带小数点 match re.search(r(\d\.?\d*), x) if match: return float(match.group(1)) return np.nan def parse_area(x): 提取面积数字统一为平方米 if pd.isna(x): return np.nan x str(x) match re.search(r(\d\.?\d*), x) if match: return float(match.group(1)) return np.nan def parse_floor(x): 解析楼层返回(所在层, 总层数) if pd.isna(x): return np.nan, np.nan x str(x) # 匹配「中楼层/共18层」这类格式 match re.search(r(\d).*?共(\d)层, x) if match: return int(match.group(1)), int(match.group(2)) # 匹配「中楼层」这类没有具体数字的 if 低 in x: return 1, np.nan if 中 in x: return 2, np.nan if 高 in x: return 3, np.nan return np.nan, np.nan # 应用清洗函数 df[price_wan] df[价格].apply(parse_price) df[area_sqm] df[面积].apply(parse_area) df[[floor_level, floor_total]] df[楼层].apply( lambda x: pd.Series(parse_floor(x)) ) # 计算单价元/平米 df[unit_price] df[price_wan] * 10000 / df[area_sqm] # 查看清洗后的统计信息 print(df[[price_wan, area_sqm, unit_price]].describe())这里有几个参数和逻辑需要说明。parse_price用正则(\d\.?\d*)匹配第一个数字能处理「350万」「350.5万」「350」等情况。parse_area同理。parse_floor稍微复杂先尝试匹配「数字/共数字层」的格式匹配不到再根据「低/中/高」给一个粗略的层级编码。unit_price的计算要注意单位换算价格是万元面积是平方米乘以 10000 后得到元/平米。describe()跑完后重点看 min 和 max如果单价出现几百或者几十万说明有异常值需要进一步处理。2.3 异常值处理与区域字段标准化清洗完数字字段后下一步是处理异常值和标准化分类字段。单价低于 3000 或高于 50000 的记录大概率是数据错误或者特殊房源比如车位、商铺混进来了建议直接过滤掉。区域字段常见问题是有空格、有「区」和没「区」混用、有「其他」这种无效分类。# 过滤异常单价 df df[(df[unit_price] 3000) (df[unit_price] 50000)] # 区域字段标准化去空格、统一后缀 df[区域] df[区域].str.strip() df[区域] df[区域].str.replace(区, , regexFalse) df[区域] df[区域].replace(, np.nan) # 丢弃区域为空的记录 df df.dropna(subset[区域, price_wan, area_sqm]) # 按区域统计房源数量和均价 area_stats df.groupby(区域).agg( 房源数(price_wan, count), 均价万(price_wan, mean), 均价单价(unit_price, mean) ).sort_values(房源数, ascendingFalse) print(area_stats)这段代码里str.strip()去掉首尾空格str.replace(区, )把「朝阳区」变成「朝阳」方便后续统一展示。dropna只保留关键字段完整的记录。groupby后的agg用字典形式指定不同列的不同聚合方式这是 pandas 里很实用的写法。跑完这一步你就能看到每个区域的房源数量和均价分布这是后续做可视化分析的基础。提示清洗过程中每一步都建议把中间结果存一份 CSV比如df.to_csv(ershoufang_cleaned.csv, indexFalse)方便回溯和对比。不要在一个脚本里从头跑到尾不落盘出了问题很难定位。3. 分析建模与可视化用 pandas 和 matplotlib 做出能放PPT的图表3.1 户型、面积、价格的分布分析数据清洗完之后核心分析目标通常是回答几个问题这个城市二手房的价格分布是什么样的哪些区域最贵户型面积和价格有什么关系装修程度对单价影响大吗这些问题用 pandas 的 groupby 和 matplotlib 的直方图、箱线图就能回答。import matplotlib.pyplot as plt import matplotlib # 设置中文字体Windows 和 Mac 路径不同 matplotlib.rcParams[font.sans-serif] [SimHei] # Windows # matplotlib.rcParams[font.sans-serif] [Arial Unicode MS] # Mac matplotlib.rcParams[axes.unicode_minus] False # 价格分布直方图 fig, axes plt.subplots(1, 2, figsize(14, 5)) axes[0].hist(df[price_wan], bins50, edgecolorblack, alpha0.7) axes[0].set_xlabel(总价万元) axes[0].set_ylabel(房源数量) axes[0].set_title(二手房总价分布) axes[1].hist(df[area_sqm], bins50, edgecolorblack, alpha0.7, colororange) axes[1].set_xlabel(面积平方米) axes[1].set_ylabel(房源数量) axes[1].set_title(二手房面积分布) plt.tight_layout() plt.savefig(price_area_dist.png, dpi150) plt.show()中文字体设置是 matplotlib 最常见的翻车点。Windows 用SimHeiMac 用Arial Unicode MSLinux 如果没有中文字体需要先安装再指定。axes.unicode_minus False解决负号显示为方块的问题。bins50是直方图的分箱数数据量大时可以适当增加数据量小就减少。dpi150保证保存的图片在 PPT 里清晰度够用。3.2 区域均价对比与户型分析区域均价对比用条形图最直观户型分析可以用分组统计加箱线图。这里的关键是把数据聚合好再交给绘图函数。# 区域均价条形图 area_price df.groupby(区域)[unit_price].mean().sort_values(ascendingFalse) fig, ax plt.subplots(figsize(12, 6)) area_price.plot(kindbar, axax, colorsteelblue, edgecolorblack) ax.set_xlabel(区域) ax.set_ylabel(均价元/平方米) ax.set_title(各区域二手房均价对比) plt.xticks(rotation45) plt.tight_layout() plt.savefig(area_price_bar.png, dpi150) plt.show() # 户型统计提取「室」的数量 def parse_room_type(x): if pd.isna(x): return np.nan match re.search(r(\d)室, str(x)) if match: return int(match.group(1)) return np.nan df[room_count] df[户型].apply(parse_room_type) # 按室数统计均价 room_stats df.groupby(room_count)[unit_price].agg([mean, count]) print(room_stats) # 箱线图展示不同室数的单价分布 df.boxplot(columnunit_price, byroom_count, figsize(10, 6)) plt.suptitle() plt.title(不同室数二手房单价分布) plt.xlabel(室数) plt.ylabel(单价元/平方米) plt.savefig(room_price_box.png, dpi150) plt.show()parse_room_type从「3室2厅」这类字符串里提取室数。groupby(room_count)后同时看均值和数量避免某个室数只有一两条记录导致均值失真。boxplot的by参数按室数分组plt.suptitle()去掉 pandas 自动加的标题再用plt.title设置自己的标题。箱线图能看出中位数、四分位距和异常值比单纯看均值更有信息量。3.3 用 seaborn 做相关性热力图如果想看多个数值字段之间的相关性seaborn 的 heatmap 是最省事的。二手房数据里面积、总价、单价、室数、楼层这些字段之间往往有相关性热力图能一眼看出来。import seaborn as sns # 选择数值字段 numeric_cols [price_wan, area_sqm, unit_price, room_count, floor_total] corr_df df[numeric_cols].dropna() # 计算相关系数矩阵 corr_matrix corr_df.corr() # 绘制热力图 plt.figure(figsize(8, 6)) sns.heatmap(corr_matrix, annotTrue, cmapcoolwarm, fmt.2f, squareTrue, linewidths0.5) plt.title(二手房数值字段相关性热力图) plt.tight_layout() plt.savefig(correlation_heatmap.png, dpi150) plt.show()annotTrue在格子里显示数值fmt.2f保留两位小数cmapcoolwarm用冷暖色区分正负相关squareTrue让格子是正方形。跑完这张图你能看到面积和总价通常高度正相关单价和总价的相关性取决于面积分布室数和面积也有一定关联。这些结论写进文档和 PPT 里比单纯罗列数字有说服力。4. 避坑与排查二手房数据分析项目里最容易翻车的 5 个地方4.1 编码报错导致数据读不进来现象pd.read_csv直接抛UnicodeDecodeError或者读进来中文全是乱码。原因二手房数据来源多样有的用 utf-8有的用 gbk还有的用 gb18030。解决先试encodingutf-8报错就换gbk再不行试gb18030。如果还是不行用chardet检测编码。import chardet with open(ershoufang_raw.csv, rb) as f: raw f.read(10000) result chardet.detect(raw) print(result) # {encoding: GB2312, confidence: 0.99}拿到编码后pd.read_csv(ershoufang_raw.csv, encodingresult[encoding])即可。4.2 正则提取数字时把年份、楼层号也提出来了现象价格字段清洗后出现 18、2023 这种明显不对的值。原因正则(\d)太宽泛把「共18层」里的 18 或者「2023年建」里的 2023 也匹配进去了。解决把正则写得更具体比如价格匹配(\d\.?\d*)万面积匹配(\d\.?\d*)㎡或(\d\.?\d*)平米先限定单位再提取数字。4.3 中文字体不生效导致图表全是方块现象matplotlib 图表里中文显示为方框。原因默认字体不含中文字形或者指定的字体系统里没有。解决Windows 用SimHeiMac 用Arial Unicode MSLinux 先fc-list :langzh看有哪些中文字体再指定。如果都没有下载一个开源中文字体放到 matplotlib 的字体目录再清除缓存。4.4 groupby 后均值被极端值拉偏现象某个区域的均价明显不合理比其他区域高好几倍。原因该区域有几条单价异常高的记录比如别墅或者数据录入错误。解决在 groupby 之前先做分位数过滤比如只保留单价在 1% 到 99% 分位数之间的记录。q1 df[unit_price].quantile(0.01) q99 df[unit_price].quantile(0.99) df df[(df[unit_price] q1) (df[unit_price] q99)]4.5 保存图片时空白或分辨率太低现象plt.savefig保存的图片打开是空白或者放到 PPT 里模糊。原因savefig在show之后调用或者dpi太低。解决先savefig再showdpi至少设 150PPT 用建议 200 以上。另外bbox_inchestight可以去掉多余白边。5. 文档与 PPT 的产出技巧让代码之外的部分也站得住5.1 文档说明该写什么、不该写什么文档说明不是把代码注释复制一遍。我一般会按这个结构写项目背景一句话说清楚数据来源和分析目标环境依赖列出 Python 版本和关键库版本数据字段说明用表格列出原始字段名、含义、清洗后字段名分析流程按清洗、统计、可视化的顺序写每一步配一张关键截图最后附上运行方式和常见问题。不要写大段代码解释代码本身有注释文档里只写「为什么这么做」和「结果怎么看」。文档章节内容要点常见问题项目背景数据来源、分析目标、适用场景写成论文摘要太长环境依赖Python 版本、pandas/matplotlib/seaborn 版本不写版本号别人跑不起来字段说明原始字段、清洗后字段、处理逻辑只列字段名不解释含义分析流程每步操作、关键代码片段、结果截图贴大段代码没有结果运行说明命令、参数、预期输出假设别人环境和你一样5.2 PPT 资料的组织逻辑PPT 资料的核心是「让不懂代码的人也能看懂你做了什么」。建议 10 到 15 页结构如下封面、项目背景与目标、数据来源与字段说明、数据清洗前后对比放两张截图、关键分析结论3 到 5 张图表、代码结构概览目录树截图、运行演示终端截图、总结与不足。图表不要直接截 matplotlib 的默认样式把标题、坐标轴标签、图例都调好再导出。每张图表配一句话结论比如「XX 区域均价最高达到 X 元/平米比最低区域高 X%」。5.3 源码目录结构建议一个能拿得出手的二手房数据分析项目目录结构应该清晰到别人打开就知道从哪跑。ershoufang_analysis/ ├── data/ │ ├── raw/ # 原始 CSV │ └── cleaned/ # 清洗后 CSV ├── src/ │ ├── clean.py # 清洗脚本 │ ├── analyze.py # 分析脚本 │ └── visualize.py # 可视化脚本 ├── output/ │ ├── figures/ # 图表 PNG │ └── reports/ # 统计结果 CSV ├── docs/ │ └── 文档说明.md ├── ppt/ │ └── 答辩PPT.pptx ├── requirements.txt └── README.mdrequirements.txt里写清楚依赖和版本README.md写运行顺序先clean.py再analyze.py最后visualize.py。每个脚本的入口用if __name__ __main__:包起来方便单独运行也方便被导入。5.4 一个容易被忽略的细节随机种子和可复现性如果分析里用了任何随机操作比如抽样、聚类初始化一定要设随机种子。np.random.seed(42)或者random_state42。不然别人跑出来的结果和你不一样答辩时被问到会很被动。另外清洗后的数据存一份到data/cleaned/分析脚本直接读清洗后的数据不要每次从原始数据重新跑既慢又容易因为原始数据变动导致结果不一致。5.5 进阶技巧用 pyecharts 做交互式图表如果想让 PPT 或文档里的图表更有交互感可以用 pyecharts 生成 HTML 图表截图后放进 PPT或者直接演示 HTML 文件。pyecharts 的 API 和 matplotlib 不同但做地图、漏斗图、动态折线图更方便。比如区域均价可以用地图展示户型分布可以用饼图。from pyecharts.charts import Bar, Map from pyecharts import options as opts # 区域均价条形图 bar ( Bar() .add_xaxis(area_price.index.tolist()) .add_yaxis(均价元/平米, area_price.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title各区域二手房均价), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate45)), ) ) bar.render(area_price_bar.html)add_xaxis和add_yaxis分别设置横轴和纵轴数据set_global_opts里配置标题和横轴标签旋转角度。render生成 HTML 文件浏览器打开就能看到交互效果。这个技巧在答辩时很加分但注意不要为了炫技而用图表的核心还是信息传达。注意pyecharts 生成的 HTML 依赖在线 CDN 加载 JS 资源如果演示环境没有网络需要提前把资源下载到本地并修改引用路径否则图表加载不出来。5.6 我踩过的一个坑清洗脚本写得太「聪明」最后说一个我自己的教训。早期写清洗脚本时我喜欢把所有逻辑塞进一个函数用一堆 if-else 处理各种边界情况。结果数据源一换整个函数就崩了排查起来极其痛苦。后来我改成每个字段一个独立函数函数里只做一件事异常情况返回 NaN 而不是抛异常然后在主流程里统一处理缺失值。这样虽然代码看起来「笨」一点但可维护性和可调试性好太多。二手房数据分析这个项目数据清洗占 60% 以上的工作量把这部分写扎实后面的分析和可视化就是水到渠成的事。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站