Dash是一个用于构建Web应用程序的Python数据可视化库。它基于Python Web框架Flask以及Javascript绘图库Plotly.js和用于构建用户界面的Javascript库React.js,所以它非常适合用于构建后端基于Flask,前端数据可视化的Web网页。因为Dash应用程序可以在Web浏览器中显示,所以可以将这些Dash应用程序部署到服务器,通过URL共享。Dash应用程序由两部分组成,第一部分是布局(Layout),该部分描述了应用程序的设计样式,用来展示数据以及引导用户使用;第二部分描述了应用程序的交互性。
下面展示一个Demo
###Demo简介
使用Dash数据可视化NBA2018-2019常规赛季得分榜前三十各项数据,查看球员得分折线图、球员得分条形图,来分析NBA各大球星的得分趋势,得分值集中范围,以及得分落差。查看得分命中率和三分命中率散点图,来分析各大球星的投篮能力,得分能力。
虎扑官网爬取NBA常规赛得分榜前三十球员的相关数据(姓名、球队、得分、得分命中率、三分命中率、罚球命中率)
代码如下:
import requests
from lxml import etree
url = 'https://nba.hupu.com/stats/players'
response = requests.get(url).text
html = etree.HTML(response)
result = []
for i in range(2,32):
cur = []
# 姓名数据解析
name = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[2]/a/text()".format(i))
# 球队数据解析
team = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[3]/a/text()".format(i))
# 得分数据解析
core = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[4]/text()".format(i))
# 得分命中率数据解析
shooting = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[6]/text()".format(i))
# 三分命中率数据解析
threeshooting = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[8]/text()".format(i))
# 罚球命中率数据解析
freeshooting = html.xpath("//table[@class='players_table']/tbody/tr[{}]/td[10]/text()".format(i))
cur.append(name[0])
cur.append(team[0])
cur.append(core[0])
cur.append(shooting[0])
cur.append(threeshooting[0])
cur.append(freeshooting[0])
result.append(cur)
得到如下数据
players = [['詹姆斯-哈登', '火箭', '36.10', '44.2%', '36.8%', '87.9%'],
['保罗-乔治', '快船', '28.00', '43.8%', '38.6%', '83.9%'],
['扬尼斯-阿德托昆博', '雄鹿', '27.70', '57.8%', '25.6%', '72.9%'],
['乔尔-恩比德', '76人', '27.50', '48.4%', '30%', '80.4%'],
['斯蒂芬-库里', '勇士', '27.30', '47.2%', '43.7%', '91.6%'],
['德文-布克', '太阳', '26.60', '46.7%', '32.6%', '86.6%'],
['科怀-伦纳德', '快船', '26.60', '49.6%', '37.1%', '85.4%'],
['凯文-杜兰特', '篮网', '26.00', '52.1%', '35.3%', '88.5%'],
['达米安-利拉德', '开拓者', '25.80', '44.4%', '36.9%', '91.2%'],
['肯巴-沃克', '凯尔特人', '25.60', '43.4%', '35.6%', '84.4%'],
['布拉德利-比尔', '奇才', '25.60', '47.5%', '35.1%', '80.8%'],
['布雷克-格里芬', '活塞', '24.50', '46.2%', '36.2%', '75.3%'],
['卡尔-安东尼-唐斯', '森林狼', '24.40', '51.8%', '40%', '83.6%'],
['多诺万-米切尔', '爵士', '23.80', '43.2%', '36.2%', '80.6%'],
['凯里-欧文', '篮网', '23.80', '48.7%', '40.1%', '87.3%'],
['扎克-拉文', '公牛', '23.70', '46.7%', '37.4%', '83.2%'],
['拉塞尔-威斯布鲁克', '火箭', '22.90', '42.8%', '29%', '65.6%'],
['克莱-汤普森', '勇士', '21.50', '46.7%', '40.2%', '81.6%'],
['朱利叶斯-兰德尔', '尼克斯', '21.40', '52.4%', '34.4%', '73.1%'],
['拉马库斯-阿尔德里奇', '马刺', '21.30', '51.9%', '23.8%', '84.7%'],
['朱-霍勒迪', '鹈鹕', '21.20', '47.2%', '32.5%', '76.8%'],
['德马尔-德罗赞', '马刺', '21.20', '48.1%', '15.6%', '83%'],
['卢卡-东契奇', '独行侠', '21.20', '42.7%', '32.7%', '71.3%'],
['迈克-康利', '爵士', '21.10', '43.8%', '36.4%', '84.5%'],
['丹吉洛-拉塞尔', '勇士', '21.10', '43.4%', '36.9%', '78%'],
['CJ-麦科勒姆', '开拓者', '21.00', '45.9%', '37.5%', '82.8%'],
['尼古拉-武切维奇', '魔术', '20.80', '51.8%', '36.4%', '78.9%'],
['巴迪-希尔德', '国王', '20.70', '45.8%', '42.7%', '88.6%'],
['尼古拉-约基奇', '掘金', '20.10', '51.1%', '30.7%', '82.1%'],
['路易斯-威廉姆斯', '快船', '20.00', '42.5%', '36.1%', '87.6%']
]
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
# 指定图的id
dcc.Graph(id = 'graph-with-slider'),
# 定义滑块的各项属性
dcc.Slider(
id = 'class-slider',
min = 1,
max = 4,
value = 2,
marks = {'1':'得分条形图','2':'命中率条形图','3':'得分折线图','4':'三分命中率散点图'},
step = None
)
])
# 定义回调函数,使用‘@app.callback()'参数装饰器来装饰该回调函数,输出绑定图id,输入绑定滑块值
@app.callback(
Output('graph-with-slider', 'figure'),
[Input('class-slider','value')]
)
def update_output_div(input_value):
# 当滑块滑至1时,即输入值为1,返回得分条形图
if input_value == 1:
fig1 = dict(
data=[{'x': [i+1], 'y': [float(players[i][2])], 'type': 'bar', 'name': '{}'.format(players[i][0])} for i in range(len(players))],
layout = dict(title = 'NBA2018-2019赛季常规赛得分榜前十各项数据比较')
)
return fig1
# 当滑块滑至3,即输入值为3时,返回球员得分折线图
if input_value == 3:
x = []
y = []
for player in players:
x.append(player[0])
y.append(player[2])
fig2 = dict(
data=[
{'x':x,'y':y,'type':'Scatter','name':'Core'}
],
layout ={
'title': '球员得分折线图'
}
)
return fig2
# 当滑块滑至2时,即输入值为2,返回命中率条形图
if input_value == 2:
fig3 = dict(
data=[
{'x':[players[i][0]],'y':[float(players[i][3][0:4])],'type':'bar','name':'{}'.format(players[i][0])}for i in range(len(players))
],
layout=dict(title='球员命中率条形图')
)
return fig3
# 当滑块滑至4时,即输入值为4,返回得分命中率与三命中率散点图
if input_value==4:
x = []
y = []
team = []
for player in players:
x.append(float(player[3][0:4]))
if len(player[4])==5:
y.append(float(player[4][0:3]))
else:
y.append(float(player[4][0:2]))
team.append(player[1])
fig4=dict(
data = [
go.Scatter(
x = [x[i]],
y = [y[i]],
text = team,
name = players[i][0],
mode='markers',
opacity=0.8,
marker=dict(size=15, line=dict(width=0.5, color='white'))
)for i in range(len(players))
],
layout=go.Layout(
xaxis=dict(type='log', title='得分命中率'),
yaxis=dict(title='三分命中率', range=[10, 50]),
margin=dict(l=40, b=40, t=10, r=10),
hovermode='closest',
title = '球员得分命中率与三分命中率',
)
)
return fig4
if __name__ == '__main__':
# 开启服务,指定端口号为7000
app.run_server(port=7000)
上述代码即定义一个dash应用程序,通过回调函数来控制不同图的展示