Взаимодействие панели / Hvplot при изменении переменной

Я пытаюсь создать панель мониторинга с двумя объектами holoviews: объект panel pn.widgets.Select, содержащий список переменных xarray и объект hvplot, который принимает выбранную переменную на входе, например:

def hvmesh(var=None):
    mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, 
       width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
    return mesh

Вот как выглядит примерная сетка для конкретной переменной (имеющей размеры как по времени, так и по высоте): введите описание изображения здесь

Я хочу, чтобы карта обновлялась, когда я выбираю переменную в виджете панели: введите описание изображения здесь Я попытался сделать это как динамическую карту, например:

from holoviews.streams import Params
import holoviews as hv

var_stream = Params(var_select, ['value'], rename={'value': 'var'})

mesh = hv.DynamicMap(hvmesh, streams=[var_stream])

но когда я пытаюсь отобразить карту, я получаю:

Exception: Nesting a DynamicMap inside a DynamicMap is not supported.

Казалось бы, обычная необходимость выбрать переменную для hvplot из виджета панели. Как лучше всего добиться этого с помощью pyviz?

Если это будет полезно, вот моя полная попытка Jupyter Notebook.


person Rich Signell    schedule 19.02.2019    source источник
comment
Я не пробовал запускать такой код, как здесь, но первое, что я попробую, - это добавить dynamic=False к вашему .quadmesh() вызову, чтобы вы не вкладывали несколько динамических карт.   -  person James A. Bednar    schedule 19.02.2019
comment
Если я добавлю dynamic=False к вызову .quadmesh(), он, похоже, застрянет в каком-то цикле или что-то в этом роде. Ядро занято, сообщений об ошибках нет, но также не возвращен объект holoviews.   -  person Rich Signell    schedule 19.02.2019


Ответы (2)


Поскольку groupby изменяется с каждой выбранной переменной, список переменных не может быть передан в hvplot. Итак, одно из решений - просто воссоздавать график каждый раз, когда выбирается новая переменная. Это работает:

import holoviews as hv
from holoviews.streams import Params

def plot(var=None, tiles=None):
    var = var or var_select.value
    tiles = tiles or map_select.value
    mesh = ds[var].hvplot.quadmesh(x='x', y='y', rasterize=True, crs=crs, title=var,
                                   width=600, height=400, groupby=list(ds[var].dims[:-2]), 
                                   cmap='jet')
    return mesh.opts(alpha=0.7) * tiles

def on_var_select(event):
    var = event.obj.value
    col[-1] = plot(var=var)

def on_map_select(event):
    tiles = event.obj.value
    col[-1] = plot(tiles=tiles)

var_select.param.watch(on_var_select, parameter_names=['value']);
map_select.param.watch(on_map_select, parameter_names=['value']);

col = pn.Column(var_select, map_select, plot(var_select.value) * tiles)

производство:  введите описание изображения здесь

Вот полная записная книжка.

person Rich Signell    schedule 27.02.2019
comment
Ссылка на ноутбук вроде не работает. - person Qaswed; 25.02.2020

Итак, здесь есть длинный ответ и короткий ответ. Начнем с краткого ответа: нет необходимости создавать настраиваемый виджет выбора для переменной данных, поскольку hvPlot позволяет автоматически выбирать между несколькими переменными данных, поэтому, если вы измените его на это:

rasterized_mesh = ds[time_vars].hvplot.quadmesh(
    x='x', y='y', z=time_vars[::-1], crs=crs, width=600, height=400, 
    groupby=list(ds[var].dims[:-2]), rasterize=True, cmap='jet')

введите описание изображения здесь

Вы получите DynamicMap, который позволяет вам выбирать непространственные измерения и переменную данных, и теперь вы можете встроить их в свою панель без дополнительной работы. Если это все, что вас волнует, остановитесь здесь, поскольку мы собираемся углубиться в некоторые детали, чтобы, надеюсь, лучше понять.

Предположим на минуту, что hvPlot не позволяет выбирать между переменными данных, что бы мы тогда делали? Итак, главное, что вам нужно знать, это то, что HoloViews позволяет объединять динамические карты в цепочку, но не позволяет их вложение. Это может быть немного сложно осмыслить, но мы разделим проблему на несколько этапов, а затем посмотрим, как мы можем достичь того, чего хотим. Итак, какова цепочка событий, которая дала бы нам наш сюжет?

  1. Выберите переменную данных
  2. Нанесите groupby на непространственные измерения
  3. Примените растеризацию к каждой QuadMesh

