Lee's Space Station

Plotly 初步

2018/11/17 Share

简介

Plotly 是一个用于绘制交互式图表的工具库,基于 React 和 Flask,基本功能免费,可以在 Jupyter Notebook 上进行在线或者离线绘图,支持 Python、MATLAB 和 R 等许多语言,其同类产品(Python 领域)是 bokeh,但是 plotly 绘制的图表更漂亮(个人感觉),支持类型更多(比如 bokeh 原生不支持 3D 绘图,而 plotly 支持)。我也曾经写过一篇关于嵌入 bokeh 绘图到博客的文章,但是后来还是转到了 plotly。

Plotly 的绘图语法和一般的还是稍有不同,这篇博文主要就是讲下 plotly 绘图的基础。为了让例子不那么死板,我将我之前写的 Python 问卷调查分析的文章(下称前文)中的图重新使用 plotly 绘制,在这个过程中来学习 plotly 绘图。

一些类型一样的图我就只展示一遍,详细的完全的重置代码见我的 GitHub 或者 nbviewer(建议使用这种方式查看,能看到图形)。

自己绘图的时候建议使用 offline 模式绘图,这样不会上传到云端,除非你想分享,详见完整代码。

数据

在开始之前先来回顾下数据是什么样的:

data

说明我就直接引用前文中的话了:

数据总大小是 9506×162,即 9506 行 162 列,上图是前 5 行,每行代表一个样本,即一个参与调查的开发者。可以看到已经将一道选择题的答案拆分成多列,这一般是用于处理多选题,例如上图中的「What other language(s) do you use?」,而单选题则不用拆分,可以直接写答案,例如上图第一列。

散点图 go.Scatter

此图用于说明 Python 2 和 3 在开发者们中的使用比例。

我先说下 plotly 的绘图逻辑(下同):

  1. 定义 trace,类似于 matplotlib 中的坐标轴和图形(例如折线),只管画图
  2. 定义 layout,就是布局,标题、margin 等
  3. 定义 data,就是一个 trace 列表,因为可能会同时绘制多个图形,例如多条折线
  4. 使用 go.Figure 或者 go.FigureWidget 绘图,传入 datalayout 参数。
1
2
3
4
5
6
7
8
9
10
colors = [rgb2hex(i) for i in sns.color_palette('rainbow')[:2]]

py2 = go.Scatter(x=frameworks_pyver.columns, y=frameworks_pyver.loc['Python 2'], mode='markers', marker={'color': colors[0]}, name='Python 2')
py3 = go.Scatter(x=frameworks_pyver.columns, y=frameworks_pyver.loc['Python 3'], mode='markers', marker={'color': colors[1]}, name='Python 3')

data = [py2, py3]
layout = go.Layout(title='Python 2 and Python 3 Usage among Frameworks')
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout) # 备注 1
py.iplot(fig) # 备注 2

注意:我是在 Jupyter Notebook(不是 Jupyter Lab,Jupyter Lab 对 plotly 的支持不太好)中绘图的,从 plotly 3.0.0 以来,你有两种方法来在 Jupyter Notebook 中绘图:

  • 使用 go.Figure:此时需要预先使用 plotly.offline.init_notebook_mode 初始化,然后使用 plotly.offline.iplot 来绘图,也就是上面代码使用的(备注 1 和 2)。而且如果要分享或者在网站上嵌入自己绘制的图形,那么就需要使用这种方式来将图形托管在 plotly 上,然后复制嵌入代码到自己的网站,就像我现在做的这样。但是注意免费账号最多能托管 25
  • 使用 go.FigureWidget:此时就不需要那些了,这是 3.0.0 的新特性,直接使用即可,即 备注 1 上面那行代码,备注 1 和 2 都可以删掉了,但是目前存在的一个问题是当绘制完关掉 notebook 后再打开,刚绘制的图就不在了

折线图 go.Scatter

此图用于说明开发者年龄和是否使用 Python 3 的关系。其实折线图和散点图用的是同一个函数,只不过 marker 不一样。

1
2
3
4
5
6
7
trace = go.Scatter(x=age_pyver.index, y=age_pyver['Python 3'], marker={'color': colors[0]}, mode='lines+markers', line={'width': 2}, name='Python 3')

data = [trace]
layout = go.Layout(title="The developers' age VS The use ratio of Python 3")
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

饼图 go.Pie

这个图用于展示开发者中使用 Python 2 和 Python 3 的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import plotly.graph_objs as go
from plotly.offline import init_notebook_mode
import plotly.offline as offline

python_version = survey_df['which version of python do you use the most?']
counts = python_version.value_counts()
labels = counts.index
values = counts.values
colors = [rgb2hex(i) for i in sns.color_palette('rainbow')[:2]]

trace = go.Pie(labels=labels, values=values,
marker={
'colors': colors
},
rotation=0,
hoverinfo='label+value')
data = [trace]
layout = {
'title': 'Python 2 VS Python 3'
}
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

横向柱形图 go.Bar

此处用于说明使用 Python 3 和 Python 2 的开发者的国别分布。

1
2
3
4
5
6
7
8
9
10
# 等同于 sns.countplot
colors = [rgb2hex(i) for i in sns.color_palette('rainbow', 10)]

