Skip to content

Commit c60edb6

Browse files
authored
Merge pull request #1 from JonasLoos/custom-plotting
Custom plotting
2 parents 0535e05 + 33dc470 commit c60edb6

13 files changed

Lines changed: 528 additions & 730 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ share/python-wheels/
2626
.installed.cfg
2727
*.egg
2828
MANIFEST
29+
uv.lock
2930

3031
# PyInstaller
3132
# Usually these files are written by a python script from a template

README.md

Lines changed: 20 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,50 @@
11
# trainplot
22

3-
Dynamically updating plots in Jupyter notebooks, e.g. for visualizing training progress. Inspired by [livelossplot](https://github.com/stared/livelossplot), and aims to be easier to use with better jupyter notebook support.
4-
5-
6-
## Installation
3+
Dynamically updating plots in Jupyter notebooks, e.g. for visualizing machine learning training progress.
74

85
```bash
6+
97
pip install trainplot
108
```
119

10+
<p align="center"><img src="https://github.com/user-attachments/assets/03bd661c-37d7-41a4-ba91-891f57ebfcf8" width="400" center></p>
11+
12+
1213

1314
## Usage
1415

15-
This is a simple example ([example notebook](examples/basic-example.ipynb)):
16+
Basic usage:
1617

1718
```python
1819
from trainplot import plot
19-
from time import sleep
2020

21-
for i in range(50):
22-
plot(loss = 1/(i+1), acc = 1-1/(.01*i**2+1))
23-
sleep(.1)
21+
for i in range(100):
22+
loss = ...
23+
acc = ...
24+
plot(loss=loss, accuracy=acc)
2425
```
2526

26-
<img src="https://github.com/JonasLoos/trainplot/assets/33965649/935e8d52-0c37-4469-9cb8-24fa77b467ff" width="500">
27-
28-
---
29-
30-
Example for the tf/keras callback ([example notebook](examples/tf-keras-mnist-example.ipynb)):
27+
If you use keras, you can use the `TrainPlotKerasCallback`:
3128

3229
```python
3330
from trainplot import TrainPlotKeras
3431

3532
model = ...
36-
model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, callbacks=[TrainPlotKeras()])
37-
```
38-
39-
<img src="https://github.com/JonasLoos/trainplot/assets/33965649/4ddff79a-978e-434c-a6c3-571cf48c0892" width="500">
40-
41-
---
42-
43-
It also works together with e.g. `tqdm.notebook` and printing ([example notebook](examples/different-output-example.ipynb)):
44-
45-
```python
46-
from trainplot import plot
47-
from tqdm.notebook import trange
48-
from time import sleep
49-
50-
for i in trange(50):
51-
plot(i=i, root=i**.5)
52-
if i % 10 == 0:
53-
print(f'currently at {i} iterations')
54-
sleep(0.1)
33+
model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, callbacks=[TrainPlotKerasCallback()])
5534
```
5635

57-
<img src="https://github.com/JonasLoos/trainplot/assets/33965649/7571efab-7a3f-4414-b537-a2dffd9e1bec" width="400">
36+
For more examples, see the [`examples`](examples/) folder.
5837

59-
---
60-
61-
You can make use of a TrainPlot object to add a bunch of custumizations ([example notebook](examples/4plots-example.ipynb)):
62-
63-
```python
64-
from trainplot import TrainPlot
65-
from time import sleep
66-
67-
tp = TrainPlot(
68-
update_period=.2,
69-
fig_args=dict(nrows=2, ncols=2, figsize=(10, 8), gridspec_kw={'height_ratios': [1, 1], 'width_ratios': [1, 1]}),
70-
plot_pos={'loss': (0, 0, 0), 'accuracy': (0, 1, 0), 'val_loss': (1, 0, 0), 'val_accuracy': (1, 1, 0)},
71-
plot_args={'loss': {'color': 'orange'}, 'accuracy': {'color': 'green'}, 'val_loss': {'color': 'orange', 'label': 'validation loss'}, 'val_accuracy': {'color': 'green', 'label': 'validation accuracy'}},
72-
)
73-
74-
for i in range(100, 200):
75-
tp(step=i, loss=(i/100-2)**4, accuracy=i/2, val_loss=(i/100-2.1)**4, val_accuracy=i/2.1)
76-
sleep(0.1)
77-
```
7838

79-
<img src="https://github.com/JonasLoos/trainplot/assets/33965649/599314e2-d1c1-4044-a915-6316722a2324" width="600">
39+
## Features
8040

81-
---
41+
* **Lightweight**: No external plotting dependencies
42+
* **Custom rendering**: Uses HTML5 Canvas for fast, smooth updates
43+
* **Multiple series**: Automatically handles multiple data series with different colors
44+
* **Real-time updates**: Configurable update periods to balance performance and responsiveness
45+
* **Keras support**: Built-in callback for TensorFlow/Keras models
8246

83-
More:
84-
* When using a Trainplot object, you can also put the plot into a separate cell than the training loop: [example notebook](examples/separate-output-example.ipynb)
85-
* Experimental plotly support (`from trainplot.trainplot import TrainPlotPlotlyExperimental`): [example notebook](examples/plotly-example.ipynb)
8647

8748
## How it works
8849

89-
Trainplot outputs the matplotlib figure to an `ipywidgets.Output` widget, so it doesn't interfere with other outputs like `tqdm` or print statements. To avoid wasting resources and flickering, the figure is only updated with a given `update_period`.
90-
A `post_run_cell` callback is added to the `IPython` instance, so that all updated TrainPlot figures include all new data when a cell execution is finished.
91-
When using `trainplot.plot`, a TrainPlot object is created for the current cell and cell-execution-count.
50+
Trainplot uses a custom HTML5 Canvas-based plotting solution that renders directly in Jupyter notebooks. For synchronization between Python and the JavaScript-based plotting function, [`anywidget`](https://github.com/manzt/anywidget) is used. To avoid wasting resources and flickering, the plot is only updated with a given `update_period`. A `post_run_cell` callback is added to the `IPython` instance, so that all updated TrainPlot figures include all new data when a cell execution is finished. When using `trainplot.plot`, a TrainPlot object is created for the current cell.

examples/4plots-example.ipynb

Lines changed: 0 additions & 55 deletions
This file was deleted.

examples/basic-example.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"cell_type": "markdown",
2828
"metadata": {},
2929
"source": [
30-
"You could do the same also by using a trainplot object:"
30+
"You can do the same also by using a trainplot object:"
3131
]
3232
},
3333
{
@@ -55,7 +55,7 @@
5555
],
5656
"metadata": {
5757
"kernelspec": {
58-
"display_name": "base",
58+
"display_name": ".venv",
5959
"language": "python",
6060
"name": "python3"
6161
},
@@ -69,7 +69,7 @@
6969
"name": "python",
7070
"nbconvert_exporter": "python",
7171
"pygments_lexer": "ipython3",
72-
"version": "3.11.4"
72+
"version": "3.13.3"
7373
}
7474
},
7575
"nbformat": 4,

