前言
大部分时间我都是使用Pyecharts去做可视化,不过一直有个比较头疼的问题没法解决。
在pyecharts中是需要把所有的坐标点的数据加载到图表中,当数据量特别大的时候,那么这样一个图表可能会有好几百MB,使用起来会非常卡顿。虽然在Echarts中有ScatterGL来支持大数据量大可视化,不过在Pyecharts中没法直接支持,只能找一些曲线救国的方法,改善效果也不是很明显。
最近使用了一下plotly,发现了超大地理数据集可视化的解决办法,我们先来看下效果:
数据总共包含100W个数据点,地图缩放或者拖拽也不会存在卡顿。
其实这个解决办法也很好理解,datashader是将所有的数据点转换成为了包含所有点的一张图片,然后将这个图片与Plotly结合到一起,就相当于是在整个地图上加了一个图层,这样切换起来就不会卡顿了。
GIS图层这里是使用了Mapbox,Mapbox是一家非常优秀的地图厂商,各位可以去Mapbox定制属于自己的地图效果。
实现方法
接下来我们就详细说一下实现方法。
Mapbox图层
这个需要各位自己去Mapbox注册,免费的,注册完之后就可以定制自己的地图了。
定制完成后点击右上角的SHARE,复制出如下两个位置的数据就可以了!
在plotly中使用Mapbox的代码:
import plotly.express as px
# 自己去Mapbox申请一个Token
px.set_mapbox_access_token('pk.eyJ1IjoiYA....')
fig = px.scatter_mapbox(
df,
lat='Lat',
lon='Lon',
# 缩放比例
zoom=12,
# 地图中心位置
center=dict(lat=40.74000520746974, lon=-73.97681683902668),
mapbox_style="mapbox://styles/awesometang/cjb4fvg3o5m392tpjetxzaqgz"
)
fig.show()
datashader的使用
datashader的作用是将坐标点数据转换为像素密度栅格,栅格可以叠加在MapBox图层。
实现代码:
# 设置画布大小
cvs = ds.Canvas(plot_width=1980, plot_height=1000)
# 坐标点聚合
agg = cvs.points(dff, x='Lon', y='Lat')
coords_lat, coords_lon = agg.coords['Lat'].values, agg.coords['Lon'].values
# 四个角的坐标,用于在Mapbox中定位
coordinates = [[coords_lon[0], coords_lat[0]],
[coords_lon[-1], coords_lat[0]],
[coords_lon[-1], coords_lat[-1]],
[coords_lon[0], coords_lat[-1]]]
# 生成的图层
img = tf.shade(agg, cmap=fire)[::-1].to_pil()
颜色这里我是使用了https://github.com/holoviz/colorcet中的配色方案fire。
完整代码
将上面两部分和在一起,便完成了我们的图表了。
需要数据的可以自行去下载:
- Kaggle:https://www.kaggle.com/fivethirtyeight/uber-pickups-in-new-york-city
- 和鲸社区:https://www.heywhale.com/mw/dataset/5de9ed47953ca8002c95d1e0
import pandas as pd
import plotly.express as px
import datashader as ds
from colorcet import fire
import datashader.transfer_functions as tf
# 替换成你们自己的数据链接
df = pd.DataFrame()
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-apr14.csv'))
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-aug14.csv'))
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-jul14.csv'))
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-jun14.csv'))
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-may14.csv'))
df = df.append(pd.read_csv('/home/mw/input/uber5306/uber-pickups-in-new-york-city/uber-raw-data-sep14.csv'))
# 限制一下区域
# 有些离得很远的点,将整个画布范围拉的很大
# 最后会导致数据点都集中在很小一块
dff = df.query('Lat < 40.82').query('Lat > 40.70').query('Lon > -74.07').query('Lon < -73.86')
# 设置画布大小
cvs = ds.Canvas(plot_width=1980, plot_height=1000)
# 坐标点聚合
agg = cvs.points(dff, x='Lon', y='Lat')
coords_lat, coords_lon = agg.coords['Lat'].values, agg.coords['Lon'].values
# 四个角的坐标,用于在Mapbox中定位
coordinates = [[coords_lon[0], coords_lat[0]],
[coords_lon[-1], coords_lat[0]],
[coords_lon[-1], coords_lat[-1]],
[coords_lon[0], coords_lat[-1]]]
# 生成的图层
img = tf.shade(agg, cmap=fire)[::-1].to_pil()
# 自己去申请一个Token
px.set_mapbox_access_token('pk.eyJ1Ijo')
fig = px.scatter_mapbox(
dff[:1],
lat='Lat',
lon='Lon',
zoom=12,
center=dict(lat=40.74000520746974, lon=-73.97681683902668),
height=800)
fig.update_layout(mapbox_style="mapbox://styles/awesometang/cjb4fvg3o5m392tpjetxzaqgz",
mapbox_layers=[
{
"sourcetype": "image",
"source": img,
"coordinates": coordinates
}],
title='Uber Pickups In New-York'
)
fig.show()
# 保存图片
pio.write_image(fig, 'Uber Pickups In New-York.png', width=1980,height=1000)