trace = go.Bar(x=count_countries.index[:10], y=count_countries[:10], marker={'color': colors})

data = [trace]
layout = go.Layout(title='Top 10 countries of # of the developers')
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

横向分组柱形图 go.Bar

此图用于说明开发者人数最多的三个国家(美国、印度和中国)的开发者年龄构成。

1
2
3
4
5
6
7
8
colors = [rgb2hex(i) for i in sns.color_palette('rainbow', 7)]

data = [go.Bar(x=three_countries.index, y=three_countries[c], marker={'color': colors[i]}, name=c)
for i, c in enumerate(three_countries.columns)]
layout = go.Layout(title="Age distribution of the developers who're from USA, India and China")
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

纵向柱形图 go.Bar

此图用于说明常用框架中使用 Python 2 和 Python 3 的比例。

1
2
3
4
5
6
7
8
9
10
11
12
colors = [rgb2hex(i) for i in sns.color_palette('rainbow', len(count_df))]
trace = go.Bar(y=count_df.index[::-1], x=count_df.values[::-1], marker={'color': colors[::-1]}, orientation='h')

data = [trace]
layout = go.Layout(
title='Framework Usage',
margin={'r': 10},
height=1000,
yaxis={'automargin': True}
)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

Distplot 图 ff.create_distplot

此图用于说明 Python 2 和 3 在开发者们中的使用比例,类似于 sns.distplot,是直方图和 KDE 的混合,用于展示一个单变量的分布。

1
2
3
4
5
import plotly.figure_factory as ff
colors = [rgb2hex(i) for i in sns.color_palette('rainbow')[:2]]
fig = ff.create_distplot(hist_data=[frameworks_pyver.loc['Python 2'], frameworks_pyver.loc['Python 3']], group_labels=['Python 2', 'Python 3'], bin_size=0.05, colors=colors)
# go.FigureWidget(fig)
py.iplot(fig)

面积图 go.Scatter

此图用于说明做数据分析和机器学习的人常用的框架。

1
2
3
4
5
6
7
8
9
10
11
sorted_da_ml_frameworks_uses = da_ml_frameworks_uses.sort_values(by='Data analysis', axis=1, ascending=True)
colors = [rgb2hex(i) for i in sns.color_palette('rainbow')[:2]]

da = go.Scatter(x=sorted_da_ml_frameworks_uses.columns, y=sorted_da_ml_frameworks_uses.loc['Data analysis'], fill='tozeroy', marker={'color': colors[0]}, name='Python 2')
ml = go.Scatter(x=sorted_da_ml_frameworks_uses.columns, y=sorted_da_ml_frameworks_uses.loc['Machine learning'], fill='tozeroy', marker={'color': colors[1]}, name='Python 3')

data = [da, ml]
layout = go.Layout(title='Frameworks Usage among Data Analysis and Machine Learning Developers')
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

Violin 图 go.Violin

此图是用于说明每个国家的开发者中使用 Python 3 的比例。

1
2
3
4
5
6
7
8
9
colors = [rgb2hex(i) for i in sns.color_palette('rainbow')[:2]]

trace = go.Violin(x=countries_pyver_ratio['Python 3'], meanline={'visible': True}, box={'visible': True}, fillcolor=colors[0], opacity=0.6, line={'color': 'black'})
layout = go.Layout(title="Use ratio of Python 3 in the world", xaxis={'zeroline': False})

data = [trace]
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

世界地图

此图同样是用于说明每个国家的开发者中使用 Python 3 的比例,只不过是在地图上呈现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
data = [ dict(
type = 'choropleth',
locations = countries_pyver_ratio.index,
locationmode = 'country names',
z = countries_pyver_ratio['Python 3'] * 100,
text = countries_pyver_ratio.index,
colorscale = 'Bluered',
autocolorscale = False,
reversescale = True,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
# autotick = False,
ticksuffix = '%',
title = 'Percent'),
) ]

layout = dict(
title = 'Python 3 in the world',
geo = dict(
showframe = False,
showcoastlines = False,
projection = dict(
type = 'equirectangular'
),
)
)

# fig = go.Figure()
# go.FigureWidget(data=data, layout=layout)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

END

以上只是我在前文中涉及到的图形的 plotly 重绘,其实还有其他图形没有涉及,例如 3D 散点图(这也是我放弃 bokeh 的原因)。此外除了在 Jupyter Notebook 上绘图外,plotly 还有一个用于构建数据分析 Web 应用的 Python 框架:Dash,基于 Plotly.js、React 和 Flask,接下来我会在此基础上讲一下如何使用 Dash 来构建这样一个 Web 应用。

CATALOG
  1. 1. 简介
  2. 2. 数据
  3. 3. 散点图 go.Scatter
  4. 4. 折线图 go.Scatter
  5. 5. 饼图 go.Pie
  6. 6. 横向柱形图 go.Bar
  7. 7. 横向分组柱形图 go.Bar
  8. 8. 纵向柱形图 go.Bar
  9. 9. Distplot 图 ff.create_distplot
  10. 10. 面积图 go.Scatter
  11. 11. Violin 图 go.Violin
  12. 12. 世界地图
  13. 13. END