Julian Bilcke
commited on
Commit
·
a0d973c
1
Parent(s):
937b72f
making progress
Browse files- docs/gradio/Interactive Plots.md +220 -0
- docs/gradio/Time Plots.md +185 -0
- vms/ui/monitoring/services/gpu.py +40 -142
- vms/ui/monitoring/services/monitoring.py +85 -136
- vms/ui/monitoring/tabs/general_tab.py +28 -12
- vms/ui/monitoring/tabs/gpu_tab.py +35 -12
docs/gradio/Interactive Plots.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Creating Plots
|
| 2 |
+
==============
|
| 3 |
+
|
| 4 |
+
Gradio is a great way to create extremely customizable dashboards. Gradio comes with three native Plot components: `gr.LinePlot`, `gr.ScatterPlot` and `gr.BarPlot`. All these plots have the same API. Let's take a look how to set them up.
|
| 5 |
+
|
| 6 |
+
Creating a Plot with a pd.Dataframe[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#creating-a-plot-with-a-pd-dataframe)
|
| 7 |
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 8 |
+
|
| 9 |
+
Plots accept a pandas Dataframe as their value. The plot also takes `x` and `y` which represent the names of the columns that represent the x and y axes respectively. Here's a simple example:
|
| 10 |
+
|
| 11 |
+
import gradio as gr
|
| 12 |
+
import pandas as pd
|
| 13 |
+
import numpy as np
|
| 14 |
+
import random
|
| 15 |
+
|
| 16 |
+
df = pd.DataFrame({
|
| 17 |
+
'height': np.random.randint(50, 70, 25),
|
| 18 |
+
'weight': np.random.randint(120, 320, 25),
|
| 19 |
+
'age': np.random.randint(18, 65, 25),
|
| 20 |
+
'ethnicity': [random.choice(["white", "black", "asian"]) for _ in range(25)]
|
| 21 |
+
})
|
| 22 |
+
|
| 23 |
+
with gr.Blocks() as demo:
|
| 24 |
+
gr.LinePlot(df, x="weight", y="height")
|
| 25 |
+
|
| 26 |
+
demo.launch()
|
| 27 |
+
|
| 28 |
+
Textbox
|
| 29 |
+
|
| 30 |
+
[gradio/plot\_guide\_line](https://huggingface.co/spaces/gradio/plot_guide_line) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 31 |
+
|
| 32 |
+
All plots have the same API, so you could swap this out with a `gr.ScatterPlot`:
|
| 33 |
+
|
| 34 |
+
import gradio as gr
|
| 35 |
+
from data import df
|
| 36 |
+
|
| 37 |
+
with gr.Blocks() as demo:
|
| 38 |
+
gr.ScatterPlot(df, x="weight", y="height")
|
| 39 |
+
|
| 40 |
+
demo.launch()
|
| 41 |
+
|
| 42 |
+
Textbox
|
| 43 |
+
|
| 44 |
+
[gradio/plot\_guide\_scatter](https://huggingface.co/spaces/gradio/plot_guide_scatter) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 45 |
+
|
| 46 |
+
The y axis column in the dataframe should have a numeric type, but the x axis column can be anything from strings, numbers, categories, or datetimes.
|
| 47 |
+
|
| 48 |
+
import gradio as gr
|
| 49 |
+
from data import df
|
| 50 |
+
|
| 51 |
+
with gr.Blocks() as demo:
|
| 52 |
+
gr.ScatterPlot(df, x="ethnicity", y="height")
|
| 53 |
+
|
| 54 |
+
demo.launch()
|
| 55 |
+
|
| 56 |
+
Loading...
|
| 57 |
+
|
| 58 |
+
[gradio/plot\_guide\_scatter\_nominal](https://huggingface.co/spaces/gradio/plot_guide_scatter_nominal) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 59 |
+
|
| 60 |
+
Breaking out Series by Color[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#breaking-out-series-by-color)
|
| 61 |
+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 62 |
+
|
| 63 |
+
You can break out your plot into series using the `color` argument.
|
| 64 |
+
|
| 65 |
+
import gradio as gr
|
| 66 |
+
from data import df
|
| 67 |
+
|
| 68 |
+
with gr.Blocks() as demo:
|
| 69 |
+
gr.ScatterPlot(df, x="weight", y="height", color="ethnicity")
|
| 70 |
+
|
| 71 |
+
demo.launch()
|
| 72 |
+
|
| 73 |
+
Loading...
|
| 74 |
+
|
| 75 |
+
[gradio/plot\_guide\_series\_nominal](https://huggingface.co/spaces/gradio/plot_guide_series_nominal) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 76 |
+
|
| 77 |
+
If you wish to assign series specific colors, use the `color_map` arg, e.g. `gr.ScatterPlot(..., color_map={'white': '#FF9988', 'asian': '#88EEAA', 'black': '#333388'})`
|
| 78 |
+
|
| 79 |
+
The color column can be numeric type as well.
|
| 80 |
+
|
| 81 |
+
import gradio as gr
|
| 82 |
+
from data import df
|
| 83 |
+
|
| 84 |
+
with gr.Blocks() as demo:
|
| 85 |
+
gr.ScatterPlot(df, x="weight", y="height", color="age")
|
| 86 |
+
|
| 87 |
+
demo.launch()
|
| 88 |
+
|
| 89 |
+
Loading...
|
| 90 |
+
|
| 91 |
+
[gradio/plot\_guide\_series\_quantitative](https://huggingface.co/spaces/gradio/plot_guide_series_quantitative) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 92 |
+
|
| 93 |
+
Aggregating Values[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#aggregating-values)
|
| 94 |
+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 95 |
+
|
| 96 |
+
You can aggregate values into groups using the `x_bin` and `y_aggregate` arguments. If your x-axis is numeric, providing an `x_bin` will create a histogram-style binning:
|
| 97 |
+
|
| 98 |
+
import gradio as gr
|
| 99 |
+
from data import df
|
| 100 |
+
|
| 101 |
+
with gr.Blocks() as demo:
|
| 102 |
+
gr.BarPlot(df, x="weight", y="height", x_bin=10, y_aggregate="sum")
|
| 103 |
+
|
| 104 |
+
demo.launch()
|
| 105 |
+
|
| 106 |
+
Loading...
|
| 107 |
+
|
| 108 |
+
[gradio/plot\_guide\_aggregate\_quantitative](https://huggingface.co/spaces/gradio/plot_guide_aggregate_quantitative) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 109 |
+
|
| 110 |
+
If your x-axis is a string type instead, they will act as the category bins automatically:
|
| 111 |
+
|
| 112 |
+
import gradio as gr
|
| 113 |
+
from data import df
|
| 114 |
+
|
| 115 |
+
with gr.Blocks() as demo:
|
| 116 |
+
gr.BarPlot(df, x="ethnicity", y="height", y_aggregate="mean")
|
| 117 |
+
|
| 118 |
+
demo.launch()
|
| 119 |
+
|
| 120 |
+
Loading...
|
| 121 |
+
|
| 122 |
+
[gradio/plot\_guide\_aggregate\_nominal](https://huggingface.co/spaces/gradio/plot_guide_aggregate_nominal) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 123 |
+
|
| 124 |
+
Selecting Regions[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#selecting-regions)
|
| 125 |
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 126 |
+
|
| 127 |
+
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.
|
| 128 |
+
|
| 129 |
+
import gradio as gr
|
| 130 |
+
from data import df
|
| 131 |
+
|
| 132 |
+
with gr.Blocks() as demo:
|
| 133 |
+
plt = gr.LinePlot(df, x="weight", y="height")
|
| 134 |
+
selection_total = gr.Number(label="Total Weight of Selection")
|
| 135 |
+
|
| 136 |
+
def select_region(selection: gr.SelectData):
|
| 137 |
+
min_w, max_w = selection.index
|
| 138 |
+
return df[(df["weight"] >= min_w) & (df["weight"] <= max_w)]["weight"].sum()
|
| 139 |
+
|
| 140 |
+
plt.select(select_region, None, selection_total)
|
| 141 |
+
|
| 142 |
+
demo.launch()
|
| 143 |
+
|
| 144 |
+
Loading...
|
| 145 |
+
|
| 146 |
+
[gradio/plot\_guide\_selection](https://huggingface.co/spaces/gradio/plot_guide_selection) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 147 |
+
|
| 148 |
+
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:
|
| 149 |
+
|
| 150 |
+
import gradio as gr
|
| 151 |
+
from data import df
|
| 152 |
+
|
| 153 |
+
with gr.Blocks() as demo:
|
| 154 |
+
plt = gr.LinePlot(df, x="weight", y="height")
|
| 155 |
+
|
| 156 |
+
def select_region(selection: gr.SelectData):
|
| 157 |
+
min_w, max_w = selection.index
|
| 158 |
+
return gr.LinePlot(x_lim=(min_w, max_w))
|
| 159 |
+
|
| 160 |
+
plt.select(select_region, None, plt)
|
| 161 |
+
plt.double_click(lambda: gr.LinePlot(x_lim=None), None, plt)
|
| 162 |
+
|
| 163 |
+
demo.launch()
|
| 164 |
+
|
| 165 |
+
Loading...
|
| 166 |
+
|
| 167 |
+
[gradio/plot\_guide\_zoom](https://huggingface.co/spaces/gradio/plot_guide_zoom) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 168 |
+
|
| 169 |
+
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.
|
| 170 |
+
|
| 171 |
+
import gradio as gr
|
| 172 |
+
from data import df
|
| 173 |
+
|
| 174 |
+
with gr.Blocks() as demo:
|
| 175 |
+
plt1 = gr.LinePlot(df, x="weight", y="height")
|
| 176 |
+
plt2 = gr.BarPlot(df, x="weight", y="age", x_bin=10)
|
| 177 |
+
plots = [plt1, plt2]
|
| 178 |
+
|
| 179 |
+
def select_region(selection: gr.SelectData):
|
| 180 |
+
min_w, max_w = selection.index
|
| 181 |
+
return [gr.LinePlot(x_lim=(min_w, max_w))] * len(plots)
|
| 182 |
+
|
| 183 |
+
for plt in plots:
|
| 184 |
+
plt.select(select_region, None, plots)
|
| 185 |
+
plt.double_click(lambda: [gr.LinePlot(x_lim=None)] * len(plots), None, plots)
|
| 186 |
+
|
| 187 |
+
demo.launch()
|
| 188 |
+
|
| 189 |
+
Loading...
|
| 190 |
+
|
| 191 |
+
[gradio/plot\_guide\_zoom\_sync](https://huggingface.co/spaces/gradio/plot_guide_zoom_sync) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 192 |
+
|
| 193 |
+
Making an Interactive Dashboard[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#making-an-interactive-dashboard)
|
| 194 |
+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 195 |
+
|
| 196 |
+
Take a look how you can have an interactive dashboard where the plots are functions of other Components.
|
| 197 |
+
|
| 198 |
+
import gradio as gr
|
| 199 |
+
from data import df
|
| 200 |
+
|
| 201 |
+
with gr.Blocks() as demo:
|
| 202 |
+
with gr.Row():
|
| 203 |
+
ethnicity = gr.Dropdown(["all", "white", "black", "asian"], value="all")
|
| 204 |
+
max_age = gr.Slider(18, 65, value=65)
|
| 205 |
+
|
| 206 |
+
def filtered_df(ethnic, age):
|
| 207 |
+
_df = df if ethnic == "all" else df[df["ethnicity"] == ethnic]
|
| 208 |
+
_df = _df[_df["age"] < age]
|
| 209 |
+
return _df
|
| 210 |
+
|
| 211 |
+
gr.ScatterPlot(filtered_df, inputs=[ethnicity, max_age], x="weight", y="height", title="Weight x Height")
|
| 212 |
+
gr.LinePlot(filtered_df, inputs=[ethnicity, max_age], x="age", y="height", title="Age x Height")
|
| 213 |
+
|
| 214 |
+
demo.launch()
|
| 215 |
+
|
| 216 |
+
Loading...
|
| 217 |
+
|
| 218 |
+
[gradio/plot\_guide\_interactive](https://huggingface.co/spaces/gradio/plot_guide_interactive) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 219 |
+
|
| 220 |
+
It's that simple to filter and control the data presented in your visualization!
|
docs/gradio/Time Plots.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
1. Data Science And Plots
|
| 2 |
+
2. Time Plots
|
| 3 |
+
|
| 4 |
+
[
|
| 5 |
+
|
| 6 |
+
←
|
| 7 |
+
|
| 8 |
+
Creating Plots
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
](../guides/creating-plots/)[
|
| 13 |
+
|
| 14 |
+
Filters Tables And Stats
|
| 15 |
+
|
| 16 |
+
→
|
| 17 |
+
|
| 18 |
+
](../guides/filters-tables-and-stats/)
|
| 19 |
+
|
| 20 |
+
Time Plots
|
| 21 |
+
==========
|
| 22 |
+
|
| 23 |
+
Creating visualizations with a time x-axis is a common use case. Let's dive in!
|
| 24 |
+
|
| 25 |
+
Creating a Plot with a pd.Dataframe[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#creating-a-plot-with-a-pd-dataframe)
|
| 26 |
+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 27 |
+
|
| 28 |
+
Time plots need a datetime column on the x-axis. Here's a simple example with some flight data:
|
| 29 |
+
|
| 30 |
+
import gradio as gr
|
| 31 |
+
import pandas as pd
|
| 32 |
+
import numpy as np
|
| 33 |
+
import random
|
| 34 |
+
|
| 35 |
+
from datetime import datetime, timedelta
|
| 36 |
+
now = datetime.now()
|
| 37 |
+
|
| 38 |
+
df = pd.DataFrame({
|
| 39 |
+
'time': [now - timedelta(minutes=5*i) for i in range(25)],
|
| 40 |
+
'price': np.random.randint(100, 1000, 25),
|
| 41 |
+
'origin': [random.choice(["DFW", "DAL", "HOU"]) for _ in range(25)],
|
| 42 |
+
'destination': [random.choice(["JFK", "LGA", "EWR"]) for _ in range(25)],
|
| 43 |
+
})
|
| 44 |
+
|
| 45 |
+
with gr.Blocks() as demo:
|
| 46 |
+
gr.LinePlot(df, x="time", y="price")
|
| 47 |
+
gr.ScatterPlot(df, x="time", y="price", color="origin")
|
| 48 |
+
|
| 49 |
+
demo.launch()
|
| 50 |
+
|
| 51 |
+
Textbox
|
| 52 |
+
|
| 53 |
+
Textbox
|
| 54 |
+
|
| 55 |
+
[gradio/plot\_guide\_temporal](https://huggingface.co/spaces/gradio/plot_guide_temporal) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 56 |
+
|
| 57 |
+
Aggregating by Time[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#aggregating-by-time)
|
| 58 |
+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 59 |
+
|
| 60 |
+
You may wish to bin data by time buckets. Use `x_bin` to do so, using a string suffix with "s", "m", "h" or "d", such as "15m" or "1d".
|
| 61 |
+
|
| 62 |
+
import gradio as gr
|
| 63 |
+
from data import df
|
| 64 |
+
|
| 65 |
+
with gr.Blocks() as demo:
|
| 66 |
+
plot = gr.BarPlot(df, x="time", y="price", x_bin="10m")
|
| 67 |
+
|
| 68 |
+
bins = gr.Radio(["10m", "30m", "1h"], label="Bin Size")
|
| 69 |
+
bins.change(lambda bins: gr.BarPlot(x_bin=bins), bins, plot)
|
| 70 |
+
|
| 71 |
+
demo.launch()
|
| 72 |
+
|
| 73 |
+
Textbox
|
| 74 |
+
|
| 75 |
+
Bin Size
|
| 76 |
+
|
| 77 |
+
10m 30m 1h
|
| 78 |
+
|
| 79 |
+
[gradio/plot\_guide\_aggregate\_temporal](https://huggingface.co/spaces/gradio/plot_guide_aggregate_temporal) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 80 |
+
|
| 81 |
+
DateTime Components[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#date-time-components)
|
| 82 |
+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 83 |
+
|
| 84 |
+
You can use `gr.DateTime` to accept input datetime data. This works well with plots for defining the x-axis range for the data.
|
| 85 |
+
|
| 86 |
+
import gradio as gr
|
| 87 |
+
from data import df
|
| 88 |
+
|
| 89 |
+
with gr.Blocks() as demo:
|
| 90 |
+
with gr.Row():
|
| 91 |
+
start = gr.DateTime("now - 24h")
|
| 92 |
+
end = gr.DateTime("now")
|
| 93 |
+
apply_btn = gr.Button("Apply")
|
| 94 |
+
plot = gr.LinePlot(df, x="time", y="price")
|
| 95 |
+
|
| 96 |
+
apply_btn.click(lambda start, end: gr.BarPlot(x_lim=[start, end]), [start, end], plot)
|
| 97 |
+
|
| 98 |
+
demo.launch()
|
| 99 |
+
|
| 100 |
+
Time
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
Time
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
Apply
|
| 109 |
+
|
| 110 |
+
Textbox
|
| 111 |
+
|
| 112 |
+
[gradio/plot\_guide\_datetime](https://huggingface.co/spaces/gradio/plot_guide_datetime) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 113 |
+
|
| 114 |
+
Note how `gr.DateTime` can accept a full datetime string, or a shorthand using `now - [0-9]+[smhd]` format to refer to a past time.
|
| 115 |
+
|
| 116 |
+
You will often have many time plots in which case you'd like to keep the x-axes in sync. The `DateTimeRange` custom component keeps a set of datetime plots in sync, and also uses the `.select` listener of plots to allow you to zoom into plots while keeping plots in sync.
|
| 117 |
+
|
| 118 |
+
Because it is a custom component, you first need to `pip install gradio_datetimerange`. Then run the following:
|
| 119 |
+
|
| 120 |
+
import gradio as gr
|
| 121 |
+
from gradio_datetimerange import DateTimeRange
|
| 122 |
+
from data import df
|
| 123 |
+
|
| 124 |
+
with gr.Blocks() as demo:
|
| 125 |
+
daterange = DateTimeRange(["now - 24h", "now"])
|
| 126 |
+
plot1 = gr.LinePlot(df, x="time", y="price")
|
| 127 |
+
plot2 = gr.LinePlot(df, x="time", y="price", color="origin")
|
| 128 |
+
daterange.bind([plot1, plot2])
|
| 129 |
+
|
| 130 |
+
demo.launch()
|
| 131 |
+
|
| 132 |
+
Time
|
| 133 |
+
|
| 134 |
+
Back Last 15mLast 1hLast 24h
|
| 135 |
+
|
| 136 |
+
Time
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
Time
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
Textbox
|
| 145 |
+
|
| 146 |
+
Textbox
|
| 147 |
+
|
| 148 |
+
[gradio/plot\_guide\_datetimerange](https://huggingface.co/spaces/gradio/plot_guide_datetimerange) built with [Gradio](https://gradio.app). Hosted on [ Spaces](https://huggingface.co/spaces)
|
| 149 |
+
|
| 150 |
+
Try zooming around in the plots and see how DateTimeRange updates. All the plots updates their `x_lim` in sync. You also have a "Back" link in the component to allow you to quickly zoom in and out.
|
| 151 |
+
|
| 152 |
+
RealTime Data[%20Copyright%202022%20Fonticons,%20Inc.%20--%3e%3cpath%20d='M172.5%20131.1C228.1%2075.51%20320.5%2075.51%20376.1%20131.1C426.1%20181.1%20433.5%20260.8%20392.4%20318.3L391.3%20319.9C381%20334.2%20361%20337.6%20346.7%20327.3C332.3%20317%20328.9%20297%20339.2%20282.7L340.3%20281.1C363.2%20249%20359.6%20205.1%20331.7%20177.2C300.3%20145.8%20249.2%20145.8%20217.7%20177.2L105.5%20289.5C73.99%20320.1%2073.99%20372%20105.5%20403.5C133.3%20431.4%20177.3%20435%20209.3%20412.1L210.9%20410.1C225.3%20400.7%20245.3%20404%20255.5%20418.4C265.8%20432.8%20262.5%20452.8%20248.1%20463.1L246.5%20464.2C188.1%20505.3%20110.2%20498.7%2060.21%20448.8C3.741%20392.3%203.741%20300.7%2060.21%20244.3L172.5%20131.1zM467.5%20380C411%20436.5%20319.5%20436.5%20263%20380C213%20330%20206.5%20251.2%20247.6%20193.7L248.7%20192.1C258.1%20177.8%20278.1%20174.4%20293.3%20184.7C307.7%20194.1%20311.1%20214.1%20300.8%20229.3L299.7%20230.9C276.8%20262.1%20280.4%20306.9%20308.3%20334.8C339.7%20366.2%20390.8%20366.2%20422.3%20334.8L534.5%20222.5C566%20191%20566%20139.1%20534.5%20108.5C506.7%2080.63%20462.7%2076.99%20430.7%2099.9L429.1%20101C414.7%20111.3%20394.7%20107.1%20384.5%2093.58C374.2%2079.2%20377.5%2059.21%20391.9%2048.94L393.5%2047.82C451%206.731%20529.8%2013.25%20579.8%2063.24C636.3%20119.7%20636.3%20211.3%20579.8%20267.7L467.5%20380z'/%3e%3c/svg%3e)](#real-time-data)
|
| 153 |
+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
| 154 |
+
|
| 155 |
+
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:
|
| 156 |
+
|
| 157 |
+
with gr.Blocks() as demo:
|
| 158 |
+
timer = gr.Timer(5)
|
| 159 |
+
plot1 = gr.BarPlot(x="time", y="price")
|
| 160 |
+
plot2 = gr.BarPlot(x="time", y="price", color="origin")
|
| 161 |
+
|
| 162 |
+
timer.tick(lambda: [get_data(), get_data()], outputs=[plot1, plot2])
|
| 163 |
+
|
| 164 |
+
You can also use the `every` shorthand to attach a `Timer` to a component that has a function value:
|
| 165 |
+
|
| 166 |
+
with gr.Blocks() as demo:
|
| 167 |
+
timer = gr.Timer(5)
|
| 168 |
+
plot1 = gr.BarPlot(get_data, x="time", y="price", every=timer)
|
| 169 |
+
plot2 = gr.BarPlot(get_data, x="time", y="price", color="origin", every=timer)
|
| 170 |
+
|
| 171 |
+
[
|
| 172 |
+
|
| 173 |
+
←
|
| 174 |
+
|
| 175 |
+
Creating Plots
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
](../guides/creating-plots/)[
|
| 180 |
+
|
| 181 |
+
Filters Tables And Stats
|
| 182 |
+
|
| 183 |
+
→
|
| 184 |
+
|
| 185 |
+
](../guides/filters-tables-and-stats/)
|
vms/ui/monitoring/services/gpu.py
CHANGED
|
@@ -10,11 +10,8 @@ from typing import Dict, List, Any, Optional, Tuple
|
|
| 10 |
from collections import deque
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
-
# Force the use of the Agg backend which is thread-safe
|
| 14 |
-
import matplotlib
|
| 15 |
-
matplotlib.use('Agg') # Must be before importing pyplot
|
| 16 |
-
import matplotlib.pyplot as plt
|
| 17 |
import numpy as np
|
|
|
|
| 18 |
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
logger.setLevel(logging.INFO)
|
|
@@ -307,180 +304,81 @@ class GPUMonitoringService:
|
|
| 307 |
"""
|
| 308 |
return self.collect_gpu_metrics()
|
| 309 |
|
| 310 |
-
def
|
| 311 |
-
"""
|
| 312 |
|
| 313 |
Args:
|
| 314 |
-
gpu_index: Index of the GPU to
|
| 315 |
|
| 316 |
Returns:
|
| 317 |
-
|
| 318 |
"""
|
| 319 |
-
plt.close('all') # Close all existing figures
|
| 320 |
-
|
| 321 |
-
plt.style.use('dark_background')
|
| 322 |
-
|
| 323 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 324 |
-
|
| 325 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 326 |
-
|
| 327 |
-
return fig
|
| 328 |
|
| 329 |
history = self.history[gpu_index]
|
| 330 |
if not history['timestamps']:
|
| 331 |
-
|
| 332 |
-
return fig
|
| 333 |
|
| 334 |
-
# Convert
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
ax.set_xticks(range(0, len(x), step))
|
| 341 |
-
ax.set_xticklabels([x[i] for i in range(0, len(x), step)], rotation=45)
|
| 342 |
-
|
| 343 |
-
# Plot utilization
|
| 344 |
-
ax.plot(x, list(history['utilization']), 'b-', label='GPU Utilization %')
|
| 345 |
-
ax.set_ylim(0, 100)
|
| 346 |
-
|
| 347 |
-
# Add temperature on secondary y-axis
|
| 348 |
-
ax2 = ax.twinx()
|
| 349 |
-
ax2.plot(x, list(history['temperature']), 'r-', label='Temperature °C')
|
| 350 |
-
ax2.set_ylabel('Temperature (°C)', color='r')
|
| 351 |
-
ax2.tick_params(axis='y', colors='r')
|
| 352 |
-
|
| 353 |
-
# Set labels and title
|
| 354 |
-
ax.set_title(f'GPU {gpu_index} Utilization Over Time')
|
| 355 |
-
ax.set_xlabel('Time')
|
| 356 |
-
ax.set_ylabel('Utilization %')
|
| 357 |
-
ax.grid(True, alpha=0.3)
|
| 358 |
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
lines2, labels2 = ax2.get_legend_handles_labels()
|
| 362 |
-
ax.legend(lines + lines2, labels + labels2, loc='upper left')
|
| 363 |
-
|
| 364 |
-
plt.tight_layout()
|
| 365 |
-
return fig
|
| 366 |
-
|
| 367 |
-
def generate_memory_plot(self, gpu_index: int) -> plt.Figure:
|
| 368 |
-
"""Generate a plot of GPU memory usage over time
|
| 369 |
|
| 370 |
Args:
|
| 371 |
-
gpu_index: Index of the GPU to
|
| 372 |
|
| 373 |
Returns:
|
| 374 |
-
|
| 375 |
"""
|
| 376 |
-
plt.close('all') # Close all existing figures
|
| 377 |
-
|
| 378 |
-
plt.style.use('dark_background')
|
| 379 |
-
|
| 380 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 381 |
-
|
| 382 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 383 |
-
|
| 384 |
-
return fig
|
| 385 |
|
| 386 |
history = self.history[gpu_index]
|
| 387 |
if not history['timestamps']:
|
| 388 |
-
|
| 389 |
-
return fig
|
| 390 |
|
| 391 |
-
# Convert
|
| 392 |
-
x = [t.strftime('%H:%M:%S') for t in history['timestamps']]
|
| 393 |
-
|
| 394 |
-
# If we have many points, show fewer labels for readability
|
| 395 |
-
if len(x) > 10:
|
| 396 |
-
step = len(x) // 10
|
| 397 |
-
ax.set_xticks(range(0, len(x), step))
|
| 398 |
-
ax.set_xticklabels([x[i] for i in range(0, len(x), step)], rotation=45)
|
| 399 |
-
|
| 400 |
-
# Plot memory percentage
|
| 401 |
-
ax.plot(x, list(history['memory_percent']), 'g-', label='Memory Usage %')
|
| 402 |
-
ax.set_ylim(0, 100)
|
| 403 |
-
|
| 404 |
-
# Add absolute memory values on secondary y-axis (convert to GB)
|
| 405 |
-
ax2 = ax.twinx()
|
| 406 |
memory_used_gb = [m / (1024**3) for m in history['memory_used']]
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
# Set labels and title
|
| 413 |
-
ax.set_title(f'GPU {gpu_index} Memory Usage Over Time')
|
| 414 |
-
ax.set_xlabel('Time')
|
| 415 |
-
ax.set_ylabel('Usage %')
|
| 416 |
-
ax.grid(True, alpha=0.3)
|
| 417 |
-
|
| 418 |
-
# Add legend
|
| 419 |
-
lines, labels = ax.get_legend_handles_labels()
|
| 420 |
-
lines2, labels2 = ax2.get_legend_handles_labels()
|
| 421 |
-
ax.legend(lines + lines2, labels + labels2, loc='upper left')
|
| 422 |
-
|
| 423 |
-
plt.tight_layout()
|
| 424 |
-
return fig
|
| 425 |
|
| 426 |
-
def
|
| 427 |
-
"""
|
| 428 |
|
| 429 |
Args:
|
| 430 |
-
gpu_index: Index of the GPU to
|
| 431 |
|
| 432 |
Returns:
|
| 433 |
-
|
| 434 |
"""
|
| 435 |
-
plt.close('all') # Close all existing figures
|
| 436 |
-
|
| 437 |
-
plt.style.use('dark_background')
|
| 438 |
-
|
| 439 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 440 |
-
|
| 441 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 442 |
-
|
| 443 |
-
return fig
|
| 444 |
|
| 445 |
history = self.history[gpu_index]
|
| 446 |
if not history['timestamps'] or not any(history['power_usage']):
|
| 447 |
-
|
| 448 |
-
return fig
|
| 449 |
|
| 450 |
-
|
| 451 |
-
x = [t.strftime('%H:%M:%S') for t in history['timestamps']]
|
| 452 |
-
|
| 453 |
-
# If we have many points, show fewer labels for readability
|
| 454 |
-
if len(x) > 10:
|
| 455 |
-
step = len(x) // 10
|
| 456 |
-
ax.set_xticks(range(0, len(x), step))
|
| 457 |
-
ax.set_xticklabels([x[i] for i in range(0, len(x), step)], rotation=45)
|
| 458 |
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
# Get power limit if available
|
| 465 |
-
power_limit = list(history['power_limit'])
|
| 466 |
-
if any(power_limit): # Only plot if we have power limit data
|
| 467 |
-
# Show power limit as horizontal line
|
| 468 |
-
limit = max(power_limit) # Should be constant, but take max just in case
|
| 469 |
-
if limit > 0:
|
| 470 |
-
ax.axhline(y=limit, color='r', linestyle='--', label=f'Power Limit ({limit}W)')
|
| 471 |
-
|
| 472 |
-
# Set labels and title
|
| 473 |
-
ax.set_title(f'GPU {gpu_index} Power Usage Over Time')
|
| 474 |
-
ax.set_xlabel('Time')
|
| 475 |
-
ax.set_ylabel('Power (Watts)')
|
| 476 |
-
ax.grid(True, alpha=0.3)
|
| 477 |
-
ax.legend(loc='upper left')
|
| 478 |
-
else:
|
| 479 |
-
ax.set_title(f"Power data not available for GPU {gpu_index}")
|
| 480 |
|
| 481 |
-
|
| 482 |
-
|
| 483 |
|
|
|
|
|
|
|
| 484 |
def shutdown(self):
|
| 485 |
"""Clean up resources when shutting down"""
|
| 486 |
self.stop_monitoring()
|
|
|
|
| 10 |
from collections import deque
|
| 11 |
from datetime import datetime
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
import numpy as np
|
| 14 |
+
import pandas as pd
|
| 15 |
|
| 16 |
logger = logging.getLogger(__name__)
|
| 17 |
logger.setLevel(logging.INFO)
|
|
|
|
| 304 |
"""
|
| 305 |
return self.collect_gpu_metrics()
|
| 306 |
|
| 307 |
+
def get_utilization_data(self, gpu_index: int) -> pd.DataFrame:
|
| 308 |
+
"""Get utilization data as a DataFrame
|
| 309 |
|
| 310 |
Args:
|
| 311 |
+
gpu_index: Index of the GPU to get data for
|
| 312 |
|
| 313 |
Returns:
|
| 314 |
+
DataFrame with time, utilization, and temperature
|
| 315 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 317 |
+
return pd.DataFrame()
|
|
|
|
| 318 |
|
| 319 |
history = self.history[gpu_index]
|
| 320 |
if not history['timestamps']:
|
| 321 |
+
return pd.DataFrame()
|
|
|
|
| 322 |
|
| 323 |
+
# Convert to dataframe
|
| 324 |
+
return pd.DataFrame({
|
| 325 |
+
'time': list(history['timestamps']),
|
| 326 |
+
'GPU Utilization (%)': list(history['utilization']),
|
| 327 |
+
'Temperature (°C)': list(history['temperature'])
|
| 328 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
+
def get_memory_data(self, gpu_index: int) -> pd.DataFrame:
|
| 331 |
+
"""Get memory data as a DataFrame
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
|
| 333 |
Args:
|
| 334 |
+
gpu_index: Index of the GPU to get data for
|
| 335 |
|
| 336 |
Returns:
|
| 337 |
+
DataFrame with time, memory percent, and memory used
|
| 338 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 340 |
+
return pd.DataFrame()
|
|
|
|
| 341 |
|
| 342 |
history = self.history[gpu_index]
|
| 343 |
if not history['timestamps']:
|
| 344 |
+
return pd.DataFrame()
|
|
|
|
| 345 |
|
| 346 |
+
# Convert to dataframe
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
memory_used_gb = [m / (1024**3) for m in history['memory_used']]
|
| 348 |
+
return pd.DataFrame({
|
| 349 |
+
'time': list(history['timestamps']),
|
| 350 |
+
'Memory Usage (%)': list(history['memory_percent']),
|
| 351 |
+
'Memory Used (GB)': memory_used_gb
|
| 352 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
|
| 354 |
+
def get_power_data(self, gpu_index: int) -> pd.DataFrame:
|
| 355 |
+
"""Get power data as a DataFrame
|
| 356 |
|
| 357 |
Args:
|
| 358 |
+
gpu_index: Index of the GPU to get data for
|
| 359 |
|
| 360 |
Returns:
|
| 361 |
+
DataFrame with time and power usage
|
| 362 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
if not self.has_nvidia_gpus or gpu_index not in self.history:
|
| 364 |
+
return pd.DataFrame()
|
|
|
|
| 365 |
|
| 366 |
history = self.history[gpu_index]
|
| 367 |
if not history['timestamps'] or not any(history['power_usage']):
|
| 368 |
+
return pd.DataFrame()
|
|
|
|
| 369 |
|
| 370 |
+
power_limit = max(history['power_limit']) if any(history['power_limit']) else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
+
df = pd.DataFrame({
|
| 373 |
+
'time': list(history['timestamps']),
|
| 374 |
+
'Power Usage (W)': list(history['power_usage'])
|
| 375 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
|
| 377 |
+
if power_limit and power_limit > 0:
|
| 378 |
+
df['Power Limit (W)'] = power_limit
|
| 379 |
|
| 380 |
+
return df
|
| 381 |
+
|
| 382 |
def shutdown(self):
|
| 383 |
"""Clean up resources when shutting down"""
|
| 384 |
self.stop_monitoring()
|
vms/ui/monitoring/services/monitoring.py
CHANGED
|
@@ -8,19 +8,13 @@ import time
|
|
| 8 |
import logging
|
| 9 |
import platform
|
| 10 |
import threading
|
|
|
|
| 11 |
from datetime import datetime, timedelta
|
| 12 |
from collections import deque
|
| 13 |
from typing import Dict, List, Optional, Tuple, Any
|
| 14 |
|
| 15 |
import psutil
|
| 16 |
|
| 17 |
-
# Force the use of the Agg backend which is thread-safe
|
| 18 |
-
import matplotlib
|
| 19 |
-
matplotlib.use('Agg') # Must be before importing pyplot
|
| 20 |
-
import matplotlib.pyplot as plt
|
| 21 |
-
|
| 22 |
-
import numpy as np
|
| 23 |
-
|
| 24 |
from vms.ui.monitoring.services.gpu import GPUMonitoringService
|
| 25 |
|
| 26 |
logger = logging.getLogger(__name__)
|
|
@@ -230,157 +224,112 @@ class MonitoringService:
|
|
| 230 |
'system': sys_info,
|
| 231 |
}
|
| 232 |
|
| 233 |
-
def
|
| 234 |
-
"""
|
| 235 |
|
| 236 |
Returns:
|
| 237 |
-
|
| 238 |
"""
|
| 239 |
-
plt.close('all') # Close all existing figures
|
| 240 |
-
|
| 241 |
-
plt.style.use('dark_background')
|
| 242 |
-
|
| 243 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 244 |
-
|
| 245 |
if not self.timestamps:
|
| 246 |
-
|
| 247 |
-
|
|
|
|
|
|
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
ax.set_xticks(range(0, len(x), step))
|
| 254 |
-
ax.set_xticklabels([x[i] for i in range(0, len(x), step)])
|
| 255 |
-
|
| 256 |
-
ax.plot(x, list(self.cpu_percent), 'b-', label='CPU Usage %')
|
| 257 |
|
|
|
|
| 258 |
if self.cpu_temp and len(self.cpu_temp) > 0:
|
| 259 |
-
#
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
|
|
|
|
|
|
| 264 |
|
| 265 |
-
|
| 266 |
-
ax.set_xlabel('Time')
|
| 267 |
-
ax.set_ylabel('Usage %')
|
| 268 |
-
ax.grid(True, alpha=0.3)
|
| 269 |
-
ax.set_ylim(0, 100)
|
| 270 |
-
|
| 271 |
-
# Add legend
|
| 272 |
-
lines, labels = ax.get_legend_handles_labels()
|
| 273 |
-
if hasattr(locals(), 'ax2'):
|
| 274 |
-
lines2, labels2 = ax2.get_legend_handles_labels()
|
| 275 |
-
ax.legend(lines + lines2, labels + labels2, loc='upper left')
|
| 276 |
-
else:
|
| 277 |
-
ax.legend(loc='upper left')
|
| 278 |
-
|
| 279 |
-
plt.tight_layout()
|
| 280 |
-
return fig
|
| 281 |
|
| 282 |
-
def
|
| 283 |
-
"""
|
| 284 |
|
| 285 |
Returns:
|
| 286 |
-
|
| 287 |
"""
|
| 288 |
-
plt.close('all') # Close all existing figures
|
| 289 |
-
|
| 290 |
-
plt.style.use('dark_background')
|
| 291 |
-
|
| 292 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 293 |
-
|
| 294 |
if not self.timestamps:
|
| 295 |
-
|
| 296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
plt.tight_layout()
|
| 325 |
-
return fig
|
| 326 |
|
| 327 |
-
|
| 328 |
-
|
|
|
|
| 329 |
|
| 330 |
Returns:
|
| 331 |
-
|
| 332 |
"""
|
| 333 |
-
|
| 334 |
-
if
|
| 335 |
-
|
| 336 |
-
plt.close('all') # Close all existing figures
|
| 337 |
-
|
| 338 |
-
plt.style.use('dark_background')
|
| 339 |
-
|
| 340 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
| 341 |
-
ax.set_title("No per-core CPU data available yet")
|
| 342 |
-
return fig
|
| 343 |
|
| 344 |
-
#
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
elif num_cores <= 6:
|
| 348 |
-
rows, cols = 2, 3
|
| 349 |
-
elif num_cores <= 9:
|
| 350 |
-
rows, cols = 3, 3
|
| 351 |
-
elif num_cores <= 12:
|
| 352 |
-
rows, cols = 3, 4
|
| 353 |
-
else:
|
| 354 |
-
rows, cols = 4, 4
|
| 355 |
-
|
| 356 |
-
fig, axes = plt.subplots(rows, cols, figsize=(12, 8), sharex=True, sharey=True)
|
| 357 |
-
axes = axes.flatten()
|
| 358 |
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
# Show fewer x-axis labels for readability
|
| 362 |
-
step = len(x) // 5
|
| 363 |
-
else:
|
| 364 |
-
step = 1
|
| 365 |
-
|
| 366 |
-
for i, (core_id, percentages) in enumerate(self.cpu_cores_percent.items()):
|
| 367 |
-
if i >= len(axes):
|
| 368 |
-
break
|
| 369 |
-
|
| 370 |
-
ax = axes[i]
|
| 371 |
-
ax.plot(x[:len(percentages)], list(percentages), 'b-')
|
| 372 |
-
ax.set_title(f'Core {core_id}')
|
| 373 |
-
ax.set_ylim(0, 100)
|
| 374 |
-
ax.grid(True, alpha=0.3)
|
| 375 |
-
|
| 376 |
-
# Add x-axis labels sparingly for readability
|
| 377 |
-
if i >= len(axes) - cols: # Only for bottom row
|
| 378 |
-
ax.set_xticks(range(0, len(x), step))
|
| 379 |
-
ax.set_xticklabels([x[i] for i in range(0, len(x), step)], rotation=45)
|
| 380 |
-
|
| 381 |
-
# Hide unused subplots
|
| 382 |
-
for i in range(num_cores, len(axes)):
|
| 383 |
-
axes[i].set_visible(False)
|
| 384 |
|
| 385 |
-
|
| 386 |
-
return fig
|
|
|
|
| 8 |
import logging
|
| 9 |
import platform
|
| 10 |
import threading
|
| 11 |
+
import pandas as pd
|
| 12 |
from datetime import datetime, timedelta
|
| 13 |
from collections import deque
|
| 14 |
from typing import Dict, List, Optional, Tuple, Any
|
| 15 |
|
| 16 |
import psutil
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
from vms.ui.monitoring.services.gpu import GPUMonitoringService
|
| 19 |
|
| 20 |
logger = logging.getLogger(__name__)
|
|
|
|
| 224 |
'system': sys_info,
|
| 225 |
}
|
| 226 |
|
| 227 |
+
def get_cpu_data(self) -> pd.DataFrame:
|
| 228 |
+
"""Get CPU usage data as a DataFrame
|
| 229 |
|
| 230 |
Returns:
|
| 231 |
+
DataFrame with CPU usage data
|
| 232 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
if not self.timestamps:
|
| 234 |
+
return pd.DataFrame({
|
| 235 |
+
'time': list(),
|
| 236 |
+
'CPU Usage (%)': list()
|
| 237 |
+
})
|
| 238 |
|
| 239 |
+
data = {
|
| 240 |
+
'time': list(self.timestamps),
|
| 241 |
+
'CPU Usage (%)': list(self.cpu_percent)
|
| 242 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
|
| 244 |
+
# Add temperature if available
|
| 245 |
if self.cpu_temp and len(self.cpu_temp) > 0:
|
| 246 |
+
# Ensure temperature data aligns with timestamps
|
| 247 |
+
# If fewer temperature readings than timestamps, pad with None
|
| 248 |
+
temp_data = list(self.cpu_temp)
|
| 249 |
+
if len(temp_data) < len(self.timestamps):
|
| 250 |
+
padding = [None] * (len(self.timestamps) - len(temp_data))
|
| 251 |
+
temp_data = padding + temp_data
|
| 252 |
+
data['CPU Temperature (°C)'] = temp_data
|
| 253 |
|
| 254 |
+
return pd.DataFrame(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
|
| 256 |
+
def get_memory_data(self) -> pd.DataFrame:
|
| 257 |
+
"""Get memory usage data as a DataFrame
|
| 258 |
|
| 259 |
Returns:
|
| 260 |
+
DataFrame with memory usage data
|
| 261 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
if not self.timestamps:
|
| 263 |
+
return pd.DataFrame({
|
| 264 |
+
'time': list(),
|
| 265 |
+
'Memory Usage (%)': list(),
|
| 266 |
+
'Memory Used (GB)': list(),
|
| 267 |
+
'Memory Available (GB)': list()
|
| 268 |
+
})
|
| 269 |
|
| 270 |
+
return pd.DataFrame({
|
| 271 |
+
'time': list(self.timestamps),
|
| 272 |
+
'Memory Usage (%)': list(self.memory_percent),
|
| 273 |
+
'Memory Used (GB)': list(self.memory_used),
|
| 274 |
+
'Memory Available (GB)': list(self.memory_available)
|
| 275 |
+
})
|
| 276 |
+
|
| 277 |
+
def get_per_core_data(self) -> Dict[int, pd.DataFrame]:
|
| 278 |
+
"""Get per-core CPU usage data as DataFrames
|
| 279 |
|
| 280 |
+
Returns:
|
| 281 |
+
Dictionary of DataFrames with per-core CPU usage data
|
| 282 |
+
"""
|
| 283 |
+
if not self.timestamps or not self.cpu_cores_percent:
|
| 284 |
+
return {}
|
| 285 |
+
|
| 286 |
+
core_data = {}
|
| 287 |
+
for core_id, percentages in self.cpu_cores_percent.items():
|
| 288 |
+
# Ensure we don't have more data points than timestamps
|
| 289 |
+
data_length = min(len(percentages), len(self.timestamps))
|
| 290 |
+
core_data[core_id] = pd.DataFrame({
|
| 291 |
+
'time': list(self.timestamps)[-data_length:],
|
| 292 |
+
f'Core {core_id} Usage (%)': list(percentages)[-data_length:]
|
| 293 |
+
})
|
| 294 |
+
|
| 295 |
+
return core_data
|
| 296 |
|
| 297 |
+
# Replace matplotlib methods with DataFrame methods
|
| 298 |
+
|
| 299 |
+
# This method is kept for backward compatibility but returns a DataFrame
|
| 300 |
+
def generate_cpu_plot(self) -> pd.DataFrame:
|
| 301 |
+
"""Get CPU usage data for plotting
|
| 302 |
|
| 303 |
+
Returns:
|
| 304 |
+
DataFrame with CPU usage data
|
| 305 |
+
"""
|
| 306 |
+
return self.get_cpu_data()
|
| 307 |
+
|
| 308 |
+
# This method is kept for backward compatibility but returns a DataFrame
|
| 309 |
+
def generate_memory_plot(self) -> pd.DataFrame:
|
| 310 |
+
"""Get memory usage data for plotting
|
| 311 |
|
| 312 |
+
Returns:
|
| 313 |
+
DataFrame with memory usage data
|
| 314 |
+
"""
|
| 315 |
+
return self.get_memory_data()
|
|
|
|
|
|
|
|
|
|
| 316 |
|
| 317 |
+
# This method is kept for backward compatibility but returns a DataFrame of all cores
|
| 318 |
+
def generate_per_core_plot(self) -> pd.DataFrame:
|
| 319 |
+
"""Get per-core CPU usage data for plotting
|
| 320 |
|
| 321 |
Returns:
|
| 322 |
+
Combined DataFrame with all cores' usage data
|
| 323 |
"""
|
| 324 |
+
core_data = self.get_per_core_data()
|
| 325 |
+
if not core_data:
|
| 326 |
+
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
|
| 328 |
+
# Combine all core data into a single DataFrame using the first core's timestamps
|
| 329 |
+
first_core_id = list(core_data.keys())[0]
|
| 330 |
+
combined_df = core_data[first_core_id][['time']].copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
+
for core_id, df in core_data.items():
|
| 333 |
+
combined_df[f'Core {core_id} Usage (%)'] = df[f'Core {core_id} Usage (%)']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
+
return combined_df
|
|
|
vms/ui/monitoring/tabs/general_tab.py
CHANGED
|
@@ -8,6 +8,7 @@ import time
|
|
| 8 |
import logging
|
| 9 |
from pathlib import Path
|
| 10 |
import os
|
|
|
|
| 11 |
import psutil
|
| 12 |
from typing import Dict, Any, List, Optional, Tuple
|
| 13 |
from datetime import datetime, timedelta
|
|
@@ -42,14 +43,30 @@ class GeneralTab(BaseTab):
|
|
| 42 |
# CPU and Memory charts in tabs
|
| 43 |
with gr.Tabs() as metrics_tabs:
|
| 44 |
with gr.Tab(label="CPU Usage") as cpu_tab:
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
with gr.Tab(label="Memory Usage") as memory_tab:
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
#with gr.Tab(label="Per-Core CPU") as per_core_tab:
|
| 51 |
-
# self.components["per_core_plot"] = gr.Plot()
|
| 52 |
-
|
| 53 |
# System information summary in columns
|
| 54 |
with gr.Row():
|
| 55 |
with gr.Column(scale=1):
|
|
@@ -171,10 +188,9 @@ class GeneralTab(BaseTab):
|
|
| 171 |
# current_metrics = self.app.monitoring.get_current_metrics()
|
| 172 |
metrics_html = "" # self.format_current_metrics(current_metrics)
|
| 173 |
|
| 174 |
-
#
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
#per_core_plot = self.app.monitoring.generate_per_core_plot()
|
| 178 |
|
| 179 |
return (
|
| 180 |
system_info_html,
|
|
@@ -182,8 +198,8 @@ class GeneralTab(BaseTab):
|
|
| 182 |
memory_info_html,
|
| 183 |
storage_info_html,
|
| 184 |
metrics_html,
|
| 185 |
-
|
| 186 |
-
|
| 187 |
#per_core_plot
|
| 188 |
)
|
| 189 |
|
|
|
|
| 8 |
import logging
|
| 9 |
from pathlib import Path
|
| 10 |
import os
|
| 11 |
+
import pandas as pd
|
| 12 |
import psutil
|
| 13 |
from typing import Dict, Any, List, Optional, Tuple
|
| 14 |
from datetime import datetime, timedelta
|
|
|
|
| 43 |
# CPU and Memory charts in tabs
|
| 44 |
with gr.Tabs() as metrics_tabs:
|
| 45 |
with gr.Tab(label="CPU Usage") as cpu_tab:
|
| 46 |
+
empty_cpu_df = pd.DataFrame({'time': [], 'CPU Usage (%)': []})
|
| 47 |
+
self.components["cpu_plot"] = gr.LinePlot(
|
| 48 |
+
empty_cpu_df,
|
| 49 |
+
show_label=False,
|
| 50 |
+
height=400,
|
| 51 |
+
x="time",
|
| 52 |
+
y=["CPU Usage (%)"],
|
| 53 |
+
y_title="Value",
|
| 54 |
+
title="CPU Usage and Temperature"
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
with gr.Tab(label="Memory Usage") as memory_tab:
|
| 58 |
+
empty_memory_df = pd.DataFrame({'time': [], 'Memory Usage (%)': [], 'Memory Used (GB)': [], 'Memory Available (GB)': []})
|
| 59 |
+
|
| 60 |
+
self.components["memory_plot"] = gr.LinePlot(
|
| 61 |
+
empty_memory_df,
|
| 62 |
+
show_label=False,
|
| 63 |
+
height=400,
|
| 64 |
+
x="time",
|
| 65 |
+
y=["Memory Usage (%)", "Memory Used (GB)", "Memory Available (GB)"],
|
| 66 |
+
y_title="Value",
|
| 67 |
+
title="Memory Usage"
|
| 68 |
+
)
|
| 69 |
|
|
|
|
|
|
|
|
|
|
| 70 |
# System information summary in columns
|
| 71 |
with gr.Row():
|
| 72 |
with gr.Column(scale=1):
|
|
|
|
| 188 |
# current_metrics = self.app.monitoring.get_current_metrics()
|
| 189 |
metrics_html = "" # self.format_current_metrics(current_metrics)
|
| 190 |
|
| 191 |
+
# Get DataFrame from monitoring service
|
| 192 |
+
cpu_plot_df = self.app.monitoring.get_cpu_data()
|
| 193 |
+
memory_plot_df = self.app.monitoring.get_memory_data()
|
|
|
|
| 194 |
|
| 195 |
return (
|
| 196 |
system_info_html,
|
|
|
|
| 198 |
memory_info_html,
|
| 199 |
storage_info_html,
|
| 200 |
metrics_html,
|
| 201 |
+
cpu_plot_df,
|
| 202 |
+
memory_plot_df,
|
| 203 |
#per_core_plot
|
| 204 |
)
|
| 205 |
|
vms/ui/monitoring/tabs/gpu_tab.py
CHANGED
|
@@ -59,13 +59,34 @@ class GPUTab(BaseTab):
|
|
| 59 |
# Display GPU metrics in tabs
|
| 60 |
with gr.Tabs(visible=self.app.monitoring.gpu.has_nvidia_gpus) as metrics_tabs:
|
| 61 |
with gr.Tab(label="Utilization") as util_tab:
|
| 62 |
-
self.components["utilization_plot"] = gr.
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
with gr.Tab(label="Memory") as memory_tab:
|
| 65 |
-
self.components["memory_plot"] = gr.
|
| 66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
with gr.Tab(label="Power") as power_tab:
|
| 68 |
-
self.components["power_plot"] = gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
# Process information
|
| 71 |
with gr.Row(visible=self.app.monitoring.gpu.has_nvidia_gpus):
|
|
@@ -190,6 +211,7 @@ class GPUTab(BaseTab):
|
|
| 190 |
Returns:
|
| 191 |
Updated values for all components
|
| 192 |
"""
|
|
|
|
| 193 |
try:
|
| 194 |
if not self.app.monitoring.gpu.has_nvidia_gpus:
|
| 195 |
return (
|
|
@@ -226,16 +248,17 @@ class GPUTab(BaseTab):
|
|
| 226 |
gpu_info = self.app.monitoring.gpu.get_gpu_info()
|
| 227 |
gpu_info_html = self.format_gpu_info(gpu_info[self.selected_gpu] if self.selected_gpu < len(gpu_info) else {})
|
| 228 |
|
| 229 |
-
#
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
|
|
|
| 233 |
|
| 234 |
return (
|
| 235 |
metrics_html,
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
process_info_html,
|
| 240 |
gpu_info_html
|
| 241 |
)
|
|
|
|
| 59 |
# Display GPU metrics in tabs
|
| 60 |
with gr.Tabs(visible=self.app.monitoring.gpu.has_nvidia_gpus) as metrics_tabs:
|
| 61 |
with gr.Tab(label="Utilization") as util_tab:
|
| 62 |
+
self.components["utilization_plot"] = gr.LinePlot(
|
| 63 |
+
show_label=False,
|
| 64 |
+
height=400,
|
| 65 |
+
x="time",
|
| 66 |
+
y=["GPU Utilization (%)", "Temperature (°C)"],
|
| 67 |
+
y_title="Value",
|
| 68 |
+
title="GPU Utilization and Temperature"
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
with gr.Tab(label="Memory") as memory_tab:
|
| 72 |
+
self.components["memory_plot"] = gr.LinePlot(
|
| 73 |
+
show_label=False,
|
| 74 |
+
height=400,
|
| 75 |
+
x="time",
|
| 76 |
+
y=["Memory Usage (%)", "Memory Used (GB)"],
|
| 77 |
+
y_title="Value",
|
| 78 |
+
title="GPU Memory Usage"
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
with gr.Tab(label="Power") as power_tab:
|
| 82 |
+
self.components["power_plot"] = gr.LinePlot(
|
| 83 |
+
show_label=False,
|
| 84 |
+
height=400,
|
| 85 |
+
x="time",
|
| 86 |
+
y=["Power Usage (W)", "Power Limit (W)"],
|
| 87 |
+
y_title="Watts",
|
| 88 |
+
title="GPU Power Usage"
|
| 89 |
+
)
|
| 90 |
|
| 91 |
# Process information
|
| 92 |
with gr.Row(visible=self.app.monitoring.gpu.has_nvidia_gpus):
|
|
|
|
| 211 |
Returns:
|
| 212 |
Updated values for all components
|
| 213 |
"""
|
| 214 |
+
|
| 215 |
try:
|
| 216 |
if not self.app.monitoring.gpu.has_nvidia_gpus:
|
| 217 |
return (
|
|
|
|
| 248 |
gpu_info = self.app.monitoring.gpu.get_gpu_info()
|
| 249 |
gpu_info_html = self.format_gpu_info(gpu_info[self.selected_gpu] if self.selected_gpu < len(gpu_info) else {})
|
| 250 |
|
| 251 |
+
# Get DataFrame from monitoring service
|
| 252 |
+
utilization_plot_df = self.app.monitoring.gpu.get_utilization_data(self.selected_gpu)
|
| 253 |
+
memory_plot_df = self.app.monitoring.gpu.get_memory_data(self.selected_gpu)
|
| 254 |
+
power_plot_df = self.app.monitoring.gpu.get_power_data(self.selected_gpu)
|
| 255 |
+
|
| 256 |
|
| 257 |
return (
|
| 258 |
metrics_html,
|
| 259 |
+
utilization_plot_df,
|
| 260 |
+
memory_plot_df,
|
| 261 |
+
power_plot_df,
|
| 262 |
process_info_html,
|
| 263 |
gpu_info_html
|
| 264 |
)
|