In [1]:
# 1. 首先将要使用的库全部进行导入,并设置程序运行路径

import os
os.chdir(r"C:\OneDrive\华莱士\Documents\office培训\PYTHON培训\第四周")
#设置路径
import pandas as pd
import matplotlib.pyplot as plt
#处理数据及绘图
import warnings
warnings.filterwarnings('ignore')
#忽略警报
from bokeh.io import output_file
from bokeh.plotting import figure,show
from bokeh.models import ColumnDataSource
#交互式处理

In [2]:
# 2. 数据导入并查看数据
# 这里有9万条上海餐饮点评打分数据,特征包含:类别、行政区、点评数、口味、环境、服务、人均消费、城市、经度、维度。
df = pd.read_csv("上海餐饮数据.csv")
# 进行数据量以及标题信息的查看
df.columns.tolist() # 第一行作为表头(可不写)
print('总数据量为{}条'.format(len(df)))
df.tail(15) # 查看后15条数据


总数据量为96398条
Out[2]:
类别 行政区 点评数 口味 环境 服务 人均消费 城市 Lng Lat
96383 蟹宴 青浦区 0 0.0 0.0 0.0 0 上海市 120.900595 31.086895
96384 蟹宴 青浦区 0 0.0 0.0 0.0 0 上海市 120.900595 31.086895
96385 粤菜 青浦区 4 7.4 7.4 7.4 0 上海市 120.894450 31.103039
96386 浙菜 青浦区 10 7.5 7.0 7.2 58 上海市 120.879630 31.120212
96387 亚菜 浦东新区 216 7.0 7.1 7.1 49 上海市 119.578522 32.418227
96388 甜点 黄浦区 218 8.2 7.9 8.1 33 上海市 114.157967 22.284796
96389 西餐 黄浦区 0 0.0 0.0 0.0 0 上海市 109.561786 18.241289
96390 西餐 黄浦区 0 0.0 0.0 0.0 0 上海市 109.561786 18.241289
96391 快餐 黄浦区 14 7.1 7.2 7.3 0 上海市 109.556522 18.242452
96392 本菜 黄浦区 2 7.1 7.1 7.1 0 上海市 109.556522 18.242452
96393 快餐 黄浦区 14 7.1 7.2 7.3 0 上海市 109.556522 18.242452
96394 美食 嘉定区 2 7.3 7.4 7.4 0 上海市 107.557465 30.613898
96395 料理 闵行区 43 7.2 7.1 7.0 25 上海市 107.371337 24.510765
96396 甜点 普陀区 0 0.0 0.0 0.0 0 上海市 107.064797 29.831243
96397 美食 虹口区 0 0.0 0.0 0.0 0 上海市 106.175199 37.997707

In [3]:
# 3. 不同类别评价指标的选择

# 通过表格输出可以发现,对于不同的菜系类别,可用的评价有’口味’, ‘环境’, ‘服务’, ‘人均消费’(‘点评数’也可以反映产品的受欢迎程度,但是其数据值比其他字段的对比要相差较多,而且其数据值本身波动范围较大),这里去掉’点评数’,添加一个‘性价比’字段的指标,假设:性价比 = (口味 + 环境 + 服务)/ 人均消费

data_filter = df[['类别', '口味', '环境', '服务', '人均消费']]
data_filter['性价比'] = (data_filter['口味'] + data_filter['环境'] + data_filter['服务']) / data_filter['人均消费']
data_filter.dropna(inplace = True) # 去掉有空缺数据的行
data_filter = data_filter[(data_filter['口味']>0) & (data_filter['人均消费']>0)].reset_index()
del data_filter['index'] # 重新设置索引并删除原索引
data_filter.tail(15)

# –> 输出结果为:(首先选取所需要的指标字段,然后获得性价比指标字段的数据;再处理缺失值,这两步可以调整顺序,最后的数据之差只有三条(先处理缺失值是54886条数据,后处理缺失值是54889条数据),其次将数据为0的行数处理掉,这里只要’口味’, ‘环境’, '服务’中一项为0,其他的都为0,所以只选择一项即可;最后重新设置索引并删除原索引)


Out[3]:
类别 口味 环境 服务 人均消费 性价比
54874 浙菜 7.8 7.2 7.4 56 0.400000
54875 浙菜 7.4 7.1 7.3 106 0.205660
54876 浙菜 7.6 6.8 7.4 41 0.531707
54877 浙菜 9.1 7.7 8.8 62 0.412903
54878 海鲜 6.9 6.9 6.9 80 0.258750
54879 浙菜 7.2 7.1 7.4 68 0.319118
54880 海鲜 6.8 6.3 6.5 81 0.241975
54881 浙菜 6.9 6.6 6.6 80 0.251250
54882 浙菜 6.7 6.9 6.6 63 0.320635
54883 浙菜 7.7 7.0 7.4 76 0.290789
54884 浙菜 7.7 7.0 7.4 76 0.290789
54885 浙菜 7.5 7.0 7.2 58 0.374138
54886 亚菜 7.0 7.1 7.1 49 0.432653
54887 甜点 8.2 7.9 8.1 33 0.733333
54888 料理 7.2 7.1 7.0 25 0.852000

