利用plotly for Python进行数据可视化

利用plotly for Python进行数据可视化

plotly是一款强大的作图工具,可以快速制作各种精美的图表,而且生成的图表可以实时与用户产生交互,大家可以参考官网的示例。

如散点图

3D图


plotly本身是基于javascript开发的,但是提供了大量与其他主流数据分析语言的API,比如Python, R, Matlab,可以参考plot.ly/api/

如果你是Python用户,plotly不仅可以从原生的Python获得支持,如果你正在使用matplotlib,pandas等库,plotly也有专门为其量身定制的接口。

R语言用户想必已经熟悉ggplot2, 在R语言中只需要针对一个ggplot2图形对象p进行如下操作

ggplotly(p)

就会直接将其转化为一个plotly对象,十分方便。

个人觉得plotly与ggplot2在图形语法上有很多相似之处,所以R语言用户可能会很快上手,起初我也是在R语言环境中使用plotly,但是R语言中对于LaTeX的支持不是很好,从而转向Python。

plotly的官网文档十分详尽,然而对于初学者而言不太容易找到自己的需求。我接触plotly大概已经一个星期,所以在这里记录下我的一些最基本的经验,说不定谁看了这篇文章之后可以少走一些弯路。

Python用户可以直接使用pip安装plotly,我比较熟悉Python3.

sudo pip3 install plotly

本篇文章以我在自己科研中获取的一些数据为基础,绘制一幅最简单的散点图,并在图上添加一些基本的图形元素

首先,获得的数据(csv格式)如下,一共约15000条数据,这里只是头部

建议大家使用IPython Notebook,可以十分方便地与图形进行交互 。

首先导入plotly与pandas,可以用pandas中的read_csv直接读取原始数据。

下面就可以利用plotly绘制散点图。考虑到我们一共有7列数据,现在我们想研究前三列(MH, MH3, MH5)的相互关系,那么我们可以考虑将其中两个的数值映射到x, y轴,另一个变量的值可以用点的颜色来表征,下面是代码

绘制出的图像如下

其中有几点地方需要注意

  1. 整个图像的比例不是很协调,需要我们重新调整其长和宽。
  2. x轴,y轴,以及右侧的图例分别代表什么含义,这个图里并没有体现。
  3. 这个图中的配色方案是‘Viridis’,plotly默认了不少其他美观的配色方案,具体效果可以参考本文附录三。

接下来我们就涉及到了“图层”的概念,以上的图像仅仅就是将原始数据映射到坐标轴上,如果我们还想继续设置x轴的标题,y轴的标题,以及在图像上添加一些几何图形,那么就可以往上继续添加图层——layout(这里就是ggplot2作图的思想)

go.Layout就可以创建图层对象,具体用法如下:

得到的图像为

这样x轴, y轴以及整个图像都有了标题,而且图像的尺寸也得到了调整。

接下来我们可以往图上添加一些简单的几何图形,我们就以直线为例


同时在定义图层时加上shapes属性

我们得到

线段,圆形,矩形等plotly都有相应的选项,如果需要的话可以查看其关于shapes的文档plot.ly/python/shapes/

接下来我需要在图像上画一些交叉点,这个plotly自身并不支持(反正我没找到),但是我们可以用两条线段来组合成一个交叉点。

于是可以定义这样一个函数,给定一个坐标,返回组成这个交叉点的两条直线,这很简单

然后我们将layout中的shapes改成

最后得到我想要的图像

到这里我需要的图基本已经完成了,其实没有什么复杂的地方。认真的读者应该发现了,为什么我右侧的图例上没有标题?

这个问题非常好,因为我也不知道怎么在Python中在图例上添加标题。。

我查了不少官方的手册,到现在都没有一个比较好的解决办法,这个问题在R语言中是不存在的,这是R语言的代码,十分简洁,只是省略了line1,line2的定义

plot_ly(data, x=~MH5, y=~MH3, color=~MH, marker=list(size=3))%>%layout(shapes=list(line1,line2))