examples/different-output-example.ipynb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# Different Output Example\n",
7+
"# Different Outputs Example\n",
88
"\n",
9-
"Here is an example, where tqdm and print statements write to the cell output at the same time as the plot is updated"
9+
"Trainplot doesn't interfere with the output of other libraries or print statements."
1010
]
1111
},
1212
{
@@ -21,15 +21,14 @@
2121
"\n",
2222
"for i in trange(50):\n",
2323
" plot(i=i, root=i**.5)\n",
24-
" if i % 10 == 0:\n",
25-
" print(f'currently at {i} iterations')\n",
24+
" print(f'currently at {i} iterations', end='\\r')\n",
2625
" sleep(0.1)"
2726
]
2827
}
2928
],
3029
"metadata": {
3130
"kernelspec": {
32-
"display_name": "py311",
31+
"display_name": ".venv",
3332
"language": "python",
3433
"name": "python3"
3534
},
@@ -43,7 +42,7 @@
4342
"name": "python",
4443
"nbconvert_exporter": "python",
4544
"pygments_lexer": "ipython3",
46-
"version": "3.11.4"
45+
"version": "3.12.3"
4746
}
4847
},
4948
"nbformat": 4,

examples/plotly-example.ipynb

Lines changed: 0 additions & 96 deletions
This file was deleted.

examples/separate-output-example.ipynb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
"source": [
77
"# Separate Output Example\n",
88
"\n",
9-
"Here is a simple example, where the plotting output is in a separate cell from where the \"training loop\" is running."
9+
"Ttrainplot supports multiple trainplot instances at the same time. They don't even have to be rendered and updated in the same cell."
1010
]
1111
},
1212
{
1313
"cell_type": "code",
14-
"execution_count": null,
14+
"execution_count": 1,
1515
"metadata": {},
1616
"outputs": [],
1717
"source": [
@@ -31,7 +31,7 @@
3131
},
3232
{
3333
"cell_type": "code",
34-
"execution_count": null,
34+
"execution_count": 3,
3535
"metadata": {},
3636
"outputs": [],
3737
"source": [
@@ -44,7 +44,7 @@
4444
],
4545
"metadata": {
4646
"kernelspec": {
47-
"display_name": "py311",
47+
"display_name": ".venv",
4848
"language": "python",
4949
"name": "python3"
5050
},
@@ -58,7 +58,7 @@
5858
"name": "python",
5959
"nbconvert_exporter": "python",
6060
"pygments_lexer": "ipython3",
61-
"version": "3.11.4"
61+
"version": "3.12.3"
6262
}
6363
},
6464
"nbformat": 4,

0 commit comments

Comments
 (0)