投影点时 Geoviews + Datashader 很慢
Geoviews + Datashader is slow when projecting points
我正在使用 datashader
绘制 550,000,000 个纬度和经度。但是,为了使它有用,我需要使用 geoviews
叠加地图图块和多边形。问题是 geoviews.points()
和相关的投影会导致大幅减速,从而使 holoview
+ bokeh
图的交互性质变得多余。
下面有一个可重现的示例,但简而言之 - 我正在尝试使地理视图实现 (3) 足够快以交互工作。
首先设置一些数据
import numpy as np
import pandas as pd
import dask.dataframe as dd
import datashader as ds
import datashader.transfer_functions as tf
import holoviews as hv
from holoviews.operation.datashader import datashade
import geopandas as gpd
import geoviews as gv
例如将数据大小缩小 10。
uk_bounding_box = (-14.02,2.09,49.67,61.06)
n = int(550000000 / 10)
# Generate some fake data of the same size
df = dd.from_pandas(
pd.DataFrame.from_dict({
'longitude': np.random.normal(
np.mean(uk_bounding_box[0:2]),
np.diff(uk_bounding_box[0:2]) / 5, n
),
'latitude': np.random.normal(
np.mean(uk_bounding_box[2:4]),
np.diff(uk_bounding_box[2:4]) / 5, n
)
}), npartitions=8
)
# Persist data in memory so reading wont slow down datashader
df = df.persist()
(1) 只是数据着色器
仅使用没有全息视图或地理的数据着色器非常快 - 输出在 4 秒内呈现,包括聚合,因此 re-renders 如果交互的话会更快。
# Set some plotting params
bounds = dict(x_range = uk_bounding_box[0:2],
y_range = uk_bounding_box[2:4])
plot_width = 400
plot_height = 300
纯数据着色器版本时间:
%%time
cvs = ds.Canvas(plot_width=plot_width, plot_height=plot_height, **bounds)
agg = cvs.points(df, 'longitude', 'latitude', ds.count())
CPU 次:用户 968 毫秒,系统:29.9 毫秒,总计:998 毫秒
挂墙时间:506 毫秒
tf.shade(agg)
(2) holoviews
中的 datashader
没有 geoviews
投影
# Set some params
sizes = dict(width=plot_width, height=plot_height)
opts = dict(bgcolor="black", **sizes)
hv.extension('bokeh')
hv.util.opts('Image Curve RGB Polygons [width=400 height=300 shared_axes=False] {+axiswise} ')
没有任何投影,这与使用纯 datashader
相当
%%time
points = hv.Points(df, ['longitude', 'latitude']).redim.range(
x=bounds['x_range'], y=bounds['y_range'])
shader = datashade(points, precompute=True ,**sizes).options(**opts)
CPU 次:用户 3.32 毫秒,系统:131 微秒,总计:3.45 毫秒
挂墙时间:3.47 毫秒
shader
(3) datashader
in holoviews
with geoviews
tiles, polygons, and projection
这是问题的症结所在 - 我想将数据着色器层与一些地图图块和地理空间多边形对齐。这会导致速度大幅下降,对于我正在处理的数据量而言,交互式可视化变得多余。 (渲染总共等待 12 分钟)。
我确定这与投影点相关的开销有关 - 有没有办法避免这种情况或任何其他解决方法,例如预先计算投影?
# Grab an example shape file to work with
ne_path = gpd.datasets.get_path('naturalearth_lowres')
example_shapes_df = gpd.read_file(ne_path)
uk_shape = example_shapes_df[example_shapes_df.name.str.contains('United K')]
# Grab maptiles
map_tiles = gv.tile_sources.ESRI
# In actual workflow I need to add some polygons
polys = gv.Polygons(uk_shape)
这就是上面添加的 gv.points()
和 projection
%%time
points = gv.Points(df, ['longitude', 'latitude']).redim.range(
x=bounds['x_range'], y=bounds['y_range'])
projected = gv.operation.project_points(points)
shader = datashade(projected, precompute=True ,**sizes).options(**opts)
CPU 次:用户 11.8 秒,系统:3.16 秒,总计:15 秒
挂墙时间:12.5 秒
shader * map_tiles * polys
正如@philippjfr 所建议的那样,解决方案是将坐标投影到适当的坐标系中并使用上面的方法 2 或 3 进行渲染。
像这样:
import cartopy
def platcaree_to_mercator_vectorised(x, y):
'''Use cartopy to convert Platecarree coords to Mercator.'''
return(cartopy.crs.GOOGLE_MERCATOR.transform_points(
cartopy.crs.PlateCarree(), x, y))
def platcaree_for_map_partitions(pddf):
'''Wrapper to apply mercator conversion and convert back to dataframe for Dask.'''
as_arrays = platcaree_to_mercator_vectorised(pddf.longitude.values,pddf.latitude.values)
as_df = pd.DataFrame.from_records(as_arrays[:, :2], columns=['longitude', 'latitude'])
return(as_df)
# Project the points
df_projected = df.map_partitions(platcaree_for_map_partitions,
meta={'longitude': 'f8', 'latitude': 'f8'})
from dask.diagnostics import ProgressBar
with ProgressBar():
df_projected.to_parquet('abb_projected.parquet', compression='SNAPPY')
然后将此投影数据集与方法 2 或 3 一起使用,详见问题。
我正在使用 datashader
绘制 550,000,000 个纬度和经度。但是,为了使它有用,我需要使用 geoviews
叠加地图图块和多边形。问题是 geoviews.points()
和相关的投影会导致大幅减速,从而使 holoview
+ bokeh
图的交互性质变得多余。
下面有一个可重现的示例,但简而言之 - 我正在尝试使地理视图实现 (3) 足够快以交互工作。
首先设置一些数据
import numpy as np
import pandas as pd
import dask.dataframe as dd
import datashader as ds
import datashader.transfer_functions as tf
import holoviews as hv
from holoviews.operation.datashader import datashade
import geopandas as gpd
import geoviews as gv
例如将数据大小缩小 10。
uk_bounding_box = (-14.02,2.09,49.67,61.06)
n = int(550000000 / 10)
# Generate some fake data of the same size
df = dd.from_pandas(
pd.DataFrame.from_dict({
'longitude': np.random.normal(
np.mean(uk_bounding_box[0:2]),
np.diff(uk_bounding_box[0:2]) / 5, n
),
'latitude': np.random.normal(
np.mean(uk_bounding_box[2:4]),
np.diff(uk_bounding_box[2:4]) / 5, n
)
}), npartitions=8
)
# Persist data in memory so reading wont slow down datashader
df = df.persist()
(1) 只是数据着色器
仅使用没有全息视图或地理的数据着色器非常快 - 输出在 4 秒内呈现,包括聚合,因此 re-renders 如果交互的话会更快。
# Set some plotting params
bounds = dict(x_range = uk_bounding_box[0:2],
y_range = uk_bounding_box[2:4])
plot_width = 400
plot_height = 300
纯数据着色器版本时间:
%%time
cvs = ds.Canvas(plot_width=plot_width, plot_height=plot_height, **bounds)
agg = cvs.points(df, 'longitude', 'latitude', ds.count())
CPU 次:用户 968 毫秒,系统:29.9 毫秒,总计:998 毫秒 挂墙时间:506 毫秒
tf.shade(agg)
(2) holoviews
中的 datashader
没有 geoviews
投影
# Set some params
sizes = dict(width=plot_width, height=plot_height)
opts = dict(bgcolor="black", **sizes)
hv.extension('bokeh')
hv.util.opts('Image Curve RGB Polygons [width=400 height=300 shared_axes=False] {+axiswise} ')
没有任何投影,这与使用纯 datashader
%%time
points = hv.Points(df, ['longitude', 'latitude']).redim.range(
x=bounds['x_range'], y=bounds['y_range'])
shader = datashade(points, precompute=True ,**sizes).options(**opts)
CPU 次:用户 3.32 毫秒,系统:131 微秒,总计:3.45 毫秒 挂墙时间:3.47 毫秒
shader
(3) datashader
in holoviews
with geoviews
tiles, polygons, and projection
这是问题的症结所在 - 我想将数据着色器层与一些地图图块和地理空间多边形对齐。这会导致速度大幅下降,对于我正在处理的数据量而言,交互式可视化变得多余。 (渲染总共等待 12 分钟)。
我确定这与投影点相关的开销有关 - 有没有办法避免这种情况或任何其他解决方法,例如预先计算投影?
# Grab an example shape file to work with
ne_path = gpd.datasets.get_path('naturalearth_lowres')
example_shapes_df = gpd.read_file(ne_path)
uk_shape = example_shapes_df[example_shapes_df.name.str.contains('United K')]
# Grab maptiles
map_tiles = gv.tile_sources.ESRI
# In actual workflow I need to add some polygons
polys = gv.Polygons(uk_shape)
这就是上面添加的 gv.points()
和 projection
%%time
points = gv.Points(df, ['longitude', 'latitude']).redim.range(
x=bounds['x_range'], y=bounds['y_range'])
projected = gv.operation.project_points(points)
shader = datashade(projected, precompute=True ,**sizes).options(**opts)
CPU 次:用户 11.8 秒,系统:3.16 秒,总计:15 秒 挂墙时间:12.5 秒
shader * map_tiles * polys
正如@philippjfr 所建议的那样,解决方案是将坐标投影到适当的坐标系中并使用上面的方法 2 或 3 进行渲染。
像这样:
import cartopy
def platcaree_to_mercator_vectorised(x, y):
'''Use cartopy to convert Platecarree coords to Mercator.'''
return(cartopy.crs.GOOGLE_MERCATOR.transform_points(
cartopy.crs.PlateCarree(), x, y))
def platcaree_for_map_partitions(pddf):
'''Wrapper to apply mercator conversion and convert back to dataframe for Dask.'''
as_arrays = platcaree_to_mercator_vectorised(pddf.longitude.values,pddf.latitude.values)
as_df = pd.DataFrame.from_records(as_arrays[:, :2], columns=['longitude', 'latitude'])
return(as_df)
# Project the points
df_projected = df.map_partitions(platcaree_for_map_partitions,
meta={'longitude': 'f8', 'latitude': 'f8'})
from dask.diagnostics import ProgressBar
with ProgressBar():
df_projected.to_parquet('abb_projected.parquet', compression='SNAPPY')
然后将此投影数据集与方法 2 或 3 一起使用,详见问题。