Как вы знаете, hvPlot выполняет шаги 2 и 3 для нас, поэтому как мы можем ввести шаг 1 до 2 и 3. В будущем мы планируем добавить поддержку передачи виджетов панели непосредственно в hvPlot, что означает, что вы? Я смогу сделать все за один шаг. Поскольку панель все еще является очень новым проектом, я буду указывать на то, как наши API в конечном итоге сделают этот процесс тривиальным, но пока нам придется придерживаться относительно подробного обходного пути. В этом случае необходимо изменить порядок действий:

  1. Нанесите groupby на непространственные измерения
  2. Выберите переменную данных
  3. Примените растеризацию к каждой QuadMesh

Поэтому для начала мы выбираем все переменные данных и пропускаем растеризацию:

meshes = ds[time_vars].hvplot.quadmesh(
    x='x', y='y', z=time_vars, crs=crs, width=600, height=400, 
    groupby=list(ds[var].dims[:-2]))

Теперь, когда у нас есть DynamicMap, который содержит все данные, которые мы, возможно, захотим отобразить, мы можем применить следующие операции. Здесь мы воспользуемся утилитой hv.util.Dynamic, которую можно использовать для цепочки операций на DynamicMap при введении значений потока. В частности, на этом этапе мы создаем поток из виджета var_select, который будет использоваться для переиндексации QuadMesh внутри наших сеток DynamicMap:

def select_var(obj, var):
    return obj.clone(vdims=[var])

var_stream = Params(var_select, ['value'], rename={'value': 'var'})
var_mesh = hv.util.Dynamic(meshes, operation=select_var, streams=[var_select])

# Note starting in hv 1.12 you'll be able to replace this with
# var_mesh = meshes.map(select_var, streams=[var_select])

# And once param 2.0 is out we intend to support
# var_mesh = meshes.map(select_var, var=var_select.param.value)

Теперь у нас есть DynamicMap, который реагирует на изменения в виджете, но еще не растеризует его, поэтому мы можем применить операцию rasterize вручную:

rasterized_mesh = rasterize(var_mesh).opts(cmap='jet', width=600, height=400)

Теперь у нас есть DynamicMap, который связан с виджетом Selection, применяет groupby и растрирует, и теперь мы можем встроить его в панель. Другой подход, на который намекал @jbednar выше, - это сделать все это за один шаг, сделав вызов hvPlot не динамическим и сделав выбор времени и уровня высоты вручную. Я не буду вдаваться в подробности здесь, но это также действительный (хотя и менее эффективный) подход.

Как я намекал выше, в конечном итоге мы также намерены сделать все параметры hvPlot динамическими, что означает, что вы сможете сделать что-то вроде этого, чтобы связать значение виджета с аргументом ключевого слова hvPlot:

ds[time_vars].hvplot.quadmesh(
    x='x', y='y', z=var_select.param.value, rasterize=True, crs=crs,
    width=600, height=400, groupby=list(ds[var].dims[:-2]), cmap='jet')
person philippjfr    schedule 19.02.2019
comment
@phillipejfr, groupby необходимо изменить с выбранной переменной, поскольку разные переменные имеют разные непространственные измерения. Можно ли этого добиться, изменив ваши короткие и длинные ответы? - person Rich Signell; 19.02.2019
comment
В этом случае может быть проще использовать панель, чтобы полностью перерисовать график при изменении переменных, т.е. вы можете создать контейнер строки / столбца, и при изменении селектора переменной вы замените objects в контейнере. - person philippjfr; 19.02.2019
comment
Объект mesh, возвращенный из hvplot, отображается нормально, но panel.Row(mesh) возвращает ValueError: Slider 'start' and 'end' cannot be equal - person Rich Signell; 19.02.2019
comment
Я полагаю, нам понадобится исправление в panel, чтобы гарантировать пропуск постоянных размеров при создании ползунков, боке, похоже, не позволяет этого. - person philippjfr; 20.02.2019