In [4]:
# 4. 绘制箱型图查看异常数据
# 下面这行代码用途是:箱型图底下的中文能显示出来
plt.rcParams['font.sans-serif']=['SimHei']
# 由于数据量较大,对于异常值的处理就很有必要,首先进行异常值的查看,可以使用箱型图进行描述,这里最终选择 三个指标 对不同菜系进行比较(当然也可以把‘服务’加进来)

fig, axes = plt.subplots(1,3,figsize = (14,6))
ls_columns = [ '口味', '人均消费', "性价比"]
for i in range(len(ls_columns)):
    data_filter.boxplot(column=ls_columns[i], ax = axes[i] )

# –> 输出结果为:(这一部分就是matplotlib子图还有箱型图的绘制了,可以看到每个指标中都存在这大量的异常值)



In [5]:
# 5. 异常值数据的清洗工作

# 这里直接封装一个函数进行异常值的清洗,之后对于此类问题的处理可以直接进行函数的调用,而且处理各个字段的异常值应该分别进行处理,这样可以避免不同字段数据之间的影响

def f1(data,col):
    q1 = data[col].quantile(q = 0.25)
    q3 = data[col].quantile(q = 0.75) 
    iqr = q3-q1
    t1 = q1 - 3 * iqr
    t2 = q3 + 3 * iqr
    return data[(data[col] > t1)&(data[col]<t2)][['类别',col]]

data_kw = f1(data_filter,'口味') # 处理口味数据
data_rj = f1(data_filter,'人均消费') # 处理人均消费数据
data_xjb = f1(data_filter,'性价比') # 处理性价比数据

# 使用iqr进行异常值的清洗,今后可以直接进行函数的调用,只需要修改一下'类别'的信息即可
data_kw.tail(15) # 查看口味后15条数据


Out[5]:
类别 口味
54874 浙菜 7.8
54875 浙菜 7.4
54876 浙菜 7.6
54877 浙菜 9.1
54878 海鲜 6.9
54879 浙菜 7.2
54880 海鲜 6.8
54881 浙菜 6.9
54882 浙菜 6.7
54883 浙菜 7.7
54884 浙菜 7.7
54885 浙菜 7.5
54886 亚菜 7.0
54887 甜点 8.2
54888 料理 7.2

In [6]:
# 6. 数据标准化并排序

# 数据异常值处理完毕之后,标准化处理,还是一样直接封装函数,方便之后直接调用,依旧是对不同的数据进行标准化
# 所谓标准化,就是把数字减去最小值,除以最大值减最小值。
# 举例说明:口味的最大值为9.4,最小值为4.7。	黄浦区甜点	分数为8.2,标准化后为(8.2-4.7)/(9.4-4.7)=0.74

def f2(data,col):
    col_name = col + '_norm'
    data_gp = data.groupby('类别').mean()
    data_gp[col_name] = (data_gp[col] - data_gp[col].min())/(data_gp[col].max()-data_gp[col].min())
    data_gp.sort_values(by = col_name, inplace = True, ascending=False)
    return data_gp

data_kw_score = f2(data_kw,'口味')
data_rj_score = f2(data_rj,'人均消费')
data_xjb_score = f2(data_xjb,'性价比')

# 标准化的基本步骤:首先创建一个新字段(列)并命名,然后按照某个要求进行分组(为什么取均值可以想一下,这个要回顾上一步异常值处理后的数据的结果),接着按照分组后的数据使用‘最大’/‘最小’标准化,最后进行某个字段(一般是某个标准化后的字段)进行排序即可

data_kw_score.tail(15) # 查看后15条数据


Out[6]:
口味 口味_norm
类别
湾菜 7.607396 0.416498
烧烤 7.606011 0.414547
西菜 7.541071 0.323088
甜点 7.524346 0.299533
龙虾 7.508283 0.276909
面馆 7.505263 0.272656
浙菜 7.503864 0.270686
午茶 7.500000 0.265244
川菜 7.489197 0.250029
美食 7.469031 0.221627
助餐 7.394745 0.117006
快餐 7.361730 0.070508
湘菜 7.353606 0.059066
北菜 7.350125 0.054164
常菜 7.311667 0.000000

