简介:本资源是一套完整的Python数据分析实战项目,面向计算机、统计学或房地产相关专业的本科生及自学开发者,聚焦南京二手房市场数据采集、清洗、可视化与聚类建模全流程,适用于毕业设计、课程大作业或数据分析能力进阶训练。压缩包共157个文件(40.04MB),包含18个核心Python脚本(爬虫、清洗、绘图、聚类)、18个CSV数据文件(含原始与清洗后多版本)、65张可视化图表PNG、15个HTML交互地图页面(集成高德JS API)、11个JS前端支持文件及1份完整答辩用PPT文档。已有997人学习下载,内容覆盖Requests+BeautifulSoup网络爬虫、Pandas数据清洗、Matplotlib/Seaborn可视化、KMeans聚类算法实现与地理热力图呈现等关键技术点,所有代码可直接运行,数据字段明确,分析逻辑清晰,附带多阶段中间结果文件便于调试与复现。
1. 项目缘起:从数据到洞察,一次完整的二手房分析实战
最近在整理本地房产数据时,我手头正好有一批南京的二手房挂牌信息。这些数据躺在Excel表格里,密密麻麻的数字和地址,除了能看出总价和单价,很难直观地感受到市场的全貌。比如,哪个区的房源最集中?不同区域的单价差异到底有多大?总价和面积的关系是怎样的?这些问题,单看原始数据表格,效率很低。作为一个习惯用Python解决实际问题的开发者,我决定动手把这些数据“可视化”,让它自己“说话”。这个项目,就是一次从原始数据清洗、到分析、再到最终生成可视化图表和汇报PPT的完整流程实践。它不仅是一堆代码,更是一套可复用的方法论,适用于任何有类似结构化数据的分析场景。无论你是数据分析的初学者,想通过一个实战项目练手,还是业务人员,希望掌握用Python快速洞察数据的能力,这篇内容都能给你提供一条清晰的路径。我会把核心思路、关键代码、踩过的坑以及最终的成果(包括源码和PPT模板)都梳理出来,你可以直接拿去,换成你自己的数据跑一遍。
2. 数据获取与预处理:清洗是分析的第一步
任何数据分析项目,数据质量决定了天花板的高度。我们拿到的原始数据往往存在缺失、重复、格式不一致等问题,直接进行分析会导致结果失真。
2.1 数据来源与字段理解
我使用的数据包含了南京各个行政区的二手房挂牌信息,典型字段包括:
- 区域:如鼓楼、建邺、秦淮、玄武等。
- 小区名称:房源所在的具体小区。
- 户型:如“3室2厅”、“2室1厅”等字符串。
- 面积:单位为平方米,通常是浮点数。
- 朝向:如“南”、“南北通透”等。
- 装修情况:如“精装”、“简装”、“毛坯”。
- 楼层信息:包含楼层和总楼层,如“中楼层/共6层”。
- 建筑年代:房屋的建成年份。
- 总价:单位为万元。
- 单价:单位为元/平方米。
这些数据可能来自公开的房产信息平台通过爬虫获取,也可能是公司内部的数据集。原始数据通常以CSV或Excel格式存储。
2.2 使用Pandas进行数据清洗
清洗工作主要借助pandas库完成,这是Python数据分析的基石。
import pandas as pd import numpy as np # 1. 读取数据 df = pd.read_csv('nanjing_second_hand.csv', encoding='utf-8') # 根据实际编码调整 # 2. 初步查看数据 print(df.head()) print(df.info()) print(df.describe())df.info()可以帮助我们快速查看各字段的非空值数量、数据类型,这是发现数据缺失问题的第一步。df.describe()则针对数值型字段(如面积、总价、单价)给出统计摘要(均值、标准差、分位数等),有助于发现异常值。
接下来是具体的清洗步骤:
处理缺失值:对于关键数值字段(如面积、总价),如果缺失比例不高,可以考虑删除该行记录。如果缺失比例较高,或者字段重要性一般,可以根据业务逻辑进行填充。例如,单价缺失可以用总价/面积计算得出;装修情况缺失可以填充为“未知”。
# 删除关键字段(如总价、面积)为空的记录 df_cleaned = df.dropna(subset=['总价', '面积']) # 或者,用中位数填充单价字段的缺失值(如果存在) if '单价' in df_cleaned.columns: df_cleaned['单价'] = df_cleaned['单价'].fillna(df_cleaned['单价'].median())处理异常值:通过描述性统计和可视化(如箱线图)可以发现异常值。例如,单价远高于或低于市场正常范围(如低于1万/平或高于10万/平),面积异常小或大(如小于10平或大于500平)。处理方式可以是设定合理的范围进行过滤。
# 假设我们认为南京二手房单价合理范围在1万到10万之间 reasonable_unit_price_range = (10000, 100000) df_cleaned = df_cleaned[(df_cleaned['单价'] >= reasonable_unit_price_range[0]) & (df_cleaned['单价'] <= reasonable_unit_price_range[1])]格式化与衍生字段:原始数据中的字段可能需要拆分或计算,以方便分析。
- 拆分楼层:从“中楼层/共6层”中提取“当前楼层”和“总楼层”。
- 计算楼龄:用当前年份减去“建筑年代”,得到“楼龄”。
- 区域标准化:确保区域名称统一,比如“鼓楼区”和“鼓楼”统一为“鼓楼”。
# 示例:拆分楼层信息(假设原始字段名为‘floor_info’) def split_floor(floor_str): try: current, total = floor_str.split('/') current = current.replace('楼层', '') total = total.replace('共', '').replace('层', '') return current, int(total) except: return None, None df_cleaned[['楼层类型', '总楼层']] = df_cleaned['floor_info'].apply(lambda x: pd.Series(split_floor(x))) # 计算楼龄 current_year = pd.Timestamp.now().year df_cleaned['楼龄'] = current_year - df_cleaned['建筑年代']去重:删除完全重复的记录。
df_cleaned = df_cleaned.drop_duplicates()注意:数据清洗没有一成不变的规则,每一步操作都需要结合业务常识来判断。例如,删除异常值虽然能让图表更“好看”,但也可能丢失了有价值的信息(如顶级豪宅或特殊产权房)。在清洗前后,最好都保存一份数据快照,方便回溯。
3. 核心分析与可视化:用图表讲述市场故事
数据清洗完毕后,就进入了最核心的分析与可视化环节。这里我主要使用matplotlib和seaborn这两个库。matplotlib是基础,功能强大但稍显繁琐;seaborn基于matplotlib,提供了更高级的接口和更美观的默认样式,非常适合统计图表。
3.1 区域维度分析:哪里是供应和价格的高地?
首先,我们想知道南京各个区的房源供应量和价格水平。
房源数量分布(柱状图):这是最直观的展示。可以清楚地看到鼓楼、江宁、浦口等区的挂牌量遥遥领先,而建邺、玄武等核心区的房源量相对较少,这符合市场常识——核心区新房和二手房供应都更稀缺。
import matplotlib.pyplot as plt import seaborn as sns plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 # 设置seaborn样式 sns.set_style("whitegrid") # 绘制各区房源数量柱状图 district_count = df_cleaned['区域'].value_counts() plt.figure(figsize=(12, 6)) ax = sns.barplot(x=district_count.index, y=district_count.values, palette="viridis") plt.title('南京各区二手房挂牌数量分布', fontsize=16) plt.xlabel('区域', fontsize=14) plt.ylabel('房源数量', fontsize=14) # 在柱子上方添加数量标签 for i, v in enumerate(district_count.values): ax.text(i, v + 5, str(v), ha='center', fontsize=10) plt.xticks(rotation=45) # 旋转x轴标签,防止重叠 plt.tight_layout() plt.show()区域单价对比(箱线图或小提琴图):箱线图可以展示每个区域单价的中位数、四分位数以及离散程度(异常值)。小提琴图则能同时展示分布形状和密度。从图中可以明显看出,建邺、鼓楼、玄武等传统核心区的单价中位数和整体水平远高于六合、溧水等外围区域。同时,核心区内部的单价差异(箱体长度和胡须长度)也更大,说明这些区域内部楼盘档次分化更明显。
# 绘制各区单价箱线图 plt.figure(figsize=(14, 8)) sns.boxplot(x='区域', y='单价', data=df_cleaned, palette="Set2") plt.title('南京各区二手房单价分布(箱线图)', fontsize=16) plt.xlabel('区域', fontsize=14) plt.ylabel('单价 (元/平方米)', fontsize=14) plt.xticks(rotation=45) plt.tight_layout() plt.show() # 绘制各区单价小提琴图,更直观显示分布密度 plt.figure(figsize=(14, 8)) sns.violinplot(x='区域', y='单价', data=df_cleaned, palette="muted", inner="quartile") plt.title('南京各区二手房单价分布(小提琴图)', fontsize=16) plt.xlabel('区域', fontsize=14) plt.ylabel('单价 (元/平方米)', fontsize=14) plt.xticks(rotation=45) plt.tight_layout() plt.show()3.2 价格与面积关系分析:市场的供需结构
总价和面积是购房者最关心的两个指标。分析它们的关系,能看出市场主力的产品类型。
总价与面积散点图(带区域颜色区分):将每个房源以点的形式画在图上,X轴是面积,Y轴是总价。我们可以用颜色来区分不同区域。这张图能直观揭示几个规律:1. 点形成的“云团”大致呈线性上升趋势,即面积越大,总价越高。2. 相同面积下,不同区域的总价点位于不同高度,直观反映了区域溢价(如建邺区的点普遍比浦口区同面积的点位置高)。3. 可以观察到一些偏离主云团的“离群点”,可能是别墅、顶级学区房或者数据噪声。
plt.figure(figsize=(12, 8)) # 使用hue参数按区域着色, palette指定调色板 scatter = sns.scatterplot(x='面积', y='总价', hue='区域', data=df_cleaned, palette='tab20c', s=60, alpha=0.7) plt.title('南京二手房总价与面积关系散点图(按区域着色)', fontsize=16) plt.xlabel('面积 (平方米)', fontsize=14) plt.ylabel('总价 (万元)', fontsize=14) plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') # 将图例放在图表外侧 plt.tight_layout() plt.show()单价分布直方图与密度曲线:观察整个南京二手房单价的集中区间。通过绘制直方图并叠加核密度估计(KDE)曲线,可以发现单价主要集中在2.5万到4.5万/平方米这个区间,呈近似正态分布但略有右偏(高价房源拉长了尾部)。
plt.figure(figsize=(10, 6)) sns.histplot(df_cleaned['单价'], kde=True, bins=50, color='skyblue', edgecolor='black') plt.title('南京二手房单价分布直方图', fontsize=16) plt.xlabel('单价 (元/平方米)', fontsize=14) plt.ylabel('频数', fontsize=14) plt.axvline(df_cleaned['单价'].mean(), color='red', linestyle='--', label=f'均值: {df_cleaned["单价"].mean():.0f}') plt.axvline(df_cleaned['单价'].median(), color='green', linestyle='--', label=f'中位数: {df_cleaned["单价"].median():.0f}') plt.legend() plt.tight_layout() plt.show()3.3 多维度交叉分析:挖掘更深层次的洞察
单一维度的分析还不够,我们需要交叉分析来回答更复杂的问题。
楼龄对单价的影响(散点图+回归线):通常,楼龄越新,单价越高。我们可以绘制楼龄和单价的散点图,并添加一条线性回归趋势线,来量化这种关系。结果可能显示负相关趋势,但趋势线的斜率(即楼龄每增加一年,单价下降多少)能给出一个粗略的量化参考。需要注意的是,这个关系受区域、装修等因素干扰很大。
plt.figure(figsize=(10, 6)) sns.regplot(x='楼龄', y='单价', data=df_cleaned, scatter_kws={'s':20, 'alpha':0.5}, line_kws={'color':'red'}) plt.title('楼龄与单价关系散点图(带回归线)', fontsize=16) plt.xlabel('楼龄 (年)', fontsize=14) plt.ylabel('单价 (元/平方米)', fontsize=14) plt.gca().invert_xaxis() # 反转X轴,让楼龄小的在右边,更符合认知 plt.tight_layout() plt.show()装修情况与单价的关系(分组柱状图):比较“精装”、“简装”、“毛坯”在不同区域的单价均值。可以绘制分组柱状图,X轴是区域,每组柱子代表不同的装修情况,柱子高度代表该条件下的平均单价。这张图能揭示:1. 在同一区域内,精装房的单价溢价是否明显。2. 在不同区域之间,装修带来的溢价幅度是否相同。
# 计算各区域、各装修情况的平均单价 pivot_table = df_cleaned.pivot_table(values='单价', index='区域', columns='装修情况', aggfunc='mean') # 绘制分组柱状图 pivot_table.plot(kind='bar', figsize=(14, 8), width=0.8) plt.title('不同区域及装修情况下二手房平均单价对比', fontsize=16) plt.xlabel('区域', fontsize=14) plt.ylabel('平均单价 (元/平方米)', fontsize=14) plt.xticks(rotation=45) plt.legend(title='装修情况') plt.tight_layout() plt.show()实操心得:可视化不是图表越多越好,而是要服务于核心结论。在开始画图前,先问自己:我想通过这张图回答什么问题?是看分布、比大小、找关系,还是看趋势?选择最合适的图表类型。另外,
seaborn的pairplot函数可以快速生成数据集中多个数值变量两两之间的散点图和分布图,用于探索性数据分析(EDA)非常高效,但在最终报告中使用时,要有选择地提取关键子图,避免信息过载。
4. 从Jupyter Notebook到可交付成果:源码组织与PPT生成
分析做完,图表也画好了,但项目还没结束。我们需要把代码整理好,方便自己日后复用和他人理解,同时还要将分析结论有效地呈现给非技术背景的听众(比如业务部门或领导),这就需要制作PPT。
4.1 源码的组织与封装
一个杂乱无章的.ipynb文件不是好作品。我习惯将项目按功能模块拆分,提高可读性和可维护性。
推荐的项目结构如下:
nanjing_house_analysis/ ├── data/ │ ├── raw/ # 存放原始数据 │ │ └── nanjing_second_hand.csv │ └── processed/ # 存放清洗后的数据 │ └── cleaned_data.csv ├── notebooks/ # Jupyter Notebook文件,用于探索性分析 │ └── 01_eda_visualization.ipynb ├── src/ # 核心源代码(Python模块) │ ├── __init__.py │ ├── data_cleaner.py # 数据清洗函数封装 │ ├── visualizer.py # 可视化图表生成函数封装 │ └── analyzer.py # 核心分析逻辑函数封装 ├── output/ # 生成的图表、报告等 │ ├── figures/ # 保存的图片(PNG/SVG) │ │ ├── district_count.png │ │ ├── price_vs_area.png │ │ └── ... │ └── report/ # 生成的报告(HTML/PDF) ├── requirements.txt # 项目依赖包列表 ├── main.py # 主程序入口,可一键运行整个分析流程 └── README.md # 项目说明文档关键代码封装示例 (src/visualizer.py):
# visualizer.py import matplotlib.pyplot as plt import seaborn as sns import pandas as pd class HousePriceVisualizer: def __init__(self, df, output_dir='./output/figures'): self.df = df self.output_dir = output_dir # 设置中文字体和样式 plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False sns.set_style("whitegrid") def plot_district_count(self, save=False): """绘制各区房源数量柱状图""" district_count = self.df['区域'].value_counts() plt.figure(figsize=(12, 6)) ax = sns.barplot(x=district_count.index, y=district_count.values, palette="viridis") plt.title('南京各区二手房挂牌数量分布', fontsize=16) plt.xlabel('区域', fontsize=14) plt.ylabel('房源数量', fontsize=14) for i, v in enumerate(district_count.values): ax.text(i, v + 5, str(v), ha='center', fontsize=10) plt.xticks(rotation=45) plt.tight_layout() if save: plt.savefig(f'{self.output_dir}/district_count.png', dpi=300, bbox_inches='tight') plt.show() plt.close() def plot_price_vs_area(self, save=False): """绘制总价-面积散点图(按区域着色)""" plt.figure(figsize=(12, 8)) scatter = sns.scatterplot(x='面积', y='总价', hue='区域', data=self.df, palette='tab20c', s=60, alpha=0.7) plt.title('南京二手房总价与面积关系散点图(按区域着色)', fontsize=16) plt.xlabel('面积 (平方米)', fontsize=14) plt.ylabel('总价 (万元)', fontsize=14) plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') plt.tight_layout() if save: plt.savefig(f'{self.output_dir}/price_vs_area.png', dpi=300, bbox_inches='tight') plt.show() plt.close() # ... 其他绘图函数这样,在主程序main.py中,调用就变得非常清晰:
# main.py from src.data_cleaner import clean_data from src.visualizer import HousePriceVisualizer def main(): # 1. 数据清洗 df_cleaned = clean_data('data/raw/nanjing_second_hand.csv') df_cleaned.to_csv('data/processed/cleaned_data.csv', index=False) # 2. 可视化 visualizer = HousePriceVisualizer(df_cleaned) visualizer.plot_district_count(save=True) visualizer.plot_price_vs_area(save=True) # ... 调用其他绘图函数 if __name__ == '__main__': main()4.2 使用Python自动化生成PPT报告
手动将图表复制粘贴到PPT里既繁琐又容易出错。我们可以用python-pptx库来自动化这个过程。思路是:创建一个PPT模板,然后用代码定位到模板中的占位符(Placeholder),将分析结论文本和生成的图片插入到指定位置。
步骤一:设计PPT模板在PowerPoint或Keynote中设计一个简单的模板,包含你需要的版式。例如:
- 标题页:项目名称、分析人、日期。
- 目录页:列出主要分析章节。
- 内容页:通常包含标题、内容文本框(用于写结论)和图片占位符。为每个要插入的图表预留一个图片占位符,并给这些占位符起一个容易识别的名字(在PPT的“选择窗格”中修改形状名称)。
步骤二:编写PPT生成脚本
# generate_ppt.py from pptx import Presentation from pptx.util import Inches, Pt import os def create_analysis_ppt(figures_dir, output_ppt_path, template_path='template.pptx'): """ 根据图表和分析结论生成PPT :param figures_dir: 存放图表图片的目录 :param output_ppt_path: 输出的PPT文件路径 :param template_path: PPT模板路径 """ prs = Presentation(template_path) # 1. 标题页 (假设模板第一页是标题页) title_slide = prs.slides[0] title = title_slide.shapes.title subtitle = title_slide.placeholders[1] # 假设第二个占位符是副标题 title.text = "南京二手房市场数据分析报告" subtitle.text = "基于Python的数据可视化分析\n报告日期:2023年10月" # 2. 添加目录页 (在模板后新增一页,使用标题和内容版式) slide_layout = prs.slide_layouts[1] # 假设版式索引1是“标题和内容” slide = prs.slides.add_slide(slide_layout) title = slide.shapes.title title.text = "报告目录" content = slide.shapes.placeholders[1] tf = content.text_frame tf.text = "1. 数据概览与清洗\n2. 区域市场分析\n3. 价格与面积关系\n4. 多维度交叉分析\n5. 核心结论与建议" # 3. 添加“区域市场分析”页,并插入图片 slide = prs.slides.add_slide(slide_layout) title = slide.shapes.title title.text = "2. 区域市场分析:供应与价格" content = slide.shapes.placeholders[1] tf = content.text_frame tf.text = "• 鼓楼、江宁、浦口挂牌量最大,占据市场主流。\n• 建邺、鼓楼、玄武为单价第一梯队,均价显著高于其他区域。\n• 核心区域内部房价分化明显,选择空间大但需仔细甄别。" # 插入“区域房源数量”图 img_path = os.path.join(figures_dir, 'district_count.png') left = Inches(0.5) top = Inches(2) # 放在内容文本框下方 slide.shapes.add_picture(img_path, left, top, width=Inches(4.5)) # 插入“区域单价分布”图(假设另一张图叫district_price_box.png) img_path2 = os.path.join(figures_dir, 'district_price_box.png') left2 = Inches(5) # 放在第一张图右边 slide.shapes.add_picture(img_path2, left2, top, width=Inches(4.5)) # 4. 类似地,添加其他分析页... # ... 插入“总价-面积散点图”、“单价分布直方图”等 # 5. 保存PPT prs.save(output_ppt_path) print(f"PPT报告已生成:{output_ppt_path}") if __name__ == '__main__': create_analysis_ppt('./output/figures', './output/report/nanjing_house_analysis.pptx')踩坑提醒:
python-pptx对PPT模板中的占位符索引和类型比较敏感。最可靠的方式不是用索引(如placeholders[1]),而是遍历幻灯片中的所有形状(slide.shapes),根据形状的名称(shape.name)来定位。你需要在PPT模板中,为每个文本框和图片占位符设置一个独特的、易于识别的名称(例如“title_box”, “content_box”, “chart1_placeholder”)。这样代码的鲁棒性会强很多。另外,插入图片时要注意调整位置和大小,可能需要多次调试才能获得满意的排版。
5. 项目复盘与扩展思考
完成整个流程后,回过头看,这个项目不仅仅是一次数据绘图练习,更是一个典型的数据分析Pipeline的缩影:数据获取 -> 清洗 -> 探索性分析 -> 可视化 -> 报告生成。每个环节都有值得深挖的地方。
关于数据源:本项目假设数据已经存在。在实际中,获取数据可能是一大挑战。你可以编写爬虫(使用requests、BeautifulSoup、Scrapy等)从房产网站定时抓取,但务必遵守网站的robots.txt协议和相关法律法规。更稳妥的方式是使用公开的数据集,或向公司内部的数据部门申请。
关于分析深度:本文展示的是描述性统计分析,告诉你市场“是什么样”。更进一步,可以做推断性分析或机器学习建模。例如:
- 预测房价:以单价或总价为因变量,面积、区域、楼龄、装修等为特征,构建回归模型(如线性回归、随机森林、XGBoost),预测房源价格,并分析哪些特征对价格影响最大(特征重要性分析)。
- 房源聚类:根据面积、总价、区域等特征对房源进行聚类(如K-Means),发现市场上不同的房源细分类型(如“刚需小户型”、“改善大平层”、“核心区豪宅”等)。
- 时间序列分析:如果你有不同时间点的历史数据,可以分析房价随时间的变化趋势,预测未来走势。
关于可视化进阶:matplotlib和seaborn足以应对大多数需求。如果想制作交互式图表或更复杂的可视化,可以学习Plotly或Pyecharts。它们能生成可在网页上交互的图表(缩放、拖拽、显示数据点详情),非常适合制作数据看板(Dashboard)。
关于项目部署:为了让分析流程自动化,你可以将脚本部署到服务器,定期运行(例如使用cron定时任务)。更进一步,可以用Flask或Streamlit快速搭建一个简单的Web应用,上传数据文件后,自动完成分析并生成可视化报告和PPT,实现一个轻量级的分析工具。
最后,源码和PPT模板的打包分享很有意义。在分享时,确保README.md文件写清楚:1. 项目目的;2. 环境配置步骤(pip install -r requirements.txt);3. 如何运行代码;4. 数据格式说明。一个清晰的项目结构和完善的文档,能让你的作品显得专业,也方便他人协作或学习。这个项目里用到的技术栈(Pandas, Matplotlib/Seaborn, python-pptx)都是Python生态中非常通用和强大的工具,掌握它们,你就能处理生活中、工作中遇到的很多类似的数据分析和自动化报告任务。
本文还有配套的精品资源,点击获取