ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python二手房数据分析:从数据清洗到房价预测全流程实战

Python二手房数据分析:从数据清洗到房价预测全流程实战 简介基于Python的二手房数据分析完整项目整合了源码、文档说明与PPT演示资料专门面向高校毕业设计、期末大作业及课程设计场景。项目覆盖房源数据获取、清洗、特征分析与可视化展示全流程代码中写入清晰注释适合初学者对照学习。压缩包共157个文件含65个PNG图表、18个Python脚本、18个CSV数据文件、15个HTML页面以及JavaScript交互脚本等各类型文件划分明确既可直接运行也便于二次开发整体大小48.05MB部署简单。内容中还提供多种CSV版本原始编码与清洗后数据便于比对数据处理前后差异。目前已有127人学习下载说明其具备实际参考价值。该项目经过严格调试系统功能完善、界面直观附带的文档说明与PPT资料能有效支撑答辩汇报是完成高分课程项目的高性价比选择。1. 开设这门课的功课不白做二手房数据分析项目到底能解决什么期末大作业、课程设计最怵的不是写代码而是选题和数据来源数据抓下来脏得没眼看代码写完了图表出不来PPT 更是凑字数。这份基于 Python 的二手房数据分析资源把最麻烦的两件事提前处理好了——原始 CSV 数据、清洗后的 CSV、完整源码、文档说明、答辩 PPT 全打包在一起拿到手不用折腾爬虫和反爬直接跑脚本就能出分析结果。项目本身是典型的“采集—清洗—分析—可视化—建模”五段式结构既有描述性统计也有房价预测导师问起来每个环节都有东西可讲。适合三类人期末大作业想要高分的学生、毕业设计需要一个能跑通全流程项目的应届生、以及想快速上手 pandas 和 matplotlib 的 Python 入门者。这套资源最值钱的地方不是代码量而是数据已经做了多版本处理省掉了最耗时的数据准备阶段。2. 数据集先摸透6 个 CSV 文件各自的定位与编码差异2.1 文件清单盘点origin、clean、20000 分别代表什么项目正文里的文件名反复出现去掉重复项后实际能用的数据文件有 6 个分为三组。第一组是ershoufang-origin-utf8.csv和ershoufang-origin-ansi.csv这两个是原始数据只是编码不一样一个 UTF-8 一个 ANSI即 GBK。第二组是ershoufang-clean-utf8-v1.1.csv和ershoufang-clean-ansi-v1.1.csv这两个是清洗后的数据v1.1 说明已经做过一轮版本迭代里面的空值、重复值、异常值基本都被处理过。第三组是ershoufang.csv和ershoufang - 20000.csv前者是基础版本后者是两万条规模的抽样数据做快速验证时用这个文件跑起来更快。拿到资源我建议先别急着打开代码而是用 pandas 把这 6 个文件的结构摸一遍。每个文件的行列数、字段名、有无空值这些信息决定了后面代码怎么改。原始文件和清洗文件对比着看还能直观感受“清洗到底做了什么”——这是答辩时很加分的细节导师问数据预处理做了什么你直接列前后对比就行。import pandas as pd # 先读原始编码的 UTF-8 版本 df_utf8 pd.read_csv(ershoufang-origin-utf8.csv, encodingutf-8) print(UTF-8 原始数据:, df_utf8.shape) print(df_utf8.head(3)) # 再读 ANSI 编码版本注意编码参数要切换 df_ansi pd.read_csv(ershoufang-origin-ansi.csv, encodinggbk) print(ANSI 原始数据:, df_ansi.shape) print(df_ansi.columns.tolist())shape返回的元组第一个值是行数第二个是列数columns.tolist()能直接看到所有字段名。这里的参数encoding是读取文本文件的解码方式UTF-8 和 GBK 是两种字符编码标准中国 Windows 系统导出的 CSV 经常是 GBK而 Linux 和 macOS 默认 UTF-8。如果不指定编码直接读大概率报UnicodeDecodeError这个错误在避坑章节还会专门说。2.2 字段结构分析二手房数据里到底存了哪些信息把表头打出来之后典型的二手房数据字段包括小区名称、所在区域、户型结构、建筑面积、单价、总价、朝向、装修情况、楼层、建筑年份、挂牌时间等。字段不算少但不是每个都能直接用于分析比如小区名称是文本类数据建模前必须转化挂牌时间跟房价的关系也不直观。所以第二步是对字段做类型梳理把连续型数值、离散型类别、时间类型分开。# 查看所有字段的类型和缺失情况 print(df_utf8.dtypes) print(每列缺失值数量) print(df_utf8.isnull().sum()) # 只看数值型字段的描述性统计 numeric_cols df_utf8.select_dtypes(include[float64, int64]).columns.tolist() print(数值字段, numeric_cols) print(df_utf8[numeric_cols].describe())dtypes能看出字段是整数、浮点数还是对象类型对象类型object就说明这一列大概率是文本。isnull().sum()统计每列空值空值太多的列要么删除要么填充这是清洗决策的依据。describe()给出均值、标准差、最小值、最大值这一眼就能发现异常——比如单价比平均值高出几十倍或者面积出现个位数这些都属于录入异常。2.3 清洗流程复现缺失值、重复值、异常值按什么顺序处理数据清洗是有顺序的我一般先处理重复值再处理缺失值最后处理异常值。顺序不能乱如果先填充缺失值再删重复填充那一步白白浪费计算如果先处理异常值再删重复删掉的行可能就是异常行工作量白做。清洗之后要和清洗前的行数、均值做对比记录“清洗了多少行、为什么清洗”这是答辩的重要素材。# 第一步去重 df_clean df_utf8.drop_duplicates() print(去重后行数, len(df_clean)) # 第二步填补缺失值数值列用中位数类别列用众数 for col in df_clean.columns: if df_clean[col].dtype in [float64, int64]: df_clean[col].fillna(df_clean[col].median(), inplaceTrue) else: df_clean[col].fillna(df_clean[col].mode()[0], inplaceTrue) # 第三步剔除异常值单价在 5000 到 150000 之外的一律删掉 df_clean df_clean[(df_clean[单价] 5000) (df_clean[单价] 150000)] print(清洗后数据规模, df_clean.shape) df_clean.to_csv(ershoufang_clean_my.csv, indexFalse, encodingutf-8)为什么数值列用中位数而不是均值因为均值容易被极端值拉高中位数对偏态分布更稳健。类别列用众数也就是出现最多的那个类别这是最不引入额外偏差的做法。单价的过滤区间是经验值——一线城市核心区挂牌价偶尔超过 15 万但所有城市平均下来还在合理范围低于 5000 的单价基本是录入错误或者非普通住宅。这段代码跑完你就得到了一份自己动手清洗过的数据和项目提供的 clean 版本可以互为对照作业里写“清洗工序”就有说服力了。2.4 编码兼容这条线UTF-8 和 ANSI 版本怎么选项目给了两种编码版本实际使用建议是在 Windows 上用 ANSI 版本因为用 Excel 打开和编辑非常顺滑在 Linux、macOS 或者服务器上跑脚本用 UTF-8 版本更稳。如果代码里统一用 pandas 读取其实两种编码都能处理关键是把encoding参数写对ANSI 在read_csv里对应的是gbk或者gb2312写错一个字母就报错。另外要注意ershoufang - 20000.csv这个文件名中间有个空格写代码引用路径时要么加上空格要么用os.rename把文件名规范化。文件名里的空格在命令行操作时容易被截断我用的一般是显式字符串所以影响不大但如果你要写 shell 脚本循环处理这个空格就是个隐患。3. 字段级分析与特征工程从哪些维度挖出房价真相3.1 哪些字段真正影响房价先做相关性初判字段梳理完不能直接建模得先看哪些字段和房价有真实关联。最简单有效的办法是算相关系数矩阵但相关系数只对数值型字段有意义类别字段要转成数值才能参与计算。这一步的逻辑是先整体看相关性再对重点字段做交叉分析最后决定建模时保留哪些特征。# 计算数值字段的相关系数矩阵 price_corr df_clean[[单价, 面积, 总价, 建筑年份]].corr() print(单价相关系数) print(price_corr[单价].sort_values(ascendingFalse))corr()默认用的是皮尔逊相关系数取值在 -1 到 1 之间。正的越大说明正相关越强负的越大说明负相关越强。从实际数据看单价和面积往往会呈弱负相关——面积大的房子单价偏低这是房地产市场里常见的“面积越大折扣越大”现象。建筑年份通常和单价正相关次新小区的单价普遍比老破小高。如果某个字段相关系数接近 0说明它和单价没有线性关系建模时可以考虑舍弃。3.2 单价、总价、面积三个基础特征的交叉规律单价和总价是同一件事的两种视角总价等于单价乘以面积但单价反映的是房屋的档次和地段溢价总价反映的是购房门槛。分析这两个字段的分布能看到市场结构的核心规律。我一般会先按区域分组看看每个区的平均单价和平均总价然后再叠加面积维度观察不同面积段的单价趋势。# 按区域分组计算单价、总价、面积的均值和中位数 area_stats df_clean.groupby(区域).agg( 平均单价(单价, mean), 单价值格中位(单价, median), 平均总价(总价, mean), 平均面积(面积, mean), 房源数量(单价, count) ).reset_index() # 按平均单价降序排列方便一眼看出哪个区最贵 area_stats area_stats.sort_values(平均单价, ascendingFalse) print(area_stats.head(10))groupby(区域)是按区域分组的核心操作agg里面的每个字段都指定了聚合函数mean是求均值median是取中位数count是统计数量。区域维度是最值得做的一张表因为地段是房价最核心的因子这份分析放进 PPT 里导师一眼就能看出你在思考市场逻辑而不是单纯跑代码。3.3 特征工程把朝向、装修、楼层这些文本变成数值建模阶段算法不认识“南北通透”“精装”这种中文文本必须转成数值。有三类处理方式无序类别用 one-hot 编码有序类别用标签编码时间字段做差值。朝向没有好坏顺序适合 one-hot装修程度有简装、普通、精装、豪装的递进关系适合标签编码建筑年份应该转化成房龄也就是当前年份减去建筑年份。from sklearn.preprocessing import LabelEncoder # 朝向转 one-hot df_model pd.get_dummies(df_clean, columns[朝向], prefix朝) # 装修程度转标签编码顺序简装0、普通1、精装2、豪装3 le LabelEncoder() df_model[装修等级] le.fit_transform(df_clean[装修情况]) # 房龄 当前年份 - 建筑年份 from datetime import datetime current_year datetime.now().year df_model[房龄] current_year - df_model[建筑年份] # 楼层作为类别处理低/中/高用标签编码 df_model[楼层等级] le.fit_transform(df_clean[楼层]) print(df_model[[装修等级, 房龄, 楼层等级]].head())pd.get_dummies会把“朝向”这一列拆成多个 0/1 列比如“朝南”一列、“朝北”一列房子是哪个朝向对应位置就是 1。LabelEncoder把文本标签按字母序转成整数注意它转出来的顺序不一定是业务顺序所以如果类别之间有明确的递进关系比如装修等级我建议手写映射字典而不是用fit_transform这样可控性更高。3.4 描述性统计与分组聚合先讲清楚数据再谈模型不少学生一上来就跑模型结果模型效果差也不知道怎么解释。正确的顺序是先把描述性统计做扎实整体单价分布什么样、哪个区间房源最多、哪个区最贵、面积和房龄的分布是否合理。这些结论本身就是期末大作业的核心成果——数据分析类项目分析过程的价值大于模型精度。# 单价分布的分位数 price_bins df_clean[单价].quantile([0.1, 0.25, 0.5, 0.75, 0.9]) print(单价十分位/四分位) print(price_bins) # 面积分箱看各面积段的房源数量 df_clean[面积段] pd.cut(df_clean[面积], bins[0, 50, 90, 120, 150, 999], labels[50平以下, 50-90, 90-120, 120-150, 150以上]) area_seg df_clean.groupby(面积段, observedTrue)[单价].agg([count, mean]) print(area_seg)quantile直接给出分位数的价格看看中位数和均值差多少差很多说明市场被高端房源拉高了。pd.cut把连续的面积切成离散的段分箱边界自己定0-50 是刚需小户型、50-90 是一般刚需、90-120 是改善型、150 以上是豪宅线这种分箱方式符合房地产市场的主流认知。observed参数是为了控制分组时 category 类型的显示避免出现空组警告。4. 可视化想把分析结果变成能直接交作业的图4.1 图表选型什么分析配什么图数据分析的可视化不是把所有图都画一遍而是每张图回答一个问题。我的选型习惯是单价分布用直方图加核密度曲线区域价格对比用横向柱状图面积与单价的关系用散点图加回归线朝向占比用饼图价格随房龄的变化用折线图。如果数据集包含经纬度可以加一张区域价格地图但那是加分项基础的六张图已经足够支撑一份课设报告。图表选型的原则是“一图一结论”一张图讲一个故事。柱状图适合对比不同类别的均值散点图适合看两个连续变量的关系直方图适合看单个变量的分布形态。Python 生态里 matplotlib 是底层库seaborn 在统计图表上更省事pyecharts 做交互式 HTML 图表效果最好。我建议三个结合着用报告里的静态图用 matplotlib 和 seaborn答辩现场演示的交互图用 pyecharts。4.2 中文字体配置与图表美化matplotlib 不显示中文的解法matplotlib 默认字体不支持中文直接画图会出现一个一个小方框。解决办法是设置中文字体Windows 上用 SimHei 或者 Microsoft YaHeimacOS 上用 PingFang SCLinux 服务器上用文泉驿或者思源黑体。字体设置必须放在画图代码之前否则不生效。import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体Linux 可以用 Noto Sans CJK SC plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, WenQuanYi Zen Hei] plt.rcParams[axes.unicode_minus] False # 解决负号显示异常 # 区域平均单价横向柱状图 fig, ax plt.subplots(figsize(10, 6)) top10 area_stats.head(10) ax.barh(top10[区域], top10[平均单价], color#4C72B0) ax.set_xlabel(平均单价元/平方米) ax.set_title(各区域平均单价对比 TOP10) ax.invert_yaxis() # 让数值最大的显示在最上面 plt.tight_layout() plt.savefig(区域单价对比.png, dpi150) plt.show()rcParams[font.sans-serif]传入的是一个字体列表matplotlib 会从左往右找第一个可用的字体所以把 SimHei 写在第一位Linux 上没有 SimHei 时就会自动落到 WenQuanYi。axes.unicode_minus设置为 False 是因为默认的负号是 Unicode 字符经常显示成方框。savefig的dpi参数控制输出分辨率150 足够论文打印用答辩投屏 150 以上更清晰。4.3 交互式图表用 pyecharts 做可以缩放、悬停的地图和图表pyecharts 的优点是交互性好鼠标悬停能看到具体数值图表还能缩放拖动演示效果比静态图好很多。做区域价格地图时需要安装echarts-countries-pypkg等地图包pyecharts 从 2.0 开始地图包和主库分离这一点经常有人在 requirements 里漏掉导致运行报错。from pyecharts.charts import Bar from pyecharts import options as opts # 区域单价对比交互柱状图 bar ( Bar() .add_xaxis(top10[区域].tolist()) .add_yaxis(平均单价, top10[平均单价].round(0).tolist()) .set_global_opts( title_optsopts.TitleOpts(title各区域平均单价 TOP10), yaxis_optsopts.AxisOpts(name平均单价元) ) ) bar.render(区域单价交互图.html)render生成的 HTML 文件浏览器直接打开就能交互不需要启动服务器。pyecharts 的写法是链式调用每个点号后面的.set_global_opts是设置图表的全局配置包括标题、坐标轴名称、图例等。这个 HTML 文件可以直接嵌入 PPT 的演示环节比贴静态图显得工作量满。4.4 可视化合成一张大图答辩 PPT 的图表组织思路PPT 里放图表不是越多越好而是按叙事逻辑排。我的建议是三张固定核心图第一张是单价分布直方图说明整体市场水平第二张是区域对比条形图说明地段分化第三张是面积与单价散点图说明产品结构。这三张图分别对应“市场整体什么样、哪里贵、为什么贵”然后接上模型部分的预测效果图整套 PPT 的逻辑就闭合了。图表配色统一用一个色系的别一张蓝一张红一张绿视觉上会显得杂乱。5. 常见的环境与数据坑现象、原因、解决一次说清5.1 pytest 读 CSV 报 UnicodeDecodeError编码写错一个字符就挂现象pd.read_csv(ershoufang-origin-ansi.csv)直接报错提示UnicodeDecodeError: utf-8 codec cant decode byte 0xc4。原因这个文件是 ANSI 编码也就是 GBKpandas 默认用 UTF-8 去解码遇到中文字节序列就崩了。解决显式指定encodinggbk如果还有个别字符解析不了可以加errorsignore忽略无法解码的字节。df pd.read_csv(ershoufang-origin-ansi.csv, encodinggbk, errorsignore)errorsignore是兜底方案非必要不建议用因为它会静默吞掉无效字符可能导致个别字段内容被截断。更稳妥的做法是用encoding_errorsreplace把无法解码的字符替换成特殊符号这样能定位到具体是哪一行数据有问题。5.2 数据量对不上为什么清洗后行数少了很多现象原始文件和清洗后文件的行数差了几千行代码没报错但结果对不上。原因原始数据里含有大量重复录入的房源同一套房源被不同中介重复挂牌去重之后自然就少了。另外ershoufang - 20000.csv是抽样文件只有两万行原始文件可能更多拿抽样文件跑出的统计量和全量对比必然有偏差。解决明确每个文件的定位全量分析用 origin 文件清洗后的版本快速验证用 20000 条抽样但报告里必须注明数据规模和来源避免导师质疑数据真实性。5.3 图表的汉字全变方框matplotlib 字体问题现象plt.title(单价分布)画出来标题是四个方框图中其他中文全部乱码。原因matplotlib 的默认字体 DejaVu Sans 不含中文字符集。解决按 4.2 节配置plt.rcParams[font.sans-serif]配置后清除 matplotlib 缓存看效果。如果设置了字体列表还是不生效就用fm.fontManager.addfont(/usr/share/fonts/xxx.ttf)直接指定字体文件路径。5.4 建模时 One-Hot 编码产生警告稀疏矩阵内存疯长现象pd.get_dummies执行后内存占用暴涨甚至把 Jupyter Notebook 卡死。原因朝向、区域这类高基数类别字段拆出的列太多比如区域有十几个朝向有八种组合出来几十列数据量一大内存就顶不住。解决对高基数字段先做频次合并把出现次数极少的类别全部合并为“其他”再编码。# 低频朝向全部合并为“其他” top_directions df_clean[朝向].value_counts().head(5).index.tolist() df_clean[朝向合并] df_clean[朝向].apply( lambda x: x if x in top_directions else 其他 )value_counts().head(5)取出现次数最多的前五个朝向剩下的全部标记为“其他”。这样编码后的列数固定模型稳定而且频繁出现的类别本身更有统计意义冷门类别作为独立特征反而容易让模型过拟合。5.5 房价预测结果全部趋向均值模型欠拟合的信号现象线性回归预测出来的房价几乎都在均值附近波动完全拟合不出高价位房源。原因房价是高度非线性的简单线性模型表达能力不够。解决换用树模型比如随机森林或者梯度提升树这类模型可以捕获特征交互和非线性关系。如果坚持用线性回归至少要在特征工程里加入面积平方项、区域与面积的交互特征让模型有更多表达能力。6. 部署与验收技巧15 分钟跑通全流程并准备好答辩演示拿到资源第一件事不是读代码而是按顺序复现整个流程。我的习惯是新建一个干净的虚拟环境依次装依赖按“原始数据→清洗→分析→可视化→建模”五步跑一遍。跑通之后把每一步的中间产物保存下来清洗后的 CSV、生成的图片、训练好的模型全都放独立文件夹答辩时按文件夹顺序展示评审视角里这叫“工程规范”。python -m venv venv source venv/bin/activate pip install pandas numpy matplotlib seaborn scikit-learn pyecharts jupyter python 01_data_clean.py python 02_analysis.py python 03_visualization.py python 04_model.py虚拟环境隔离 Python 版本和依赖避免和系统环境冲突。四个脚本按编号执行数字开头保证执行顺序一目了然。每一步脚本里建议加print输出关键结果终端里能看到进度答辩翻车了也能快速定位是哪一步的错。答辩演示的顺序我推荐这样组织先讲数据和清洗展示清洗前后的对比表说明你处理了多少脏数据再讲描述性分析和可视化把区域价格差异的结论亮出来最后讲建模讲清楚用了什么算法、分了多少训练集测试集、预测误差有多少。不要一上来就甩模型先让评委建立“你确实理解数据”的信任感。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, r2_score # 假设 df_model 已经完成了特征编码 feature_cols [c for c in df_model.columns if c not in [总价, 单价, 小区, 面积段]] X df_model[feature_cols].select_dtypes(include[number]) y df_model[单价] # 训练测试集划分随机种子固定保证结果可复现 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model RandomForestRegressor(n_estimators200, max_depth12, random_state42) model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, r2_score(y_test, y_pred)) print(平均绝对误差:, mean_absolute_error(y_test, y_pred))RandomForestRegressor里n_estimators是树的数量200 棵足够收敛max_depth限制单棵树深度12 层既能捕获非线性又不容易过拟合。random_state42让每次运行结果一致这是答辩时最容易被追问的点——加了这个参数任何人都能复现你的结果。mean_absolute_error告诉你平均预测偏差多少元每平米这个数字要记下来答辩时能报出来说明你真的跑过模型。从那次期末大作业之后我每次做数据分析项目都强制自己走一遍完整流程——先摸数据、再清洗、后建模每一步留下中间产物绝不跳步。这次拆这份二手房资源又验证了一遍数据预处理的时间和建模时间五五开是常态项目里已经帮你处理好的部分要善用但自己亲手跑一遍清洗工序更有价值。希望帮到你把这份资源变成答辩时能自信拿出手的作品。本文还有配套的精品资源点击获取
返回列表