Spaces:
Running
Running
Commit
·
0e1d4ae
1
Parent(s):
d0b05b4
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 |
)
|