Geoviews + Datashader работает медленно при проецировании точек - PullRequest
0 голосов
/ 29 ноября 2018

Я строю 550 000 000 широт и долгот, используя 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)

just data shader

(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 render

(3) datashader inholoviews с 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 render

1 Ответ

0 голосов
/ 30 ноября 2018

Как предлагает @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, подробно обсуждается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...