holoviz/holonote

Add batch selection by category/description/type

Closed this issue · 4 comments

Issue:

In neuroscience multi-timeseries plots, the capability to select and modify all annotations associated with a specific description is crucial. Typically, these plots have fewer than 10 unique descriptions, yet a single description can encompass over 100 annotations. For efficient editing or removal of annotations under a specific description, a batch-select feature becomes indispensable. This batch-select is distinct but complementary to a shift-select feature (which also should be implemented), the latter being more apt for selecting a smaller number of annotations or those spanning multiple descriptions.

Proposed Solution:

Introduce a dropdown widget for category batch action:

  • The dropdown will list various description categories available.
  • Selecting an option from the dropdown will automatically select all annotations of that category for batch editing or removal.
  • Ensure synchronization between the dropdown values and the annotation table. Editing a description should reflect in the dropdown options.
  • The dropdown option colors should correspond to the color of the annotations in that description category.
  • The description field itself should be 'category' dtype.

This seems somewhat related to #42, although I'm not entirely clear on the scope of request on that one

Updating multiple fields is possible by using annotator.update_annotation_fields. Some of the other criteria you setup likely need to be more tailored as an app:

Setup annotator

import hvplot.pandas
import pandas as pd
from holonote.annotate import Annotator, SQLiteDB


speed_data = pd.read_parquet("../assets/example.parquet")
curve = speed_data.hvplot("TIME", "SPEED")

annotator = Annotator(
    curve,
    fields=["category", "description"],
    connector=SQLiteDB(table_name="example"),
)

start_time = pd.date_range("2022-06-04", "2022-06-22", periods=5)
end_time = start_time + pd.Timedelta(days=2)
data = {
    "start_time": start_time,
    "end_time": end_time,
    "category": ["A", "B", "A", "C", "B"],
    "description": ["A", "B", "A", "C", "B"],
}
annotator.define_annotations(pd.DataFrame(data), TIME=("start_time", "end_time"))

Creating the app:

import panel as pn


def update_test_input(v):
    df = annotator.df
    mask = df.category == v
    text_input.value = df[mask].iloc[0]["description"]


def update_index(*e):
    indexes = annotator.df.category == widget.value
    annotator.update_annotation_fields(indexes, description=text_input.value)


widget = pn.widgets.Select(value="A", options=["A", "B", "C"], name="Category")
text_input = pn.widgets.TextInput(name="Description")
button = pn.widgets.Button(name="Update")

annotator.groupby = "category"
# annotator.visible = widget.rx.pipe(lambda x: [x])  # If you want to only show the category itself

button.on_click(update_index)

update_test_input(widget.value)
pn.bind(update_test_input, widget, watch=True)

pn.Row(pn.Column(widget, text_input, button), annotator * curve)
screenrecord-2024-01-31_12.37.22.mp4

Small point just to avoid further confusion, I wanted to clarify something: your code snippet treats category and description as two different fields; I was using these terms interchangeably to mean the 'primary' categorical identifier for a set of annotations (mostly because I had seen the term 'description' used in this repo for such a field in the past).

But let's call this primary field 'Category' from now on in this issue.

So for each Category of annotations, we need a UI demonstration of the following:

  • Edit the name of an entire category (e.g. change Category 'A' to Category 'AA'). This change should be reflected in the UI dropdown options.
  • Delete an entire category
  • An way to display all the annotation categories at once, categorically colored, or display certain categories

I haven't yet figured out a way to do these things.

superseded by #99