Improve plot guide, add double clicking to plots (#9064)

* changes

* add changeset

* chages

* changes

* changes

* changes

* changes

---------

Co-authored-by: Ali Abid <aliabid94@gmail.com>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
aliabid94 2024-08-07 19:17:14 -07:00 committed by GitHub
parent 6778b8353d
commit 4ba7b238e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 169 additions and 6 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/nativeplot": minor
"gradio": minor
---
feat:Improve plot guide, add double clicking to plots

View File

@ -0,0 +1,13 @@
import pandas as pd
import numpy as np
import random
from datetime import datetime, timedelta
now = datetime.now()
df = pd.DataFrame({
'time': [now - timedelta(minutes=5*i) for i in range(25)],
'price': np.random.randint(100, 1000, 25),
'origin': [random.choice(["DFW", "DAL", "HOU"]) for _ in range(25)],
'destination': [random.choice(["JFK", "LGA", "EWR"]) for _ in range(25)],
})

View File

@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: plot_guide_filters_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/plot_guide_filters_events/data.py"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from data import df\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " origin = gr.Dropdown([\"All\", \"DFW\", \"DAL\", \"HOU\"], value=\"All\", label=\"Origin\")\n", " destination = gr.Dropdown([\"All\", \"JFK\", \"LGA\", \"EWR\"], value=\"All\", label=\"Destination\")\n", " max_price = gr.Slider(0, 1000, value=1000, label=\"Max Price\")\n", "\n", " plt = gr.ScatterPlot(df, x=\"time\", y=\"price\", inputs=[origin, destination, max_price])\n", "\n", " @gr.on(inputs=[origin, destination, max_price], outputs=plt)\n", " def filtered_data(origin, destination, max_price):\n", " _df = df[df[\"price\"] <= max_price]\n", " if origin != \"All\":\n", " _df = _df[_df[\"origin\"] == origin]\n", " if destination != \"All\":\n", " _df = _df[_df[\"destination\"] == destination]\n", " return _df\n", "\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -0,0 +1,23 @@
import gradio as gr
from data import df
with gr.Blocks() as demo:
with gr.Row():
origin = gr.Dropdown(["All", "DFW", "DAL", "HOU"], value="All", label="Origin")
destination = gr.Dropdown(["All", "JFK", "LGA", "EWR"], value="All", label="Destination")
max_price = gr.Slider(0, 1000, value=1000, label="Max Price")
plt = gr.ScatterPlot(df, x="time", y="price", inputs=[origin, destination, max_price])
@gr.on(inputs=[origin, destination, max_price], outputs=plt)
def filtered_data(origin, destination, max_price):
_df = df[df["price"] <= max_price]
if origin != "All":
_df = _df[_df["origin"] == origin]
if destination != "All":
_df = _df[_df["destination"] == destination]
return _df
if __name__ == "__main__":
demo.launch()

View File

@ -0,0 +1,10 @@
import pandas as pd
import numpy as np
import random
df = pd.DataFrame({
'height': np.random.randint(50, 70, 25),
'weight': np.random.randint(120, 320, 25),
'age': np.random.randint(18, 65, 25),
'ethnicity': [random.choice(["white", "black", "asian"]) for _ in range(25)]
})

View File

@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: plot_guide_selection"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/plot_guide_selection/data.py"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from data import df\n", "\n", "with gr.Blocks() as demo:\n", " plt = gr.LinePlot(df, x=\"weight\", y=\"height\")\n", " selection_total = gr.Number(label=\"Total Weight of Selection\")\n", "\n", " def select_region(selection: gr.SelectData):\n", " min_w, max_w = selection.index\n", " return df[(df[\"weight\"] >= min_w) & (df[\"weight\"] <= max_w)][\"weight\"].sum()\n", "\n", " plt.select(select_region, None, selection_total)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -0,0 +1,15 @@
import gradio as gr
from data import df
with gr.Blocks() as demo:
plt = gr.LinePlot(df, x="weight", y="height")
selection_total = gr.Number(label="Total Weight of Selection")
def select_region(selection: gr.SelectData):
min_w, max_w = selection.index
return df[(df["weight"] >= min_w) & (df["weight"] <= max_w)]["weight"].sum()
plt.select(select_region, None, selection_total)
if __name__ == "__main__":
demo.launch()

View File

@ -0,0 +1,10 @@
import pandas as pd
import numpy as np
import random
df = pd.DataFrame({
'height': np.random.randint(50, 70, 25),
'weight': np.random.randint(120, 320, 25),
'age': np.random.randint(18, 65, 25),
'ethnicity': [random.choice(["white", "black", "asian"]) for _ in range(25)]
})

View File

@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: plot_guide_zoom"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/plot_guide_zoom/data.py"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from data import df\n", "\n", "with gr.Blocks() as demo:\n", " plt = gr.LinePlot(df, x=\"weight\", y=\"height\")\n", "\n", " def select_region(selection: gr.SelectData):\n", " min_w, max_w = selection.index\n", " return gr.LinePlot(x_lim=(min_w, max_w)) # type: ignore\n", "\n", " plt.select(select_region, None, plt)\n", " plt.double_click(lambda: gr.LinePlot(x_lim=None), None, plt)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -0,0 +1,15 @@
import gradio as gr
from data import df
with gr.Blocks() as demo:
plt = gr.LinePlot(df, x="weight", y="height")
def select_region(selection: gr.SelectData):
min_w, max_w = selection.index
return gr.LinePlot(x_lim=(min_w, max_w)) # type: ignore
plt.select(select_region, None, plt)
plt.double_click(lambda: gr.LinePlot(x_lim=None), None, plt)
if __name__ == "__main__":
demo.launch()

View File

@ -0,0 +1,10 @@
import pandas as pd
import numpy as np
import random
df = pd.DataFrame({
'height': np.random.randint(50, 70, 25),
'weight': np.random.randint(120, 320, 25),
'age': np.random.randint(18, 65, 25),
'ethnicity': [random.choice(["white", "black", "asian"]) for _ in range(25)]
})

View File

@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: plot_guide_zoom_sync"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/plot_guide_zoom_sync/data.py"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from data import df\n", "\n", "with gr.Blocks() as demo:\n", " plt1 = gr.LinePlot(df, x=\"weight\", y=\"height\")\n", " plt2 = gr.BarPlot(df, x=\"weight\", y=\"age\", x_bin=10)\n", " plots = [plt1, plt2]\n", "\n", " def select_region(selection: gr.SelectData):\n", " min_w, max_w = selection.index\n", " return [gr.LinePlot(x_lim=(min_w, max_w))] * len(plots) # type: ignore\n", "\n", " for plt in plots:\n", " plt.select(select_region, None, plots)\n", " plt.double_click(lambda: [gr.LinePlot(x_lim=None)] * len(plots), None, plots)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -0,0 +1,18 @@
import gradio as gr
from data import df
with gr.Blocks() as demo:
plt1 = gr.LinePlot(df, x="weight", y="height")
plt2 = gr.BarPlot(df, x="weight", y="age", x_bin=10)
plots = [plt1, plt2]
def select_region(selection: gr.SelectData):
min_w, max_w = selection.index
return [gr.LinePlot(x_lim=(min_w, max_w))] * len(plots) # type: ignore
for plt in plots:
plt.select(select_region, None, plots)
plt.double_click(lambda: [gr.LinePlot(x_lim=None)] * len(plots), None, plots)
if __name__ == "__main__":
demo.launch()

View File

@ -38,7 +38,7 @@ class NativePlot(Component):
Demos: native_plots
"""
EVENTS = [Events.select]
EVENTS = [Events.select, Events.double_click]
def __init__(
self,

View File

@ -725,6 +725,9 @@ class Events:
doc="This listener is triggered when the user changes the value of the {{ component }}.",
)
click = EventListener("click", doc="Triggered when the {{ component }} is clicked.")
double_click = EventListener(
"double_click", doc="Triggered when the {{ component }} is double clicked."
)
submit = EventListener(
"submit",
doc="This listener is triggered when the user presses the Enter key while the {{ component }} is focused.",

1
gradio/hash_seed.txt Normal file
View File

@ -0,0 +1 @@
f7311342c5e04ba58dd320ef66cfecd0

View File

@ -45,6 +45,23 @@ If your x-axis is a string type instead, they will act as the category bins auto
$code_plot_guide_aggregate_nominal
$demo_plot_guide_aggregate_nominal
## Selecting Regions
You can use the `.select` listener to select regions of a plot. Click and drag on the plot below to select part of the plot.
$code_plot_guide_selection
$demo_plot_guide_selection
You can combine this and the `.double_click` listener to create some zoom in/out effects by changing `x_lim` which sets the bounds of the x-axis:
$code_plot_guide_zoom
$demo_plot_guide_zoom
If you had multiple plots with the same x column, your event listeners could target the x limits of all other plots so that the x-axes stay in sync.
$code_plot_guide_zoom_sync
$demo_plot_guide_zoom_sync
## Making an Interactive Dashboard
Take a look how you can have an interactive dashboard where the plots are functions of other Components.

View File

@ -18,7 +18,7 @@ $demo_plot_guide_aggregate_temporal
## DateTime Components
You can use `gr.DateTime` to accept input datetime data. This works well with plots for defining the x-axis range for the data. The `x_lim` attribute sets the x-axis bounds.
You can use `gr.DateTime` to accept input datetime data. This works well with plots for defining the x-axis range for the data.
$code_plot_guide_datetime
$demo_plot_guide_datetime
@ -36,7 +36,7 @@ Try zooming around in the plots and see how DateTimeRange updates. All the plots
## RealTime Data
In many cases, you're working with live, realtime date, not a static dataframe. In this case, you'd the plot to update regularly with a `gr.Timer()`. Assuming there's a `get_data` method that gets the latest dataframe:
In many cases, you're working with live, realtime date, not a static dataframe. In this case, you'd update the plot regularly with a `gr.Timer()`. Assuming there's a `get_data` method that gets the latest dataframe:
```python
with gr.Blocks() as demo:
@ -47,7 +47,7 @@ with gr.Blocks() as demo:
timer.tick(lambda: [get_data(), get_data()], outputs=[plot1, plot2])
```
You can use the `every` shorthand to attach a `Timer` to a component that has a function value:
You can also use the `every` shorthand to attach a `Timer` to a component that has a function value:
```python
with gr.Blocks() as demo:

View File

@ -4,10 +4,14 @@ Your dashboard will likely consist of more than just plots. Let's take a look at
## Filters
Use any of the standard Gradio form components to filter your data. Because the dataframe is not static, we'll use function-as-value format for the LinePlot value.
Use any of the standard Gradio form components to filter your data. You can do this via event listeners or function-as-value syntax. Let's look at the event listener approach first:
$code_plot_guide_filters_events
$demo_plot_guide_filters_events
And this would be the function-as-value approach for the same demo.
$code_plot_guide_filters
$demo_plot_guide_filters
## Tables and Stats

View File

@ -77,6 +77,7 @@
}[];
export let gradio: Gradio<{
select: SelectData;
double_click: undefined;
clear_status: LoadingStatus;
}>;
@ -159,6 +160,19 @@
view = result.view;
resizeObserver.observe(chart_element);
var debounceTimeout: NodeJS.Timeout;
view.addEventListener("dblclick", () => {
gradio.dispatch("double_click");
});
// prevent double-clicks from highlighting text
chart_element.addEventListener(
"mousedown",
function (e) {
if (e.detail > 1) {
e.preventDefault();
}
},
false
);
if (_selectable) {
view.addSignalListener("brush", function (_, value) {
if (Object.keys(value).length === 0) return;