用 Pandas 绘制带交互的可视化图表

Python编程与实战

共 13985字,需浏览 28分钟

 ·

2021-10-19 06:24

大家好,我是村长。

Pandas0.25.0版本之后,提供了一些其他绘图后端,其中就有我们今天要演示的主角基于Bokeh

Starting in 0.25 pandas can be extended with third-party plotting backends. The main idea is letting users select a plotting backend different than the provided one based on Matplotlib.

目录:

  • 0. 环境准备

  • 1. 折线图

  • 2. 柱状图(条形图)

  • 3. 散点图

  • 4. 点图

  • 5. 阶梯图

  • 6. 饼图

  • 7. 直方图

  • 8. 面积图

  • 9. 地图

  • 10. 其他


b2300f055757798f6e8155905b87b427.webp

0. 环境准备

我们用到的是pandas-bokeh,它为PandasGeoPandasPysparkDataFrames提供了Bokeh绘图后端,类似于Pandas已经存在的可视化功能。导入库后,在DataFramesSeries上就新添加了一个绘图方法plot_bokeh()

安装第三方库

pip install pandas-bokeh

or conda:

conda install -c patrikhlobil pandas-bokeh

如果你是使用jupyter notebook,可以这样让其直接显示

import pandas as pd
import pandas_bokeh

pandas_bokeh.output_notebook()

同样如果输出是html文件,则可以用以下方式处理

import pandas as pd
import pandas_bokeh

pandas_bokeh.output_file("Interactive Plot.html")

当然在使用的时候,记得先设置 绘制后端为pandas_bokeh

import pandas as pd

pd.set_option('plotting.backend''pandas_bokeh')

目前这个绘图方式支持的可视化图表有以下几类:

  • 折线图
  • 柱状图(条形图)
  • 散点图
  • 点图
  • 阶梯图
  • 饼图
  • 直方图
  • 面积图
  • 地图


b2300f055757798f6e8155905b87b427.webp

1. 折线图

交互元素含有以下几种:

  • 可平移或缩放
  • 单击图例可以显示或隐藏折线
  • 悬停显示对应点数据信息

先看一个简单案例:

import numpy as np

np.random.seed(42)
df = pd.DataFrame({"谷歌": np.random.randn(1000)+0.2
                   "苹果": np.random.randn(1000)+0.17}, 
                   index=pd.date_range('1/1/2020', periods=1000))
df = df.cumsum()
df = df + 50
df.plot_bokeh(kind="line")       #等价于 df.plot_bokeh.line()
f587269b186c58668dbaf38ad86dc5f1.webp折线图

在绘制过程中,我们还可以设置很多参数,用来设置可视化图表的一些功能:

  • kind : 图表类型,目前支持的有:“line”、“point”、“scatter”、“bar”“histogram”;在不久的将来,更多的将被实现为水平条形图、箱形图、饼图等
  • x:x的值,如果未指定x参数,则索引用于绘图的 x 值;或者,也可以传递与 DataFrame 具有相同元素数量的值数组
  • y:y的值。
  • figsize : 图的宽度和高度
  • title : 设置标题
  • xlim / ylim:为 x 和 y 轴设置可见的绘图范围(也适用于日期时间 x 轴
  • xlabel / ylabel : 设置 x 和 y 标签
  • logx / logy : 在 x/y 轴上设置对数刻度
  • xticks / yticks : 设置轴上的刻度
  • color:为绘图定义颜色
  • colormap:可用于指定要绘制的多种颜色
  • hovertool:如果 True 悬停工具处于活动状态,否则如果为 False 则不绘制悬停工具
  • hovertool_string:如果指定,此字符串将用于悬停工具(@{column} 将替换为鼠标悬停在元素上的列的值)
  • toolbar_location:指定工具栏位置的位置(None, “above”, “below”, “left” or “right”)),默认值:right
  • zooming:启用/禁用缩放,默认值:True
  • panning:启用/禁用平移,默认值:True
  • fontsize_label/fontsize_ticks/fontsize_title/fontsize_legend:设置标签、刻度、标题或图例的字体大小(整数或“15pt”形式的字符串)
  • rangetool启用范围工具滚动条,默认False
  • kwargs **:bokeh.plotting.figure.line 的可选关键字参数
df.plot_bokeh.line(
    figsize=(800450), # 图的宽度和高度
    y="苹果"# y的值,这里选择的是df数据中的苹果列
    title="苹果"# 标题
    xlabel="Date"# x轴标题
    ylabel="Stock price [$]"# y轴标题
    yticks=[0100200300400], # y轴刻度值
    ylim=(0400), # y轴区间
    toolbar_location=None# 工具栏(取消)
    colormap=["red""blue"], # 颜色
    hovertool_string=r"""                        src='https://dss0.bdstatic.com/-0U0bnSm1A5BphGlnYG/tam-ogel/920152b13571a9a38f7f3c98ec5a6b3f_122_122.jpg' 
                        height="42" alt="@imgs" width="42"
                        style="float: left; margin: 0px 15px 15px 0px;"
                        border="2"> Apple 
                        
                        

 Stock Price: 

 @{苹果}"""
,  # 悬停工具显示形式(支持css)
    panning=False# 禁止平移
    zooming=False# 禁止缩放
cfc97d30907c8aa7e62071d3a19fb214.webp

对于折线图来说,还有一些特殊的参数,它们是:

  • plot_data_points:添加绘制线上的数据点
  • plot_data_points_size:设置数据点的大小
  • 标记:定义点类型*(默认值:circle)*,可能的值有:“circle”、“square”、“triangle”、“asterisk”、“circle_x”、“square_x”、“inverted_triangle”、“x”、“circle_cross”、“square_cross”、“diamond”、“cross” '
  • kwargs **:bokeh.plotting.figure.line 的可选关键字参数
df.plot_bokeh.line(
    figsize=(800450),
    title="苹果 vs 谷歌",
    xlabel="Date",
    ylabel="价格 [$]",
    yticks=[0100200300400],
    ylim=(0100),
    xlim=("2020-01-01""2020-02-01"),
    colormap=["red""blue"],
    plot_data_points=True# 是否线上数据点
    plot_data_points_size=10# 数据点的大小
    marker="square"# 数据点的类型
45b48d6be185d18bd6c0625a70d68157.webp

启动范围工具滚动条的折线图

ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2020', periods=1000))
df = pd.DataFrame(np.random.randn(10004), index=ts.index, columns=list('ABCD'))
df = df.cumsum()

df.plot_bokeh(rangetool=True)
0813035910d79a56af9a394fc3cd5e49.webp带有范围滚动条的折线图


1db727a316d92a6dc72038d93ea9885b.webp

2. 柱状图(条形图)

柱状图没有特殊的关键字参数,一般分为柱状图和堆叠柱状图,默认是柱状图。

data = {
    'fruits':
    ['Apples''Pears''Nectarines''Plums''Grapes''Strawberries'],
    '2015': [214324],
    '2016': [533246],
    '2017': [324453]
}
df = pd.DataFrame(data).set_index("fruits")

p_bar = df.plot_bokeh.bar(
    ylabel="Price per Unit [€]"
    title="Fruit prices per Year"
    alpha=0.6)
5282126a2766c41c28fd9c6e1a597899.webp柱状图

我们可以通过参数stacked来绘制堆叠柱状图:

p_stacked_bar = df.plot_bokeh.bar(
    ylabel="Price per Unit [€]",
    title="Fruit prices per Year",
    stacked=True# 堆叠柱状图
    alpha=0.6)
1ff7f687ef639add99de46abcc4b21bf.webp

默认情况下,x轴的值就是数据索引列的值,我们也可通过指定参数x来设置x轴;另外,我们还可以通过关键字kind="barh"或访问器plot_bokeh.barh来进行条形图绘制。

#Reset index, such that "fruits" is now a column of the DataFrame:
df.reset_index(inplace=True)

#Create horizontal bar (via kind keyword):
p_hbar = df.plot_bokeh(
    kind="barh",
    x="fruits",
    xlabel="Price per Unit [€]",
    title="Fruit prices per Year",
    alpha=0.6,
    legend = "bottom_right",
    show_figure=False)

#Create stacked horizontal bar (via barh accessor):
p_stacked_hbar = df.plot_bokeh.barh(
    x="fruits",
    stacked=True,
    xlabel="Price per Unit [€]",
    title="Fruit prices per Year",
    alpha=0.6,
    legend = "bottom_right",
    show_figure=False)

#Plot all barplot examples in a grid:
pandas_bokeh.plot_grid([[p_bar, p_stacked_bar],
                        [p_hbar, p_stacked_hbar]], 
                       plot_width=450)
e88ed965f8a8d7015b8072ca118201fd.webp


b2300f055757798f6e8155905b87b427.webp

3. 散点图

散点图需要指定x和y,以下参数可选:

  • category:确定用于为散点着色的类别对应列字段名
  • kwargs **:bokeh.plotting.figure.scatter 的可选关键字参数

以下绘制表格和散点图:

# Load Iris Dataset:
df = pd.read_csv(
    r"https://raw.githubusercontent.com/PatrikHlobil/Pandas-Bokeh/master/docs/Testdata/iris/iris.csv"
)
df = df.sample(frac=1)

# Create Bokeh-Table with DataFrame:
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.models import ColumnDataSource

data_table = DataTable(
    columns=[TableColumn(field=Ci, title=Ci) for Ci in df.columns],
    source=ColumnDataSource(df),
    height=300,
)

# Create Scatterplot:
p_scatter = df.plot_bokeh.scatter(
    x="petal length (cm)",
    y="sepal width (cm)",
    category="species",
    title="Iris DataSet Visualization",
    show_figure=False,
)

# Combine Table and Scatterplot via grid layout:
pandas_bokeh.plot_grid([[data_table, p_scatter]], plot_width=400, plot_height=350)
2f532a7403cdb9df59e6f03d1b89f648.webp表格与散点图

我们还可以传递一些参数比如 散点的大小之类的(用某列的值)

#Change one value to clearly see the effect of the size keyword
df.loc[13"sepal length (cm)"] = 15

#Make scatterplot:
p_scatter = df.plot_bokeh.scatter(
    x="petal length (cm)",
    y="sepal width (cm)",
    category="species",
    title="Iris DataSet Visualization with Size Keyword",
    size="sepal length (cm)"# 散点大小
)
9a656943797d8ab8a40157f053412ff4.webp


b2300f055757798f6e8155905b87b427.webp

4. 点图

点图比较简单,直接调用pointplot即可

import numpy as np

x = np.arange(-330.1)
y2 = x**2
y3 = x**3
df = pd.DataFrame({"x": x, "Parabula": y2, "Cube": y3})
df.plot_bokeh.point(
    x="x",
    xticks=range(-34),
    size=5,
    colormap=["#009933""#ff3399"],
    title="Pointplot (Parabula vs. Cube)",
    marker="x")
ed108199546cfe36c858abd873451981.webp点图


b2300f055757798f6e8155905b87b427.webp

5. 阶梯图

阶梯图主要是需要设置其模式mode,目前可供选择的是before, aftercenter

import numpy as np

x = np.arange(-331)
y2 = x**2
y3 = x**3
df = pd.DataFrame({"x": x, "Parabula": y2, "Cube": y3})
df.plot_bokeh.step(
    x="x",
    xticks=range(-11),
    colormap=["#009933""#ff3399"],
    title="Pointplot (Parabula vs. Cube)",
    figsize=(800,300),
    fontsize_title=30,
    fontsize_label=25,
    fontsize_ticks=15,
    fontsize_legend=5,
    )

df.plot_bokeh.step(
    x="x",
    xticks=range(-11),
    colormap=["#009933""#ff3399"],
    title="Pointplot (Parabula vs. Cube)",
    mode="after",
    figsize=(800,300)
    )
6dcc11b93e1752e5d782d81b42727c19.webp


b2300f055757798f6e8155905b87b427.webp

6. 饼图

这里我们用网上的一份自 2002 年以来德国所有联邦议院选举结果的数据集为例展示

df_pie = pd.read_csv(r"https://raw.githubusercontent.com/PatrikHlobil/Pandas-Bokeh/master/docs/Testdata/Bundestagswahl/Bundestagswahl.csv")
df_pie

Partei20022005200920132017
0CDU/CSU38.535.233.841.532.9
1SPD38.534.223.025.720.5
2FDP7.49.814.64.810.7
3Grünen8.68.110.78.48.9
4Linke/PDS4.08.711.98.69.2
5AfD0.00.00.00.012.6
6Sonstige3.04.06.011.05.0
df_pie.plot_bokeh.pie(
    x="Partei",
    y="2017",
    colormap=["blue""red""yellow""green""purple""orange""grey"],
    title="Results of German Bundestag Election 2017",
    )
f78bef57350a29068af0f603de23b5a0.webp饼图

如果我们想绘制全部的列(上图中我们绘制的是2017年的数据),则无需对y赋值,结果会嵌套显示在一个图中:

df_pie.plot_bokeh.pie(
    x="Partei",
    colormap=["blue""red""yellow""green""purple""orange""grey"],
    title="Results of German Bundestag Elections [2002-2017]",
    line_color="grey")
795b3e512f07f0e988191e210abde039.webp


b2300f055757798f6e8155905b87b427.webp

7. 直方图


在绘制直方图时,有不少参数可供选择:

  • bins:确定用于直方图的 bin,如果 bins 是 int,则它定义给定范围内的等宽 bin 数量(默认为 10),如果 bins 是一个序列,它定义了 bin 边缘,包括最右边的边缘,允许不均匀的 bin 宽度,如果 bins 是字符串,则它定义用于计算最佳 bin 宽度的方法,如histogram_bin_edges所定义
  • histogram_type“sidebyside”“topontop”“stacked”,默认值:“topontop”
  • stacked:布尔值,如果给定,则将histogram_type覆盖为*“stacked”*。默认值:*假False
  • kwargs **:bokeh.plotting.figure.quad 的可选关键字参数
import numpy as np

df_hist = pd.DataFrame({
    'a': np.random.randn(1000) + 1,
    'b': np.random.randn(1000),
    'c': np.random.randn(1000) - 1
    },
    columns=['a''b''c'])

#Top-on-Top Histogram (Default):
df_hist.plot_bokeh.hist(
    bins=np.linspace(-5541),
    vertical_xlabel=True,
    hovertool=False,
    title="Normal distributions (Top-on-Top)",
    line_color="black")

#Side-by-Side Histogram (multiple bars share bin side-by-side) also accessible via
#kind="hist":
df_hist.plot_bokeh(
    kind="hist",
    bins=np.linspace(-5541),
    histogram_type="sidebyside",
    vertical_xlabel=True,
    hovertool=False,
    title="Normal distributions (Side-by-Side)",
    line_color="black")

#Stacked histogram:
df_hist.plot_bokeh.hist(
    bins=np.linspace(-5541),
    histogram_type="stacked",
    vertical_xlabel=True,
    hovertool=False,
    title="Normal distributions (Stacked)",
    line_color="black")
027cacb08477d9c22b82cfaf181416f2.webpTop-on-Top Histogram (Default)8dd11f9582602eb62418b7993152a132.webpSide-by-Side Histogram3e7ed290ae1e60af9febee31625f7f5e.webpStacked histogram

同时,对于直方图我们还有更高级的参数:

  • weights:DataFrame 的一列,用作 histogramm 聚合的权重(另请参见numpy.histogram)
  • normed:如果为 True,则直方图值被归一化为 1(直方图值之和 = 1)。也可以传递一个整数,例如normed=100将导致带有百分比 y 轴的直方图(直方图值的总和 = 100),默认值:False
  • cumulative:如果为 True,则显示累积直方图,默认值:False
  • show_average:如果为 True,则还显示直方图的平均值,默认值:False
p_hist = df_hist.plot_bokeh.hist(
    y=["a""b"],
    bins=np.arange(-46.50.5),
    normed=100,
    vertical_xlabel=True,
    ylabel="Share[%]",
    title="Normal distributions (normed)",
    show_average=True,
    xlim=(-46),
    ylim=(030),
    show_figure=False)

p_hist_cum = df_hist.plot_bokeh.hist(
    y=["a""b"],
    bins=np.arange(-46.50.5),
    normed=100,
    cumulative=True,
    vertical_xlabel=True,
    ylabel="Share[%]",
    title="Normal distributions (normed & cumulative)",
    show_figure=False)

pandas_bokeh.plot_grid([[p_hist, p_hist_cum]], plot_width=450, plot_height=300# 仪表盘输出方式
54afbc5c5a9e35215c1a9ad64ab5c94e.webp


b2300f055757798f6e8155905b87b427.webp

8. 面积图

面积图嘛,提供两种:堆叠或者在彼此之上绘制

  • stacked:如果为 True,则面积图堆叠;如果为 False,则在彼此之上绘制图。默认值:False
  • kwargs **:bokeh.plotting.figure.patch 的可选关键字参数
# 我们用 之前饼图里的数据来绘制
df_energy = df_pie
df_energy.plot_bokeh.area(
    x="Partei",
    stacked=True,
    legend="top_right",
    colormap=["brown""orange""black""grey""blue"],
    title="标题",
    ylabel="Y轴",
    )
492d95a1c141e7c86363acdb6a504969.webp堆叠面积图
df_energy.plot_bokeh.area(
    x="Partei",
    stacked=False,
    legend="top_right",
    colormap=["brown""orange""black""grey""blue"],
    title="标题",
    ylabel="Y轴",
    )
fa6f3a56fdf1a826f322be21e17f537b.webp非堆叠面积图

当我们使用normed关键字对图进行规范时,还可以看到这种效果:

df_energy.plot_bokeh.area(
    x="Partei",
    stacked=True,
    normed=100,  # 规范满100(可看大致占比)
    legend="top_right",
    colormap=["brown""orange""black""grey""blue"],
    title="标题",
    ylabel="Y轴",
    )
487e5467fab0169ca3b2a522cafe74e8.webp


b2300f055757798f6e8155905b87b427.webp

9. 地图

关于地图绘制部分内容较多,这里我们不做详细介绍,后续出个专题讲解!

plot_bokeh.map函数,参数x和y分别对应经纬度坐标,我们以全球超过100万居民所有城市为例简单展示一下:

df_mapplot = pd.read_csv(r"https://raw.githubusercontent.com/PatrikHlobil/Pandas-Bokeh/master/docs/Testdata/populated%20places/populated_places.csv")
df_mapplot.head()

namepop_maxlatitudelongitude
0Mesa108539433.423915-111.736084
1Sharjah110302725.37138355.406478
2Changwon108149935.219102128.583562
3Sheffield129290053.366677-1.499997
4Abbottabad118364734.14950373.199501
df_mapplot["size"] = df_mapplot["pop_max"] / 1000000
df_mapplot.plot_bokeh.map(
    x="longitude",
    y="latitude",
    hovertool_string="""

 @{name} 

 
    
                        

 Population: @{pop_max} 

"""
,
    tile_provider="STAMEN_TERRAIN_RETINA",
    size="size"
    figsize=(900600),
    title="World cities with more than 1.000.000 inhabitants")
80488b5b6010e7ef51661da00c40b236.webpmap


b2300f055757798f6e8155905b87b427.webp

10. 其他

仪表盘输出,通过pandas_bokeh.plot_grid来设计仪表盘(大家具体看这行代码的逻辑

import pandas as pd
import numpy as np
import pandas_bokeh
pandas_bokeh.output_notebook()

#Barplot:
data = {
    'fruits':
    ['Apples''Pears''Nectarines''Plums''Grapes''Strawberries'],
    '2015': [214324],
    '2016': [533246],
    '2017': [324453]
}
df = pd.DataFrame(data).set_index("fruits")
p_bar = df.plot_bokeh(
    kind="bar",
    ylabel="Price per Unit [€]",
    title="Fruit prices per Year",
    show_figure=False)

#Lineplot:
np.random.seed(42)
df = pd.DataFrame({
    "Google": np.random.randn(1000) + 0.2,
    "Apple": np.random.randn(1000) + 0.17
},
                  index=pd.date_range('1/1/2000', periods=1000))
df = df.cumsum()
df = df + 50
p_line = df.plot_bokeh(
    kind="line",
    title="Apple vs Google",
    xlabel="Date",
    ylabel="Stock price [$]",
    yticks=[0100200300400],
    ylim=(0400),
    colormap=["red""blue"],
    show_figure=False)

#Scatterplot:
from sklearn.datasets import load_iris
iris = load_iris()
df = pd.DataFrame(iris["data"])
df.columns = iris["feature_names"]
df["species"] = iris["target"]
df["species"] = df["species"].map(dict(zip(range(3), iris["target_names"])))
p_scatter = df.plot_bokeh(
    kind="scatter",
    x="petal length (cm)",
    y="sepal width (cm)",
    category="species",
    title="Iris DataSet Visualization",
    show_figure=False)

#Histogram:
df_hist = pd.DataFrame({
    'a': np.random.randn(1000) + 1,
    'b': np.random.randn(1000),
    'c': np.random.randn(1000) - 1
},
                       columns=['a''b''c'])

p_hist = df_hist.plot_bokeh(
    kind="hist",
    bins=np.arange(-66.50.5),
    vertical_xlabel=True,
    normed=100,
    hovertool=False,
    title="Normal distributions",
    show_figure=False)

#Make Dashboard with Grid Layout:
pandas_bokeh.plot_grid([[p_line, p_bar], 
                        [p_scatter, p_hist]], plot_width=450)
0b2775c8157359b92fb4df64da89894c.webp仪表盘输出

又或者这样:

p_line.plot_width = 900
p_hist.plot_width = 900

layout = pandas_bokeh.column(p_line,
                pandas_bokeh.row(p_scatter, p_bar),
                p_hist)  # 指定每行显示的内容

pandas_bokeh.show(layout)
263aca3683b95c217d13730ca2946391.webp替代仪表板布局

以上就是本次全部内容,通过这部分的学习,我们发现Pandas除了结合matplotlib常规绘图外,还可以通过bokeh绘图后端快速绘制可交互的图表,用起来非常方便。

当然,如果想更深入了解或者定制化这些可视化图表,可能需要对bokeh有更多的了解,这块查阅官网资料即可!

浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报