Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into 414_item_table_source
Browse files Browse the repository at this point in the history
  • Loading branch information
jnumainville committed May 1, 2024
2 parents f58a038 + f342dad commit ff24f29
Show file tree
Hide file tree
Showing 53 changed files with 678 additions and 446 deletions.
704 changes: 362 additions & 342 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions plugins/ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.

- - -
## ui-v0.13.1 - 2024-04-22
#### Bug Fixes
- toggle_button was not passing through `on_change` (#427) - (d452a00) - mofojed

- - -

## ui-v0.13.0 - 2024-04-18
#### Bug Fixes
- Fix conditional use_effect in use_table_listener (#422) - (5f4f238) - Joe
Expand Down
29 changes: 16 additions & 13 deletions plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ ui.section(
###### Parameters

| Parameter | Type | Description |
| ----------- |---------------|-------------------------------------------|
| ----------- | ------------- | ----------------------------------------- |
| `*children` | `Item` | The options to render within the section. |
| `title` | `str \| None` | The title of the section. |
| `**props` | `Any` | Any other Section prop |
Expand Down Expand Up @@ -1282,7 +1282,7 @@ ui.list_view(
default_selected_keys: Selection | None = None,
selected_keys: Selection | None = None,
render_empty_state: Element | None = None,
on_selection_change: Callable[[Selection], None] | None = None,
on_selection_change: Callable[[Selection], None] | None = None,
on_change: Callable[[Selection], None] | None = None,
**props: Any
) -> ListViewElement
Expand All @@ -1292,15 +1292,14 @@ ui.list_view(
| Parameter | Type | Description |
|-------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `*children` | `Item \| Table \| ItemTableSource` | The options to render within the list_view. |
| `selection_mode` | `SelectionMode \| None` | By default `"MULTIPLE"`, which allows multiple selection. May also be `"SINGLE"` to allow only single selection, or `"None"`/`None` to allow no selection. |
| `selection_mode` | `SelectionMode \| None` | By default `"MULTIPLE"`, which allows multiple selection. May also be `"SINGLE"` to allow only single selection, or `"None"`/`None` to allow no selection. |
| `default_selected_keys` | `Selection \| None` | The initial selected keys in the collection (uncontrolled). |
| `selected_keys` | `Selection \| None` | The currently selected keys in the collection (controlled). |
| `render_empty_state` | `Element \| None` | Sets what the `list_view` should render when there is no content to display. |
| `on_selection_change` | `Callable[[Selection], None] \| None` | Handler that is called when the selections changes. |
| `on_change` | `Callable[[Selection], None] \| None` | Alias of `on_selection_change`. Handler that is called when the selections changes. |
| `**props` | `Any` | Any other [ListView](https://react-spectrum.adobe.com/react-spectrum/ListView.html) prop, with the exception of `items`, `dragAndDropHooks`, and `onLoadMore`. |


```py
import deephaven.ui as ui

Expand Down Expand Up @@ -1381,19 +1380,22 @@ list_view5 = ui.list_view(
```

###### ui.date_picker
A date picker that can be used to select a date.

A date picker that can be used to select a date.

There are three types that can be passed in to the props that control the date format:

1. `LocalDate`: A LocalDate is a date without a time zone in the ISO-8601 system, such as "2007-12-03" or "2057-01-28".
This will create a date picker with a granularity of days.
This will create a date picker with a granularity of days.
2. `Instant`: An Instant represents an unambiguous specific point on the timeline, such as 2021-04-12T14:13:07 UTC.
This will create a date picker with a granularity of seconds in UTC.
This will create a date picker with a granularity of seconds in UTC.
3. `ZonedDateTime`: A ZonedDateTime represents an unambiguous specific point on the timeline with an associated time zone, such as 2021-04-12T14:13:07 America/New_York.
This will create a date picker with a granularity of seconds in the specified time zone.
This will create a date picker with a granularity of seconds in the specified time zone.

The format of the date picker and the type of the value passed to the `on_change` handler
The format of the date picker and the type of the value passed to the `on_change` handler
is determined by the type of the following props in order of precedence:
1. `value`

1. `value`
2. `default_value`
3. `placeholder_value`

Expand All @@ -1415,8 +1417,9 @@ ui.date_picker(
```

###### Parameters

| Parameter | Type | Description |
|----------------------|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| -------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `placeholder_value` | `Date \| None` | A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone. |
| `value` | `Date \| None` | The current value (controlled). |
| `default_value` | `Date \| None` | The default value (uncontrolled). |
Expand Down Expand Up @@ -2200,7 +2203,7 @@ ListViewItem = Stringable | ItemElement
LocalDateConvertible = Union[None, LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]
InstantConvertible = Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
ZonedDateTimeConvertible = Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
Date = Instant | LocalDate | ZonedDateTime | LocalDateConvertible | InstantConvertible | ZonedDateTimeConvertible
Date = Instant | LocalDate | ZonedDateTime | LocalDateConvertible | InstantConvertible | ZonedDateTimeConvertible
Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"]
MenuTriggerAction = Literal["FOCUS", "INPUT", "MANUAL"]

Expand Down Expand Up @@ -2372,7 +2375,7 @@ tft = double_text_filter_table(_stocks)

Which should result in a UI like this:

![Double Text Filter Tables](examples/assets/double-tft.png)
![Double Text Filter Tables](docs/_assets/double-tft.png)

How does that look when the notebook is executed? When does each code block execute?

Expand Down
2 changes: 1 addition & 1 deletion plugins/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tox -e py

## Usage

Once you have the JS and python plugins installed and the server started, you can use deephaven.ui. See [examples](examples/README.md) for examples.
Once you have the JS and python plugins installed and the server started, you can use deephaven.ui. See [examples](docs/README.md) for examples.

## Logging

Expand Down
114 changes: 114 additions & 0 deletions plugins/ui/docs/Plotting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: Plotting and dh.ui
---

Creating dynamic plots that respond to user input is a common task in data analysis. The `dh.ui` module provides a simple interface for creating interactive plots using the `deephaven-express` library. This guide will show you how to create plots that updates based on user input.

## Plotting a filtered table

This example demonstrates how to create a simple line plot that updates based on user input. The plot will display the price of a stock filtered based on the stock symbol entered by the user. Here we have used a `ui.text_field` to get the value, but it could be driven by any dh.ui input, including double clicking on a value from a `ui.table`. We've previously referred to this sort of behaviour as a "one-click" component in enterprise, as the plot updates as soon as the user enters a filter.

```python
import deephaven.plot.express as dx
import deephaven.ui as ui

_stocks = dx.data.stocks()


@ui.component
def plot_filtered_table(table, initial_value):
text, set_text = ui.use_state("DOG")
# the filter is memoized so that it is only recalculated when the text changes
filtered_table = ui.use_memo(
lambda: table.where(f"sym = `{text.upper()}`"), [table, text]
)
return [
ui.text_field(value=text, on_change=set_text),
dx.line(filtered_table, x="timestamp", y="price", title=f"Filtered by: {text}"),
]


p = plot_filtered_table(_stocks, "DOG")
```

## Plotting a partitioned table

Using a partitioned table, as opposed to a where, can be more efficient if you are going to be filtering the same table multiple times with different values. This is because the partitioning is only done once, and then the key is selected from the partitioned table. Compared to using a where statement, it can be faster to return results, but at the expense of the query engine using more memory. Depending on the size of your table and the number of unique values in the partition key, this can be a tradeoff worth making or not.

```python
import deephaven.plot.express as dx
import deephaven.ui as ui

_stocks = dx.data.stocks()


@ui.component
def plot_partitioned_table(table, initial_value):
text, set_text = ui.use_state(initial_value)
# memoize the partition by so that it only performed once
partitioned_table = ui.use_memo(lambda: table.partition_by(["sym"]), [table])
constituent_table = ui.use_memo(
lambda: partitioned_table.get_constituent(text.upper()),
[partitioned_table, text],
)
return [
ui.text_field(value=text, on_change=set_text),
# only attempt to plot valid partition keys
dx.line(
constituent_table, x="timestamp", y="price", title=f"partition key: {text}"
)
if constituent_table != None
else ui.text("Please enter a valid partition."),
]


p = plot_partitioned_table(_stocks, "DOG")
```

## Combining a fitler and a partition by

Deephaven Express allows you to plot by a partition and assign unique colors to each key. Sometimes as a user you may also want to filter the data in addition to partitioning it. We've previously referred to this as a "one-click plot by" behaviour in enterprise. This can be done by either filtering the table first and then partitioning it, or partitioning it first and then filtering it. The choice of which to use depends on the size of the table and the number of unique values in the partition key. The first example is more like a traditional "one-click" component, and the second is more like a parameterized query. Both will give you the same result, but the first one may return results faster, whereas the second one may be more memory efficient.

```python
import deephaven.plot.express as dx
import deephaven.ui as ui

_stocks = dx.data.stocks()


@ui.component
def partition_then_filter(table, by, initial_value):
"""
Partition the table by both passed columns, then filter it by the value entered by the user
"""
text, set_text = ui.use_state(initial_value)
partitioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by])
filtered = ui.use_memo(
lambda: partitioned_table.filter(f"{by[0]} = `{text.upper()}`"),
[text, partitioned_table],
)
return [
ui.text_field(value=text, on_change=set_text),
dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]),
]


@ui.component
def where_then_partition(table, by, initial_value):
"""
Filter the table by the value entered by the user, then re-partition it by the second passed column
"""
text, set_text = ui.use_state(initial_value)
filtered = ui.use_memo(
lambda: table.where(f"{by[0]} = `{text.upper()}`"), [text, table]
)
return [
ui.text_field(value=text, on_change=set_text),
dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]),
]


# outputs the same thing, done two different ways depending on how you want the work done
ptf = partition_then_filter(_stocks, ["sym", "exchange"], "DOG")
wtp = where_then_partition(_stocks, ["sym", "exchange"], "DOG")
```
Loading

0 comments on commit ff24f29

Please sign in to comment.