In [7]:
# 7. 数据合并与绘图前准备

# 经过异常值清洗和标准化处理,数据已经可以进行使用了。接着就是将分开处理的数据再合并在一起,因为存在三组数据,所以应该是两两合并(索引的标签都是餐馆的类型)

data_final_q1 = pd.merge(data_kw_score,data_rj_score,left_index=True,right_index=True)    # 首先合并口味、人均消费指标得分
data_final_q1 = pd.merge(data_final_q1,data_xjb_score,left_index=True,right_index=True)       # 接着合并性价比指标得分


# 使用bokeh绘图之前,需要将columns的数据设置为英文(index这一列的名称也要是英文),另外设置一下点的大小(size参数)

data_final_q1['size'] = data_final_q1['口味_norm'] * 40  # 添加size字段
data_final_q1.index.name = 'type'
data_final_q1.columns = ['kw','kw_norm','price','price_norm','xjb','xjb_norm','size']

data_final_q1.tail(15) # 查看后15条数据


Out[7]:
kw kw_norm price price_norm xjb xjb_norm size
type
素菜 8.021705 1.000000 71.251969 0.364955 0.497415 0.371044 40.000000
南菜 7.984874 0.948128 91.411765 0.527529 0.325731 0.182992 37.925100
本菜 7.959824 0.912848 111.316243 0.688044 0.287742 0.141381 36.513934
火锅 7.949428 0.898206 98.524051 0.584884 0.282454 0.135589 35.928242
亚菜 7.889937 0.814421 94.426724 0.551842 0.310835 0.166675 32.576838
蟹宴 7.873469 0.791228 123.475248 0.786097 0.180064 0.023437 31.649130
西餐 7.853583 0.763221 93.768841 0.546537 0.367199 0.228414 30.528840
疆菜 7.791221 0.675392 59.610687 0.271077 0.611234 0.495714 27.015692
州菜 7.772500 0.649026 71.125000 0.363931 0.441502 0.309800 25.961024
料理 7.770436 0.646118 75.802834 0.401655 0.403445 0.268114 25.844738
海鲜 7.693175 0.537306 113.282158 0.703898 0.269551 0.121456 21.492258
粤菜 7.635520 0.456107 91.484435 0.528115 0.333748 0.191773 18.244289
啡厅 7.630942 0.449659 45.053159 0.153681 0.601575 0.485134 17.986344
湾菜 7.607396 0.416498 64.836310 0.313218 0.466233 0.336889 16.659923
烧烤 7.606011 0.414547 73.534963 0.383366 0.429858 0.297046 16.581887

In [9]:
# 8. 出图

from bokeh.layouts import gridplot
from bokeh.models import HoverTool
from bokeh.models.annotations import BoxAnnotation

output_file('菜系类型.html')#输出文件
source = ColumnDataSource(data_final_q1)#创建数据
data_type = data_final_q1.index.tolist()#横坐标为index,要先转化为列表

hover = HoverTool(tooltips = [
    ('餐饮类型','@type'),
    ('人均消费','@price'),
    ('性价比得分','@xjb_norm'),
    ('口味得分','@kw_norm'),
])

result = figure(plot_width = 800,plot_height = 300,title = '餐饮类型得分',
				x_axis_label = '人均消费', y_axis_label = '性价比',
               tools = [hover, 'box_select, reset, xwheel_zoom,pan,crosshair'])
result.circle(x = 'price', y = 'xjb_norm', source = source,
			line_color ='black',line_dash =[6,4], fill_alpha =0.6,size = 'size')

price_mid = BoxAnnotation(left = 40, right = 80, fill_alpha = 0.1, fill_color = 'navy')
result.add_layout(price_mid)#这里设置标记区


kw = figure(plot_width = 800,plot_height = 300,title = '口味得分',x_range = data_type,
               tools = [hover, 'box_select, reset, xwheel_zoom,pan,crosshair'])
kw.vbar(x = 'type', top = 'kw_norm', source = source, width = 0.8, alpha = 0.7,color = 'red')

price = figure(plot_width = 800,plot_height = 300,title = '人均消费得分',x_range = data_type,
               tools = [hover, 'box_select, reset, xwheel_zoom,pan,crosshair'])
price.vbar(x = 'type', top = 'price_norm', source = source, width = 0.8, alpha = 0.7,color = 'green')

p = gridplot([[result],[kw],[price]])#将三个图放在一个画布上
from bokeh.io import output_notebook
output_notebook() # 使用jupyter,加上这一行,可以直接在jupyter内显示
show(p)#一定要加show(p),否则不显示


Loading BokehJS ...

In [ ]: