python plotly数据可视化(踩坑纪实)

接触一下python plotly这个数据可视化的模块,万万没想到,走出去的第一步就踩进泥坑,描述一下我想要实现的功能:

用柱状图显示部分省份2018年的GDP以及GDP增长速度。也就是以城市为X坐标,GDP和增长率为Y坐标(左右各一个),每个城市两个柱形,显示10组这样的数据。

万万没想到,第一步就遇到了官方bug。

环境

  • python 3.6.6
  • plotly 3.7.1

参考

plotly的使用

  1. pip安装plotly
    pip install plotly
  2. 项目中引入plotly模块
    import plotly, json
    from plotly import graph_objs as go
  3. 通过官方文档熟悉其用法后,可以简单绘制出 y=x x∈[1, 4] 的函数图
    import plotly
    from plotly import graph_objs as go
    
    trace1 = go.Scatter(
        x=[1, 2, 3, 4],
        y=[1, 2, 3, 4]
    )
    
    data = [trace1]
    
    plotly.offline.plot(data, auto_play=True)

从陷入泥潭到实现目标

首先去收集了一下2018年部分省份的GDP信息,并以json格式存储到Data文件中,长这样:

{
    "cities":[{
        "name": "广州",
        "gdp": 21705.29,
        "speed": 7
    },
    {
        "name": "江苏",
        "gdp": 21093.3,
        "speed": 7.1
    },
    {
        "name": "山东",
        "gdp": 18900.6,
        "speed": 6.7
    },
    {
        "name": "浙江",
        "gdp": 11691,
        "speed": 7.4
    },
    {
        "name": "河南",
        "gdp": 10611,
        "speed": 7.9
    },
    {
        "name": "四川",
        "gdp": 8590.2,
        "speed": 8.2
    },
    {
        "name": "湖北",
        "gdp": 8188.84,
        "speed": 7.6
    },
    {
        "name": "上海",
        "gdp": 7863.40,
        "speed": 6.8
    },
    {
        "name": "湖南",
        "gdp": 7777.65,
        "speed": 8
    },
    {
        "name": "河北",
        "gdp": 7564,
        "speed": 6
    }]
}

plotly中一组数据称为一个trace,一个trace中包含以两个列表存储的x坐标数据和y坐标数据。所以需要将json中的数据转储到三个列表(分别存储了城市名、GDP和GDP增长速度):

def get_json():
    with open("Data.json", "r", encoding="utf-8") as f:
        data = json.load(f)
        return data


def get_data():
    data = get_json()
    cities = data["cities"]
    cities = sorted(cities, key=lambda x: x["gdp"]) # 按GDP升序排序,原顺序为降序
    
    city_names = []
    gdps = []
    speeds = []
    for each in cities:
        city_names.append(each["name"])
        gdps.append(each["gdp"])
        speeds.append(each["speed"])
        
    return city_names, gdps, speeds

之后,根据 官方文档 的描述建立了两个trace,初步生成了带有GDP和GDP增速的柱状图:

def main():
    names, gdps, speeds = get_data()
    
    trace1 = go.Bar(
        x=names,
        y=gdps,
        name="2018年省份GDP(亿元)",
    )
    
    trace2 = go.Bar(
        x=names,
        y=speeds,
        name="2018年省份GDP增速",
    )
    
    Data = [trace1, trace2]
    Layout = go.Layout(
                barmode='group'
        )

    fig = go.Figure(data=Data, layout=Layout)
    
    plotly.offline.plot(fig, auto_play=True)

可以看到,由于增速和GDP在同一Y轴下,而增速和GDP不是一个量级,所以增速的数据完全没有被显示出来,这时就要加入第二个Y坐标。

官方文档里也有两个Y坐标的示例,我心想:“与柱状图结合一下不就完了”,于是结合了一下:

def main():
    names, gdps, speeds = get_data()
    
    trace1 = go.Bar(
        x=names,
        y=gdps,
        name="2018年省份GDP(亿元)",
    )
    
    trace2 = go.Bar(
        x=names,
        y=speeds,
        name="2018年省份GDP增速",
        yaxis="y2"
    )
    
    Data = [trace1, trace2]
    Layout = go.Layout(
        barmode='group',
        title="2018年部分省份GDP以及GDP增速情况",
        yaxis=dict(
            title="GDP"
        ),
        yaxis2=dict(
            title="GDP增速",
            overlaying="y",
            side="right"
        )
    )
    
    fig = go.Figure(data=Data, layout=Layout)
    
    plotly.offline.plot(fig, auto_play=True)

喜闻乐见,事情并没有想象的那么简单,增速数据的柱形盖在了GDP柱形上面。之后发觉第二个Y轴的Layout中有一个overlaying的属性,我又心想:“这不就是overlay(覆盖)的情况吗”,于是注释掉了overlaying:

GDP的数据完全消失,十个省份人民一年的劳动果实被我一行代码整蒸发了。

几番查找,先在 StackOverflow 找到了一个没人顶的答案,之后又在 Github issue 里找到了更加详细的临时解决方法——对每组数据(trace)增加一个假trace(只指定x轴数据不指定y轴数据),同真trace一起传到figure里绘制,才能解决这个官方bug——一个只在多个Y轴柱状图下才有的bug。

正确的实现方式:

def main():
    names, gdps, speeds = get_data()

    trace1 = go.Bar(
        x=names,
        y=gdps,
        name="2018年省份GDP(亿元)",
        yaxis="y1"
    )

    trace2 = go.Bar(
        x=names,
        y=speeds,
        name="2018年省份GDP增速",
        yaxis="y2"
    )

    trace3 = go.Bar( # GDP假trace
        x=names,
        y=[0],
        yaxis="y1",
        showlegend=False
    )

    trace4 = go.Bar( # 增速假trace
        x=names,
        y=[0],
        yaxis="y2",
        showlegend=False
    )

    Layout = go.Layout(
        barmode="group",
        title="2018年部分省份GDP以及GDP增速情况",
        yaxis1=dict(
            title="GDP",
            overlaying="y2"
        ),
        yaxis2=dict(
            title="GDP增速",
            side="right"
        ),
        xaxis=dict(
            title="城市"
        )
    )

    Data = [trace1, trace3, trace4, trace2] # 假trace必须放到真trace之间,否则依然会产生覆盖的效果

    fig = go.Figure(data=Data, layout=Layout)

    plotly.offline.plot(fig, auto_play=True)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据