上海餐饮数据分析与可视化
数据下载入口:Pandas+Pyecharts | 上海市餐饮数据分析可视化 - Heywhale.com
数据介绍
- 类别:餐饮类别的名称(如烧烤、美食、粤菜等)
- 行政区:餐厅所在行政区的名称(如浦东新区、闵行区等)
- 点评数:该餐厅的点评数量
- 口味、环境、服务:餐厅在这三个方面的评分
- 人均消费:餐厅的人均消费
- 城市:餐厅所在的城市(上海市)
- Lng、Lat:餐厅的经纬度
首先先读取数据:
import pandas as pd# 尝试的编码格式列表
encodings_tried = ['utf-8', 'gbk', 'ISO-8859-1', 'latin1']# 文件路径
file_path = '上海餐饮数据.csv'# 尝试使用不同的编码格式读取文件
for encoding in encodings_tried:try:df = pd.read_csv(file_path, encoding=encoding)print(f"成功使用编码格式 {encoding} 读取文件")break # 如果成功读取,则跳出循环except UnicodeDecodeError:print(f"编码格式 {encoding} 读取文件时发生错误")continue # 如果发生错误,则尝试下一个编码df
由于下载的csv文件不是utf-8编码,所以需要用其他编码读取方式进行读取,数据概览如下:
一:数据预处理
# 获取每一列的不同元素
for col in df.columns:unique_elements = df[col].unique()print(f"{col}列的不同元素: {unique_elements}")
首先观察每一列含有的元素如下:
可以发现“类别”,“行政区”列含有空值,且“口味”,“环境”,“服务”,“人均消费”列同时含有数字与文本。
# 删除“点评数”和“人均消费”列中含有 0 的行
df = df[(df['点评数']!= '0') & (df['人均消费']!= '0')]
# 去除每一列的空值
df = df.dropna(subset=['类别', '行政区', '点评数', '口味', '环境', '服务', '人均消费', '城市', 'Lng', 'Lat'])
首先去除数据的空值,然后只保留“口味”,“环境”,“服务”,“人均消费”列中的数字行。
# 定义一个函数来判断元素是否为数字
def is_numeric(value):try:float(value)return Trueexcept ValueError:return False# 选择需要检查的列
columns_to_check = ['口味', '环境', '服务', '人均消费', 'Lng', 'Lat']# 删除含有文本的行
df = df[df[columns_to_check].applymap(is_numeric).all(axis=1)]
二:数据可视化
1:各类别餐厅数量分布图
import matplotlib.pyplot as plt
# 设置Matplotlib的字体以支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 各类别餐厅的数量分布
category_counts = df['类别'].value_counts()# 绘制柱状图
plt.figure(figsize=(10, 6))
category_counts.plot(kind='bar', color='skyblue')
plt.title('各类别餐厅数量分布')
plt.xlabel('类别')
plt.ylabel('数量')
plt.xticks(rotation=45)
plt.show()
首先绘制各类别餐厅数量分布图:
可以观测“甜点”,“快餐”,“咖厅”等快餐类餐厅数量较多。
2:各行政区餐厅数量分布
import matplotlib.pyplot as plt# 设置 Matplotlib 的字体以支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 各行政区餐厅的数量分布
category_counts = df['行政区'].value_counts()# 绘制饼形图
plt.figure(figsize=(10, 6))
plt.pie(category_counts, labels=category_counts.index, autopct='%1.1f%%')
plt.title('各行政区餐厅数量分布')
plt.show()
再绘制各行政区餐厅的数量分布图:
可以观测出浦东新区,闵行区,徐汇区,松江区,宝山区的数量相对多。
3:类别与行政区之间的相关性
from scipy.stats import chi2_contingency# 创建交叉表
cross_tab = pd.crosstab(df['类别'], df['行政区'])# 进行卡方检验
chi2, p, dof, expected = chi2_contingency(cross_tab)print(f"卡方值:{chi2}")
print(f"p 值:{p}")if p < 0.05:print("类别与行政区之间存在相关性")
else:print("类别与行政区之间不存在相关性")
根据结果求出卡方值较大,p值小于0.05,说明类别与行政区之间存在相关性。然后具体展示类别与行政区相关性的体现:
import seaborn as sns# 创建交叉表
cross_tab = pd.crosstab(df['类别'], df['行政区'])# 绘制热力图
plt.figure(figsize=(10, 8))
sns.heatmap(cross_tab, annot=True, fmt='d', cmap='YlGnBu')
plt.title('类别与行政区的相关性热力图')
plt.xlabel('行政区')
plt.ylabel('类别')
plt.show()
体现相关性的热力图如下:
4:不同类别的平均综合评分比较
import numpy as np# 将"口味","服务","环境","人均消费"列的数据类型转换为数值型
df['口味'] = pd.to_numeric(df['口味'], errors='coerce')
df['服务'] = pd.to_numeric(df['服务'], errors='coerce')
df['环境'] = pd.to_numeric(df['环境'], errors='coerce')
df['人均消费'] = pd.to_numeric(df['人均消费'], errors='coerce')# 提取相关列的数据
columns = ['口味', '服务', '环境', '人均消费']
data = df[columns].values# 数据标准化
normalized_data = (data - np.min(data, axis=0)) / (np.max(data, axis=0) - np.min(data, axis=0))# 计算熵值
k = 1 / np.log(data.shape[0])
entropy = -k * np.sum(normalized_data * np.log(normalized_data + 1e-5), axis=0)# 计算权重
weight = (1 - entropy) / np.sum(1 - entropy)# 计算综合评分
composite_score = np.dot(normalized_data, weight)# 将综合评分添加到数据框中
df['综合评分'] = composite_score
首先使用熵权法得出每行数据的综合评分一列,然后整合得出不同类别的平均综合评分:
# 创建DataFrame
df1 = pd.DataFrame(data)# 按照“类别”分组并计算综合评分平均值
category_avg_score = df.groupby('类别')['综合评分'].mean()# 将结果从大到小排序并转换为DataFrame
sorted_df = category_avg_score.sort_values(ascending=False).reset_index()sorted_df
排序后的前10类别的平均综合评分如下:
可以看出南菜,午茶,素菜的评分相对较高。
5:不同行政区的平均综合评分比较
# 创建DataFrame
df2 = pd.DataFrame(data)# 按照“行政区”分组并计算综合评分平均值
category_avg_score = df.groupby('行政区')['综合评分'].mean()# 将结果从大到小排序并转换为DataFrame
sorted_df2 = category_avg_score.sort_values(ascending=False).reset_index()sorted_df2
同理得到排序后的前10行政区的平均综合评分如下:
可以看出静安区,卢湾区,徐汇区,浦东新区的餐厅平均综合评分较高。
想要探索多元化的数据分析视角,可以关注之前发布的相关内容。