Geoviews + Datashader работает медленно при проецировании точек

Я строю 550000000 широт и долгот, используя datashader, Но для того, чтобы это было полезно, мне нужно наложить плитки и полигоны карты, используя geoviews, Проблема в том, что geoviews.points() и связанная с этим проекция приводит к большому замедлению, которое делает интерактивный характер holoview + bokeh участки избыточны.

Ниже приведен воспроизводимый пример, но вкратце - я пытаюсь сделать реализацию geoviews (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) Просто даташадер

Просто использовать datashader без holoviews или geo очень быстро - вывод выводится за 4 секунды, включая агрегирование, поэтому повторные рендеринг будет происходить быстрее, если он будет интерактивным.

# 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 

Время для чистой версии datashader:

%%time
cvs = ds.Canvas(plot_width=plot_width, plot_height=plot_height, **bounds)
agg = cvs.points(df, 'longitude', 'latitude', ds.count())

Время ЦП: пользовательский 968 мс, системный: 29,9 мс, общее время: 998 мс Время нахождения на стенке: 506 мс

tf.shade(agg)

просто шейдер данных

(2) datashader в holoviews без 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)

Время ЦП: пользовательский 3,32 мс, sys: 131 мкс, общее время: 3,45 мс Время ожидания: 3,47 мс

shader

визуализация holoviews

(3) datashader в holoviews с geoviews плитки, полигоны и проекция

Вот суть проблемы - я хочу выровнять слой данных с некоторыми плитками карты и геопространственными полигонами. Это приводит к значительному замедлению, что для размера данных, с которыми я имею дело, делает избыточной интерактивную визуализацию. (Всего 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() и проекция

%%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)

Время процессора: пользовательский 11,8 с, системный: 3,16 с, всего: 15 с. Время настенного режима: 12,5 с.

shader * map_tiles * polys

Geoviews визуализации

1 ответ

Как предложено @philippjfr, решение состоит в том, чтобы спроецировать координаты в соответствующую систему координат и визуализировать, используя holoviews (вместо geoviews) (используя методы 2 выше).

Что-то вроде этого:

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, подробно описанным в вопросе.

Другие вопросы по тегам