可以看到右侧图例的标题是自动加上的,而在Python中我却没有找到好的解决办法,如果大家知道的话请告诉我,感激不尽。。

(其实我强行找了一种十分丑陋的方法,但是也算勉强解决了问题,具体实现方法可以参考附录二)

以上就是我这几天来学习plotly的一些心得,刚开始接触的时候面对官方文档纷繁复杂无从下手,到现在稍微找到一些眉目,所以在这记录下来与大家分享,时间仓促中间不免有些疏漏,还请大家指正。


附录一:

完整的Python3代码

def cross(x, y):
    line1 = dict(
        type = 'line',
        xref = 'x',
        yref = 'y',
        x0 = x - 4,
        y0 = y - 5,
        x1 = x + 4,
        y1 = y + 5,
        line = dict(
            color = 'black',
            width = 2,
        )
    )
    line2 = dict(
        type = 'line',
        xref = 'x',
        yref = 'y',
        x0 = x - 4,
        y0 = y + 5,
        x1 = x + 4,
        y1 = y - 5,
        line = dict(
            color = 'black',
            width = 2,
        )
    )
    return line1, line2

import plotly.plotly as py
import plotly.graph_objs as go
import pandas as pd
data = pd.read_csv('30_-10_full.csv')

aes = go.Scatter( 
    x = data['MH5'],
    y = data['MH3'],
    mode = 'markers',
    name = 'MH',
    marker = dict( 
        color = data['MH'],
        size = '5',
        colorscale = 'Viridis',
        showscale = True 
    )
)
line1 = dict( 
    type = 'line',
    xref = 'x',    
    yref = 'y',
    x0 = 200, 
    y0 = 100,
    x1 = 200,
    y1 = 600,
    line = dict(       
        color = 'black', 
        width = 2, 
    )
)
line2 = dict(
    type = 'line',
    xref = 'x',
    yref = 'y',
    x0 = 100,
    y0 = 180.385,
    x1 = 519.615,
    y1 = 600,
    line = dict(
        color = 'black',
        width = 2,
    )
)
layout = go.Layout(
    title = '$MH_5, MH_3$',
    autosize = False,     
    width = 1000,
    height = 800,
    shapes = [line1, line2, cross(230, 530)[0], cross(230, 530)[1], cross(230, 430)[0], cross(230, 430)[1], cross(230, 330)[0], cross(230, 330)[1], cross(300, 530)[0], cross(300, 530)[1], cross(300, 430)[0], cross(300, 430)[1], cross(370, 530)[0], cross(370, 530)[1]],
    xaxis = dict( 
        title = '$MH_5$'
    ),
    yaxis = dict(
        title = '$MH_3$'
    ),
)
fig = go.Figure(data=[aes], layout=layout) 
py.iplot(fig)

附录二:

在Python环境下给右侧图例添加标题的一种方法:

关于图例,官方文档有专门一节讲了相关内容,参见plot.ly/python/legend/

然而其所用例子多是折线图, 图例的标题实际上是可以在go.Scatter中的‘name’选项定义的,就像这样

实际上如果大家加上这么一句再运行的话是不起任何效果的。

我发现这样一个事实,只有当存在至少两个data对象的时候才会显示legend的名字,所以我们可以在图中添加一个很小的白色的点,并且将它的名字设置为一个空格(即空白),但是单独作为一个对象,即

再在最后组合data与layout的语句中加上aes1

最后我们得到

可以看到右侧图例总算有了一个名字,虽然不在正上方,也不是很好看,不过至少有了。同时(300,300)新加的这个点根本看不见。

这个问题应当去阅读plotly的源码才能更好地解决,这里只是一个权宜之计。

附录三:

plotly自带的一些colorscale配色方案,方案的名字我已经写在图像的标题上了。

Viridis就是我们之前所用的颜色,如果你想使用下面的colorscale,只需将代码中的‘Viridis’改成相应的名称就好了

编辑于 2017-04-19