progress-table 3.0.2__tar.gz → 3.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {progress_table-3.0.2 → progress_table-3.1.1}/.gitignore +4 -0
- {progress_table-3.0.2 → progress_table-3.1.1}/PKG-INFO +3 -2
- {progress_table-3.0.2 → progress_table-3.1.1}/README.md +2 -1
- {progress_table-3.0.2 → progress_table-3.1.1}/README_pypi.md +2 -1
- progress_table-3.1.1/docs/advanced-usage.md +290 -0
- progress_table-3.1.1/docs/integrations.md +114 -0
- progress_table-3.1.1/docs/v3-notice.md +52 -0
- progress_table-3.1.1/examples/brown2d.py +115 -0
- progress_table-3.1.1/examples/download_v1.py +92 -0
- progress_table-3.1.1/examples/download_v2.py +96 -0
- progress_table-3.1.1/examples/tictactoe.py +85 -0
- progress_table-3.1.1/examples/training.py +138 -0
- {progress_table-3.0.2 → progress_table-3.1.1}/progress_table/__init__.py +1 -1
- {progress_table-3.0.2 → progress_table-3.1.1}/progress_table/progress_table.py +41 -18
- {progress_table-3.0.2 → progress_table-3.1.1}/progress_table/styles.py +0 -2
- {progress_table-3.0.2 → progress_table-3.1.1}/pyproject.toml +19 -5
- {progress_table-3.0.2 → progress_table-3.1.1}/LICENSE.txt +0 -0
- {progress_table-3.0.2 → progress_table-3.1.1}/progress_table/common.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: progress-table
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.1
|
|
4
4
|
Summary: Display progress as a pretty table in the command line.
|
|
5
5
|
Project-URL: Home, https://github.com/gahaalt/progress-table
|
|
6
6
|
Project-URL: Documentation, https://github.com/sjmikler/progress-table/blob/main/docs
|
|
@@ -34,7 +34,8 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
# Progress Table
|
|
35
35
|
|
|
36
36
|
[](https://pypi.org/project/progress-table)
|
|
37
|
-
[](https://
|
|
37
|
+
[](https://github.com/sjmikler/progress-table/blob/main/LICENSE.txt)
|
|
38
|
+
[](https://codecov.io/gh/sjmikler/progress-table)
|
|
38
39
|
|
|
39
40
|
Lightweight utility to display the progress of your process as a pretty table in the command line.
|
|
40
41
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
# Progress Table
|
|
8
8
|
|
|
9
9
|
[](https://pypi.org/project/progress-table)
|
|
10
|
-
[](https://
|
|
10
|
+
[](https://github.com/sjmikler/progress-table/blob/main/LICENSE.txt)
|
|
11
|
+
[](https://codecov.io/gh/sjmikler/progress-table)
|
|
11
12
|
|
|
12
13
|
Lightweight utility to display the progress of your process as a pretty table in the command line.
|
|
13
14
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
# Progress Table
|
|
8
8
|
|
|
9
9
|
[](https://pypi.org/project/progress-table)
|
|
10
|
-
[](https://
|
|
10
|
+
[](https://github.com/sjmikler/progress-table/blob/main/LICENSE.txt)
|
|
11
|
+
[](https://codecov.io/gh/sjmikler/progress-table)
|
|
11
12
|
|
|
12
13
|
Lightweight utility to display the progress of your process as a pretty table in the command line.
|
|
13
14
|
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Advanced usage
|
|
2
|
+
|
|
3
|
+
## Indexing
|
|
4
|
+
|
|
5
|
+
Progress Table with `interactive>=2` supports modyfing already closed rows (rows above the current row).
|
|
6
|
+
This can be done either with `.update` method or using `.at` indexing which is a shorthand for `AtIndexer` object.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
> When using `interactive<2` mode, you can only modify the current row.
|
|
10
|
+
> Any changes made to other rows will not be displayed.
|
|
11
|
+
|
|
12
|
+
### `.update` method
|
|
13
|
+
|
|
14
|
+
When using `.update` method you update one value at once.
|
|
15
|
+
Use column name and row index to specify the cell location.
|
|
16
|
+
Example of using `.update` method:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from progress_table import ProgressTable
|
|
20
|
+
|
|
21
|
+
table = ProgressTable()
|
|
22
|
+
table.add_column("Value")
|
|
23
|
+
table.add_rows(3) # Adding empty rows
|
|
24
|
+
|
|
25
|
+
table.update(name="Value", value=1.0, row=1)
|
|
26
|
+
table.update(name="Value", value=2.0, row=0, cell_color="red bold")
|
|
27
|
+
table.update(name="Value", value=3.0, row=2) # modify last-but-one row
|
|
28
|
+
table.close()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Which might give you the following:
|
|
32
|
+
|
|
33
|
+
```output
|
|
34
|
+
╭──────────╮
|
|
35
|
+
│ Value │
|
|
36
|
+
├──────────┤
|
|
37
|
+
│ 2.0000 │
|
|
38
|
+
│ 1.0000 │
|
|
39
|
+
│ 3.0000 │
|
|
40
|
+
╰──────────╯
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### `.at` indexing
|
|
44
|
+
|
|
45
|
+
When using `.at` indexing you do not use column names, instead you use column indices.
|
|
46
|
+
First column has index 0, second 1, etc. Slicing operations are also supported with `.at` indexing.
|
|
47
|
+
Treat the table similarly to a numpy array:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from progress_table import ProgressTable
|
|
51
|
+
|
|
52
|
+
table = ProgressTable()
|
|
53
|
+
table.add_columns(4) # Add more columns with automatic names
|
|
54
|
+
table.add_rows(4) # Adding empty rows
|
|
55
|
+
|
|
56
|
+
table.at[:] = 0.0 # Initialize all values to 0.0
|
|
57
|
+
table.at[0, :] = 2.0 # Set all values in the first row to 2.0
|
|
58
|
+
table.at[:, 1] = 2.0 # Set all values in the second column to 2.0
|
|
59
|
+
table.at[2, 0] = 3.0 # Set the first column in the second-to-last row to 3.0
|
|
60
|
+
|
|
61
|
+
table.close()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Which should give you the following:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
╭──────────┬──────────┬──────────┬──────────╮
|
|
68
|
+
│ 0 │ 1 │ 2 │ 3 │
|
|
69
|
+
├──────────┼──────────┼──────────┼──────────┤
|
|
70
|
+
│ 2.0000 │ 2.0000 │ 2.0000 │ 2.0000 │
|
|
71
|
+
│ 0.0000 │ 2.0000 │ 0.0000 │ 0.0000 │
|
|
72
|
+
│ 3.0000 │ 2.0000 │ 0.0000 │ 0.0000 │
|
|
73
|
+
│ 0.0000 │ 2.0000 │ 0.0000 │ 0.0000 │
|
|
74
|
+
╰──────────┴──────────┴──────────┴──────────╯
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Progress Bars
|
|
78
|
+
|
|
79
|
+
There are two types of progress bars in Progress Table: embedded and non-embedded.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
> When using `interactive=0` mode, the progress bars will not be displayed.
|
|
83
|
+
|
|
84
|
+
> When using `interactive=1` mode, progress bars are be displayed only in the bottom row.
|
|
85
|
+
|
|
86
|
+
### Embedded progress bars
|
|
87
|
+
|
|
88
|
+
Embedded progress bars are displayed as an overlay under a table row that contains data.
|
|
89
|
+
This allows you to see the progress and the new data all at once.
|
|
90
|
+
Those progress bars are displayed 'under' the row and are aligned with the data columns.
|
|
91
|
+
It is possible to customize the looks of the embedded progress bar by specyfing an argument when creating a table:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from progress_table import ProgressTable
|
|
95
|
+
|
|
96
|
+
table = ProgressTable(pbar_style_embed="dash")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Below we show a sample of available styles for embedded progress bars:
|
|
100
|
+
|
|
101
|
+
`dash`
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
| Name | Value | Number |
|
|
105
|
+
|-----------------------------------|
|
|
106
|
+
| test1 | 1.0 | 42 |
|
|
107
|
+
|---test2--|----2.0-> | 37 |
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`rich`
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
| Name | Value | Number |
|
|
114
|
+
|-----------------------------------|
|
|
115
|
+
| test1 | 1.0 | 42 |
|
|
116
|
+
|━━━test2━━|━━━━2.0━━ | 37 |
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`under`
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
| Name | Value | Number |
|
|
123
|
+
|-----------------------------------|
|
|
124
|
+
| test1 | 1.0 | 42 |
|
|
125
|
+
|___test2__|____2.0__ | 37 |
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
To see the exahustive list of available options, one can use the following code:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from progress_table import styles
|
|
134
|
+
|
|
135
|
+
print(styles.available_pbar_styles())
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Non-embedded progress bars
|
|
139
|
+
|
|
140
|
+
Non-embedded progress bars are displayed as a separate row under the row that contains data.
|
|
141
|
+
Optionally, you can force every progress bar, including the embedded ones, to look like a non-embedded and cover the data.
|
|
142
|
+
You might want to do that if you aim for specific looks of your progress bars.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from progress_table import ProgressTable
|
|
146
|
+
|
|
147
|
+
table = ProgressTable(
|
|
148
|
+
pbar_style="square", # specify specific style
|
|
149
|
+
pbar_embedded=False, # force all progress bars to be non-embedded
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Below we show a sample of available styles for non-embedded progress bars
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
`square`
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
| Name | Value | Number |
|
|
161
|
+
|-----------------------------------|
|
|
162
|
+
| test1 | 1.0 | 42 |
|
|
163
|
+
| test2 | 2.0 | 37 |
|
|
164
|
+
|■■■■■■■■■■■■■■■■■■■■□□□□□□□□□□□□□□□|
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`circle`
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
| Name | Value | Number |
|
|
171
|
+
|-----------------------------------|
|
|
172
|
+
| test1 | 1.0 | 42 |
|
|
173
|
+
| test2 | 2.0 | 37 |
|
|
174
|
+
| ●●●●●●●●●●●●●●●●●●◉○○○○○○○○○○○○○○○|
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
As previously, you can see the exhaustive list of available options by using the following code:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from progress_table import styles
|
|
183
|
+
|
|
184
|
+
print(styles.available_pbar_styles())
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
> You can create custom styles by following code in `styles.py` file.
|
|
188
|
+
|
|
189
|
+
### Infobar
|
|
190
|
+
|
|
191
|
+
You can display detailed information like:
|
|
192
|
+
|
|
193
|
+
* throughput
|
|
194
|
+
* progress in percents
|
|
195
|
+
* progress as the current step and total steps
|
|
196
|
+
* ETA (estimated time arrival) - estimated time in seconds, minutes or hours when the progress bar will finish
|
|
197
|
+
* custom description
|
|
198
|
+
|
|
199
|
+
To add or remove some of this information, specify the arguments like below.
|
|
200
|
+
Additionaly, you can create a progress bar object and specify
|
|
201
|
+
all the options only for this specific progress bar.
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from progress_table import ProgressTable
|
|
205
|
+
|
|
206
|
+
table = ProgressTable(
|
|
207
|
+
pbar_show_throughput=True,
|
|
208
|
+
pbar_show_progress=False,
|
|
209
|
+
pbar_show_percents=False,
|
|
210
|
+
pbar_show_eta=False,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
pbar = table.pbar(
|
|
214
|
+
range(1000),
|
|
215
|
+
description="train epoch",
|
|
216
|
+
show_throughput=True,
|
|
217
|
+
show_progress=False,
|
|
218
|
+
show_percents=False,
|
|
219
|
+
show_eta=True,
|
|
220
|
+
style="circle",
|
|
221
|
+
style_embed="cdots",
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Which, when used, might look like this:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
│ │ │ │ │
|
|
229
|
+
│[train epoch, 24.1 it/s, ETA 53s] ●●●●●●●◉○○○○○│
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### More progress bar customization
|
|
233
|
+
|
|
234
|
+
There is more ways to customize the look and feel of your progress bars
|
|
235
|
+
by adding special keywords to the style string:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from progress_table import ProgressTable
|
|
239
|
+
|
|
240
|
+
table = ProgressTable(pbar_style="angled alt red blue")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The example above would result in an angled-type pbar with alternative way of displaying empty bars,
|
|
244
|
+
with filled bars colored red and empty bars colored blue. There's a separate style string
|
|
245
|
+
for the embedded type progress bars - styling of embedded and non-embedded progress bars is indepedent.
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from progress_table import ProgressTable
|
|
249
|
+
|
|
250
|
+
table = ProgressTable(pbar_style_embed="cdots red blue")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The available keywords are:
|
|
254
|
+
|
|
255
|
+
* `alt` - alternative way of displaying empty bars, for example `■` instead of `□`.
|
|
256
|
+
This is only useful when using colors, otherwise you might not be able to differentiate between filled and empty bars.
|
|
257
|
+
* `clean` - removes empty bars from the progress bar. Spaces are used instead.
|
|
258
|
+
* `red`, `green`, `blue`, `yellow` and other colors available in colorama. If more than one is provided,
|
|
259
|
+
the first one is used for filled bars and the last one is used for empty bars.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
Additionaly, you can specify the color of the progress bar using
|
|
264
|
+
`color` and `color_empty` arguments when creating a progress bar object.
|
|
265
|
+
This will override whatever color is set in `style` or `style_embed`.
|
|
266
|
+
We can combine this option with `colorama.Back` to modify colors
|
|
267
|
+
of the background instead of the foreground symbols.
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from progress_table import ProgressTable
|
|
271
|
+
import colorama
|
|
272
|
+
import time
|
|
273
|
+
|
|
274
|
+
table = ProgressTable("a", "b", "c")
|
|
275
|
+
table.add_rows(1)
|
|
276
|
+
|
|
277
|
+
pbar = table.pbar(
|
|
278
|
+
range(100),
|
|
279
|
+
style_embed="hidden",
|
|
280
|
+
color=colorama.Back.RED,
|
|
281
|
+
color_empty=colorama.Back.BLUE,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
for _ in pbar:
|
|
285
|
+
time.sleep(0.1)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Try the example above. It contains a different type of embedded progress bar.
|
|
289
|
+
Here the typical progress bar symbols will are hidden,
|
|
290
|
+
but the background color will show us the progress of the process.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Integrations
|
|
2
|
+
|
|
3
|
+
Progress Table is not tied up to any specific deep learning framework.
|
|
4
|
+
Because of it, it's lightweight and minimal. You can still easily integrate it with your code and your favourite framework. Below you will find examples of how to do this.
|
|
5
|
+
|
|
6
|
+
# PyTorch
|
|
7
|
+
|
|
8
|
+
This is an example of a simple custom training loop in PyTorch. We present two versions: without and with Progress Bar.
|
|
9
|
+
|
|
10
|
+
### Without Progress Table
|
|
11
|
+
|
|
12
|
+
Manual logging is often done in haste. It can look like this:
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
for epoch in range(n_epochs):
|
|
18
|
+
print(f'Epoch {epoch}')
|
|
19
|
+
cumulated_loss = 0
|
|
20
|
+
cumulated_accuracy = 0
|
|
21
|
+
|
|
22
|
+
for batch_idx, (x, y) in enumerate(train_loader):
|
|
23
|
+
loss, predictions = training_step(x, y)
|
|
24
|
+
accuracy = torch.mean((predictions == y).float()).item()
|
|
25
|
+
|
|
26
|
+
cumulated_loss += loss.item()
|
|
27
|
+
cumulated_accuracy += accuracy
|
|
28
|
+
|
|
29
|
+
if (batch_idx) % 100 == 0:
|
|
30
|
+
print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Accuracy: {:.4f}'.format(
|
|
31
|
+
epoch,
|
|
32
|
+
n_epochs,
|
|
33
|
+
batch_idx,
|
|
34
|
+
len(train_loader),
|
|
35
|
+
cumulated_loss / (batch_idx + 1),
|
|
36
|
+
cumulated_accuracy / (batch_idx + 1),
|
|
37
|
+
))
|
|
38
|
+
|
|
39
|
+
print('Epoch [{}/{}], Step [FINISHED], Loss: {:.4f}, Accuracy: {:.4f}'.format(
|
|
40
|
+
epoch,
|
|
41
|
+
n_epochs,
|
|
42
|
+
cumulated_loss / len(train_loader),
|
|
43
|
+
cumulated_accuracy / len(train_loader),
|
|
44
|
+
))
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With Progress Table
|
|
48
|
+
|
|
49
|
+
When using Progress Table, you get detailed and clean logs. Moreover, your code is shorter, simpler and you get more functionality out of it.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
table = ProgressTable("Epoch", "Step")
|
|
55
|
+
table.add_columns(["Loss", "Accuracy"], aggregate="mean")
|
|
56
|
+
|
|
57
|
+
for epoch in range(n_epochs):
|
|
58
|
+
table["Epoch"] = f"{epoch}/{n_epochs}"
|
|
59
|
+
|
|
60
|
+
for x, y in table(train_loader):
|
|
61
|
+
loss, predictions = training_step(x, y)
|
|
62
|
+
accuracy = torch.mean((predictions == y).float()).item()
|
|
63
|
+
|
|
64
|
+
table["Loss"] = loss
|
|
65
|
+
table["Accuracy"] = accuracy
|
|
66
|
+
table.next_row()
|
|
67
|
+
|
|
68
|
+
table.close()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
# Keras
|
|
72
|
+
|
|
73
|
+
In case of PyTorch or TensorFlow, we often use custom training loops
|
|
74
|
+
where we can integrate Progress Table as shown above.
|
|
75
|
+
What about Keras, where the progress bar is built-in the `model.fit` method?
|
|
76
|
+
You can create a callback that will replace the progress bar built-in Keras
|
|
77
|
+
with a progress table. Callback should inherit from `ProgbarLogger`.
|
|
78
|
+
|
|
79
|
+
Here's an example:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
table = ProgressTable()
|
|
85
|
+
table.add_columns(["loss", "accuracy", "val_loss", "val_accuracy"], aggregate="mean")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TableCallback(tf.keras.callbacks.ProgbarLogger):
|
|
89
|
+
def __init__(self, table):
|
|
90
|
+
super().__init__()
|
|
91
|
+
self.table = table
|
|
92
|
+
|
|
93
|
+
def on_epoch_begin(self, epoch, logs=None):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def on_epoch_end(self, epoch, logs=None):
|
|
97
|
+
self.table.update_from_dict(logs)
|
|
98
|
+
self.table.next_row()
|
|
99
|
+
|
|
100
|
+
def on_train_end(self, logs=None):
|
|
101
|
+
self.table.close()
|
|
102
|
+
|
|
103
|
+
def on_train_batch_end(self, batch, logs=None):
|
|
104
|
+
self.table.update_from_dict(logs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
model.fit(
|
|
108
|
+
x_train,
|
|
109
|
+
y_train,
|
|
110
|
+
validation_data=(x_train, y_train),
|
|
111
|
+
callbacks=[TableCallback(table)],
|
|
112
|
+
epochs=10,
|
|
113
|
+
)
|
|
114
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Version 3.0.0
|
|
2
|
+
|
|
3
|
+
Version 3.0.0 of Progress Table is now available.
|
|
4
|
+
|
|
5
|
+
It introduces:
|
|
6
|
+
|
|
7
|
+
* Important internal changes to increase the stability.
|
|
8
|
+
* Increased compatibility with some types of terminals.
|
|
9
|
+
* Minor breaking changes in the API.
|
|
10
|
+
|
|
11
|
+
## BREAKING API CHANGES
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
ProgressTable(["c1", "c2", "c3"])
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
↑ becomes now ↓
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
ProgressTable("c1", "c2", "c3")
|
|
23
|
+
# or
|
|
24
|
+
ProgressTable(columns=["c1", "c2", "c3"])
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
ProgressTable([], 1, 20)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
↑ will raise an error. Use named arguments instead ↓
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
ProgressTable(interactive=1, refresh_rate=20)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
from progress_table import ProgressTableV1, ProgressTableV2
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
↑ multiple versions of ProgressTable are not available anymore. Use ↓
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
from progress_table import ProgressTable
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Copyright (c) 2022-2025 Szymon Mikler
|
|
2
|
+
# Licensed under the MIT License
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
import random
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from progress_table import ProgressTable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def calc_distance(pos):
|
|
12
|
+
return (pos[0] ** 2 + pos[1] ** 2) ** 0.5
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_color_by_distance(distance):
|
|
16
|
+
if distance < 20:
|
|
17
|
+
return "white"
|
|
18
|
+
elif distance < 40:
|
|
19
|
+
return "cyan"
|
|
20
|
+
elif distance < 60:
|
|
21
|
+
return "blue"
|
|
22
|
+
elif distance < 80:
|
|
23
|
+
return "magenta"
|
|
24
|
+
elif distance < 100:
|
|
25
|
+
return "red"
|
|
26
|
+
else:
|
|
27
|
+
return "yellow bold"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def shift_rows_up(table, last_row_color):
|
|
31
|
+
num_rows = table.num_rows()
|
|
32
|
+
num_columns = table.num_columns()
|
|
33
|
+
|
|
34
|
+
for row in range(num_rows - 1):
|
|
35
|
+
for col in range(num_columns):
|
|
36
|
+
table.at[row, col] = table.at[row + 1, col]
|
|
37
|
+
table.at[row, col, "color"] = table.at[row + 1, col, "color"]
|
|
38
|
+
for col in range(num_columns):
|
|
39
|
+
table.at[-2, col, "color"] = last_row_color
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(random_seed=None, sleep_duration=0.001, **overrides):
|
|
43
|
+
if random_seed is None:
|
|
44
|
+
random_seed = random.randint(0, 100)
|
|
45
|
+
random.seed(random_seed)
|
|
46
|
+
|
|
47
|
+
table = ProgressTable(
|
|
48
|
+
pbar_embedded=False,
|
|
49
|
+
pbar_style="full clean blue",
|
|
50
|
+
**overrides,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
TARGET_DISTANCE = 100
|
|
54
|
+
|
|
55
|
+
PARTICLE_VELOCITY = 1
|
|
56
|
+
PARTICLE_MOMENTUM = 0.999
|
|
57
|
+
|
|
58
|
+
MAX_ROWS = 20
|
|
59
|
+
STEP_SIZE = 100
|
|
60
|
+
|
|
61
|
+
distance_pbar = table.pbar(
|
|
62
|
+
TARGET_DISTANCE,
|
|
63
|
+
description="Distance",
|
|
64
|
+
show_throughput=False,
|
|
65
|
+
show_progress=True,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
current_position = (0, 0)
|
|
69
|
+
current_velocity = PARTICLE_VELOCITY
|
|
70
|
+
|
|
71
|
+
print("Particle will move randomly in 2D space.")
|
|
72
|
+
print(f"Simulation stops when it reaches distance of {TARGET_DISTANCE} from the center.")
|
|
73
|
+
|
|
74
|
+
tick = 0
|
|
75
|
+
pbar_momentum = 0
|
|
76
|
+
while calc_distance(current_position) < TARGET_DISTANCE:
|
|
77
|
+
random_direction = random.uniform(0, 2 * 3.1415)
|
|
78
|
+
new_velocity = random.uniform(0, PARTICLE_VELOCITY * 2)
|
|
79
|
+
current_velocity = current_velocity * PARTICLE_MOMENTUM + new_velocity * (1 - PARTICLE_MOMENTUM)
|
|
80
|
+
move_vector = (
|
|
81
|
+
current_velocity * math.cos(random_direction),
|
|
82
|
+
current_velocity * math.sin(random_direction),
|
|
83
|
+
)
|
|
84
|
+
current_position = (
|
|
85
|
+
current_position[0] + move_vector[0],
|
|
86
|
+
current_position[1] + move_vector[1],
|
|
87
|
+
)
|
|
88
|
+
distance_from_center = calc_distance(current_position)
|
|
89
|
+
|
|
90
|
+
tick += 1
|
|
91
|
+
table["tick"] = tick
|
|
92
|
+
table["velocity"] = current_velocity
|
|
93
|
+
table["position X"] = current_position[0]
|
|
94
|
+
table["position Y"] = current_position[1]
|
|
95
|
+
table["distance from center"] = distance_from_center
|
|
96
|
+
|
|
97
|
+
pbar_momentum = pbar_momentum * 0.95 + int(distance_from_center) * 0.05
|
|
98
|
+
distance_pbar.set_step(round(pbar_momentum))
|
|
99
|
+
|
|
100
|
+
if tick % STEP_SIZE == 0:
|
|
101
|
+
color = get_color_by_distance(distance_from_center)
|
|
102
|
+
if table.num_rows() > MAX_ROWS:
|
|
103
|
+
shift_rows_up(table, last_row_color=color)
|
|
104
|
+
else:
|
|
105
|
+
table.next_row(color=color)
|
|
106
|
+
|
|
107
|
+
time.sleep(sleep_duration)
|
|
108
|
+
|
|
109
|
+
color = get_color_by_distance(100)
|
|
110
|
+
table.next_row(color=color)
|
|
111
|
+
table.close()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
main()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (c) 2022-2025 Szymon Mikler
|
|
2
|
+
# Licensed under the MIT License
|
|
3
|
+
import random
|
|
4
|
+
import time
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
6
|
+
|
|
7
|
+
import colorama
|
|
8
|
+
|
|
9
|
+
from progress_table import ProgressTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_random_file_info():
|
|
13
|
+
info = {}
|
|
14
|
+
index = random.randint(0, 1000)
|
|
15
|
+
types = ["image_.jpg", "video_.mp4", "archive_.zip", "movie_.avi"]
|
|
16
|
+
info["name"] = random.choice(types).replace("_", str(index))
|
|
17
|
+
info["size int"] = random.randint(1, 1000)
|
|
18
|
+
info["size unit"] = random.choice(["KB", "MB", "GB"])
|
|
19
|
+
info["time"] = 2 + random.random() * 10
|
|
20
|
+
return info
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
NUM_FILES = 15
|
|
24
|
+
files_to_download = [create_random_file_info() for _ in range(NUM_FILES)]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
table = ProgressTable(
|
|
28
|
+
pbar_show_progress=False,
|
|
29
|
+
pbar_show_throughput=False,
|
|
30
|
+
pbar_show_eta=True,
|
|
31
|
+
default_column_width=8,
|
|
32
|
+
default_header_color="bold",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
main_pbar = table.pbar(
|
|
36
|
+
NUM_FILES,
|
|
37
|
+
position=1,
|
|
38
|
+
show_progress=True,
|
|
39
|
+
style="square clean blue",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def fake_download(idx, file_info):
|
|
44
|
+
# Modify pbar styling so that emebedded pbar shows color only
|
|
45
|
+
pbar = table.pbar(1, position=idx, static=True, style_embed="hidden", color=colorama.Back.BLACK)
|
|
46
|
+
|
|
47
|
+
# Update table values in a specifc row
|
|
48
|
+
table.update("total size", str(file_info["size int"]) + " " + file_info["size unit"], row=idx)
|
|
49
|
+
table.update("name", file_info["name"], row=idx)
|
|
50
|
+
table.update("seeds", random.randint(1, 30), row=idx)
|
|
51
|
+
table.update("peers", random.randint(0, 5), row=idx)
|
|
52
|
+
|
|
53
|
+
number_of_errors = 0
|
|
54
|
+
|
|
55
|
+
t0 = time.time()
|
|
56
|
+
td = 0
|
|
57
|
+
while True:
|
|
58
|
+
pbar.set_step(td / file_info["time"]) # Set specific pbar progress
|
|
59
|
+
|
|
60
|
+
# Maybe add an error to the error counter
|
|
61
|
+
if random.random() < 0.004:
|
|
62
|
+
number_of_errors += 1
|
|
63
|
+
random_delay = random.random()
|
|
64
|
+
time.sleep(random_delay)
|
|
65
|
+
t0 += random_delay
|
|
66
|
+
|
|
67
|
+
downloaded_units = int(td / file_info["time"] * file_info["size int"])
|
|
68
|
+
table.update("downloaded", str(downloaded_units) + " " + file_info["size unit"], row=idx)
|
|
69
|
+
table.update("warnings", number_of_errors, row=idx)
|
|
70
|
+
time.sleep(0.01)
|
|
71
|
+
|
|
72
|
+
td = time.time() - t0
|
|
73
|
+
if td > file_info["time"]:
|
|
74
|
+
break
|
|
75
|
+
pbar.close()
|
|
76
|
+
main_pbar.update()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
table.add_column("name", alignment="right", width=25)
|
|
80
|
+
table.add_columns("total size", "downloaded", "seeds", "peers", "warnings")
|
|
81
|
+
table.add_rows(len(files_to_download), color="blue")
|
|
82
|
+
|
|
83
|
+
threads = []
|
|
84
|
+
executor = ThreadPoolExecutor()
|
|
85
|
+
|
|
86
|
+
for idx, pkg in enumerate(files_to_download):
|
|
87
|
+
threads.append(executor.submit(fake_download, idx, pkg))
|
|
88
|
+
|
|
89
|
+
for thread in threads:
|
|
90
|
+
thread.result()
|
|
91
|
+
|
|
92
|
+
table.close()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Copyright (c) 2022-2025 Szymon Mikler
|
|
2
|
+
# Licensed under the MIT License
|
|
3
|
+
import random
|
|
4
|
+
import time
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
6
|
+
|
|
7
|
+
import colorama
|
|
8
|
+
|
|
9
|
+
from progress_table import ProgressTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_random_file_info():
|
|
13
|
+
info = {}
|
|
14
|
+
index = random.randint(0, 1000)
|
|
15
|
+
types = ["image_.jpg", "video_.mp4", "archive_.zip", "movie_.avi"]
|
|
16
|
+
info["name"] = random.choice(types).replace("_", str(index))
|
|
17
|
+
info["size int"] = random.randint(1, 1000)
|
|
18
|
+
info["size unit"] = random.choice(["KB", "MB", "GB"])
|
|
19
|
+
info["time"] = 2 + random.random() * 10
|
|
20
|
+
return info
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
NUM_FILES = 40
|
|
24
|
+
files_to_download = [create_random_file_info() for _ in range(NUM_FILES)]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
table = ProgressTable(
|
|
28
|
+
pbar_show_progress=False,
|
|
29
|
+
pbar_show_throughput=False,
|
|
30
|
+
pbar_show_eta=True,
|
|
31
|
+
default_column_width=8,
|
|
32
|
+
default_header_color="bold",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
main_pbar = table.pbar(
|
|
36
|
+
NUM_FILES,
|
|
37
|
+
position=1,
|
|
38
|
+
show_progress=True,
|
|
39
|
+
style="square clean blue",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def fake_download(file_info):
|
|
44
|
+
# Modify pbar styling so that emebedded pbar shows color only
|
|
45
|
+
idx = table.num_rows() - 1
|
|
46
|
+
table.next_row()
|
|
47
|
+
|
|
48
|
+
pbar = table.pbar(1, position=idx, static=True, style_embed="hidden", color=colorama.Back.BLACK)
|
|
49
|
+
|
|
50
|
+
# Update table values in a specifc row
|
|
51
|
+
table.update("total size", str(file_info["size int"]) + " " + file_info["size unit"], row=idx)
|
|
52
|
+
table.update("name", file_info["name"], row=idx)
|
|
53
|
+
table.update("seeds", random.randint(1, 30), row=idx)
|
|
54
|
+
table.update("peers", random.randint(0, 5), row=idx)
|
|
55
|
+
|
|
56
|
+
number_of_errors = 0
|
|
57
|
+
|
|
58
|
+
t0 = time.time()
|
|
59
|
+
td = 0
|
|
60
|
+
while True:
|
|
61
|
+
pbar.set_step(td / file_info["time"]) # Set specific pbar progress
|
|
62
|
+
|
|
63
|
+
# Maybe add an error to the error counter
|
|
64
|
+
if random.random() < 0.004:
|
|
65
|
+
number_of_errors += 1
|
|
66
|
+
random_delay = random.random()
|
|
67
|
+
time.sleep(random_delay)
|
|
68
|
+
t0 += random_delay
|
|
69
|
+
|
|
70
|
+
downloaded_units = int(td / file_info["time"] * file_info["size int"])
|
|
71
|
+
table.update("downloaded", str(downloaded_units) + " " + file_info["size unit"], row=idx)
|
|
72
|
+
table.update("warnings", number_of_errors, row=idx)
|
|
73
|
+
time.sleep(0.01)
|
|
74
|
+
|
|
75
|
+
td = time.time() - t0
|
|
76
|
+
if td > file_info["time"]:
|
|
77
|
+
break
|
|
78
|
+
pbar.close()
|
|
79
|
+
main_pbar.update()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
table.add_column("name", alignment="right", width=25)
|
|
83
|
+
table.add_columns("total size", "downloaded", "seeds", "peers", "warnings")
|
|
84
|
+
|
|
85
|
+
threads = []
|
|
86
|
+
executor = ThreadPoolExecutor()
|
|
87
|
+
|
|
88
|
+
for _idx, pkg in enumerate(files_to_download):
|
|
89
|
+
threads.append(executor.submit(fake_download, pkg))
|
|
90
|
+
random_wait_time = random.randint(1, 10)
|
|
91
|
+
time.sleep(random_wait_time / 10)
|
|
92
|
+
|
|
93
|
+
for thread in threads:
|
|
94
|
+
thread.result()
|
|
95
|
+
|
|
96
|
+
table.close()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright (c) 2022-2025 Szymon Mikler
|
|
2
|
+
# Licensed under the MIT License
|
|
3
|
+
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from progress_table import ProgressTable
|
|
8
|
+
|
|
9
|
+
BOARD_SIZE = 10
|
|
10
|
+
STREAK_LENGTH = 5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(random_seed=None, sleep_duration=0.05, **overrides):
|
|
14
|
+
if random_seed is None:
|
|
15
|
+
random_seed = random.randint(0, 100)
|
|
16
|
+
random.seed(random_seed)
|
|
17
|
+
|
|
18
|
+
table = ProgressTable(
|
|
19
|
+
pbar_embedded=False,
|
|
20
|
+
default_column_width=1,
|
|
21
|
+
print_header_on_top=False,
|
|
22
|
+
pbar_style="circle alt red",
|
|
23
|
+
**overrides,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
print("Two players are playing tic-tac-toe.")
|
|
27
|
+
print(f"The first to get a streak of {STREAK_LENGTH} wins.")
|
|
28
|
+
|
|
29
|
+
# Use convenience method to add multiple columns at once
|
|
30
|
+
table.add_columns(BOARD_SIZE)
|
|
31
|
+
|
|
32
|
+
# Adding multiple new rows at once is also possible
|
|
33
|
+
# We get the 1-st row automatically, so we add one less
|
|
34
|
+
table.add_rows(BOARD_SIZE, split=True)
|
|
35
|
+
|
|
36
|
+
sign = 0
|
|
37
|
+
x = 0
|
|
38
|
+
y = 0
|
|
39
|
+
|
|
40
|
+
win_row_slice = None
|
|
41
|
+
win_col_slice = None
|
|
42
|
+
for _i in table(BOARD_SIZE * BOARD_SIZE, show_throughput=False, show_progress=True):
|
|
43
|
+
current_symbol = True
|
|
44
|
+
while current_symbol:
|
|
45
|
+
x = random.randint(0, BOARD_SIZE - 1)
|
|
46
|
+
y = random.randint(0, BOARD_SIZE - 1)
|
|
47
|
+
current_symbol = table.at[y, x]
|
|
48
|
+
sign = 1 - sign
|
|
49
|
+
|
|
50
|
+
table.at[y, x] = "X" if sign else "O"
|
|
51
|
+
table.at[y, x, "COLOR"] = "CYAN" if sign else "BLUE"
|
|
52
|
+
|
|
53
|
+
finished = False
|
|
54
|
+
for row_idx in range(BOARD_SIZE):
|
|
55
|
+
row = table.at[row_idx, :]
|
|
56
|
+
for j in range(BOARD_SIZE - STREAK_LENGTH):
|
|
57
|
+
values = set(row[j : j + STREAK_LENGTH])
|
|
58
|
+
if values == {"X"} or values == {"O"}:
|
|
59
|
+
win_row_slice = slice(row_idx, row_idx + 1)
|
|
60
|
+
win_col_slice = slice(j, j + STREAK_LENGTH)
|
|
61
|
+
finished = True
|
|
62
|
+
for col_idx in range(BOARD_SIZE):
|
|
63
|
+
col = table.at[:, col_idx]
|
|
64
|
+
|
|
65
|
+
for j in range(BOARD_SIZE - STREAK_LENGTH):
|
|
66
|
+
values = set(col[j : j + STREAK_LENGTH])
|
|
67
|
+
if values == {"X"} or values == {"O"}:
|
|
68
|
+
win_row_slice = slice(j, j + STREAK_LENGTH)
|
|
69
|
+
win_col_slice = slice(col_idx, col_idx + 1)
|
|
70
|
+
finished = True
|
|
71
|
+
|
|
72
|
+
if finished:
|
|
73
|
+
break
|
|
74
|
+
time.sleep(sleep_duration)
|
|
75
|
+
|
|
76
|
+
# Flashing the winner
|
|
77
|
+
for color in ["bold white", "bold red"] * 8:
|
|
78
|
+
table.at[win_row_slice, win_col_slice, "COLOR"] = color
|
|
79
|
+
time.sleep(0.1)
|
|
80
|
+
|
|
81
|
+
table.close()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright (c) 2022-2025 Szymon Mikler
|
|
2
|
+
# Licensed under the MIT License
|
|
3
|
+
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import numpy as np
|
|
9
|
+
except ImportError as ex:
|
|
10
|
+
raise ImportError("Numpy is required to run the example!") from ex
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from sklearn.datasets import load_iris
|
|
14
|
+
from sklearn.model_selection import train_test_split
|
|
15
|
+
from sklearn.utils import shuffle
|
|
16
|
+
except ImportError as ex:
|
|
17
|
+
raise ImportError("Scikit-learn is required to run the example!") from ex
|
|
18
|
+
|
|
19
|
+
from progress_table import ProgressTable
|
|
20
|
+
|
|
21
|
+
# Training parameters
|
|
22
|
+
SGD_LR = 0.01
|
|
23
|
+
NUM_EPOCHS = 15
|
|
24
|
+
SLEEP_DURATION = 0.04
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def softmax(x):
|
|
28
|
+
exp = np.exp(x)
|
|
29
|
+
return exp / np.sum(exp, axis=1, keepdims=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def log_softmax(x):
|
|
33
|
+
bot = np.sum(np.exp(x), axis=1, keepdims=True)
|
|
34
|
+
return x - np.log(bot)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def cross_entropy_loss(targets, logits):
|
|
38
|
+
# Simulate heavy computation
|
|
39
|
+
time.sleep(SLEEP_DURATION)
|
|
40
|
+
|
|
41
|
+
assert len(targets) == len(logits)
|
|
42
|
+
num_elements = len(targets)
|
|
43
|
+
|
|
44
|
+
logits = log_softmax(logits)
|
|
45
|
+
return -logits[np.arange(num_elements), targets]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cross_entropy_loss_grads(targets, logits):
|
|
49
|
+
one_hot_targets = np.zeros_like(logits)
|
|
50
|
+
one_hot_targets[np.arange(len(targets)), targets] = 1
|
|
51
|
+
return one_hot_targets - softmax(logits)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def model_grads(targets, logits, inputs):
|
|
55
|
+
cross_entropy_grads = cross_entropy_loss_grads(targets, logits)
|
|
56
|
+
return inputs.T @ cross_entropy_grads
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main(random_seed=None, sleep_duration=SLEEP_DURATION, **overrides):
|
|
60
|
+
if random_seed is None:
|
|
61
|
+
random_seed = random.randint(0, 100)
|
|
62
|
+
global SLEEP_DURATION
|
|
63
|
+
SLEEP_DURATION = sleep_duration
|
|
64
|
+
|
|
65
|
+
random.seed(random_seed)
|
|
66
|
+
np.random.seed(random_seed)
|
|
67
|
+
|
|
68
|
+
table = ProgressTable(
|
|
69
|
+
pbar_embedded=False, # Do not use embedded pbar
|
|
70
|
+
pbar_style="angled alt red blue",
|
|
71
|
+
**overrides,
|
|
72
|
+
)
|
|
73
|
+
print("Training a simple linear model on the Iris dataset.")
|
|
74
|
+
|
|
75
|
+
# Loading dataset
|
|
76
|
+
X, Y = load_iris(return_X_y=True)
|
|
77
|
+
X_train, X_valid, Y_train, Y_valid = train_test_split(X, Y)
|
|
78
|
+
weights = np.random.rand(4, 3)
|
|
79
|
+
|
|
80
|
+
for epoch in table(NUM_EPOCHS, show_throughput=False, show_eta=True):
|
|
81
|
+
table["epoch"] = epoch
|
|
82
|
+
# Shuffling training dataset each epoch
|
|
83
|
+
X_train, Y_train = shuffle(X_train, Y_train) # type: ignore
|
|
84
|
+
|
|
85
|
+
NUM_BATCHES = 16
|
|
86
|
+
X_batches = np.array_split(X_train, NUM_BATCHES)
|
|
87
|
+
Y_batches = np.array_split(Y_train, NUM_BATCHES)
|
|
88
|
+
|
|
89
|
+
for batch in table(zip(X_batches, Y_batches), total=NUM_BATCHES, description="train epoch"):
|
|
90
|
+
x, y = batch
|
|
91
|
+
logits = x @ weights
|
|
92
|
+
|
|
93
|
+
# Computing and applying gradient update
|
|
94
|
+
weights_updates = model_grads(y, logits, x)
|
|
95
|
+
weights = weights + SGD_LR * weights_updates
|
|
96
|
+
|
|
97
|
+
# Computing loss function for the logging
|
|
98
|
+
accuracy = np.mean(np.argmax(logits, axis=1) == y)
|
|
99
|
+
loss_value = np.mean(cross_entropy_loss(y, logits))
|
|
100
|
+
|
|
101
|
+
# We're using .update instead of __setitem__ so that we can specify column details
|
|
102
|
+
table.update("train loss", loss_value, aggregate="mean", color="blue")
|
|
103
|
+
table.update("train accuracy", accuracy, aggregate="mean", color="blue bold")
|
|
104
|
+
|
|
105
|
+
run_validation = epoch % 5 == 4 or epoch == NUM_EPOCHS - 1
|
|
106
|
+
if run_validation:
|
|
107
|
+
NUM_BATCHES = 32
|
|
108
|
+
X_batches = np.array_split(X_valid, NUM_BATCHES)
|
|
109
|
+
Y_batches = np.array_split(Y_valid, NUM_BATCHES)
|
|
110
|
+
|
|
111
|
+
for batch in table(zip(X_batches, Y_batches), total=NUM_BATCHES, description="valid epoch"):
|
|
112
|
+
x, y = batch
|
|
113
|
+
logits = x @ weights
|
|
114
|
+
accuracy = np.mean(np.argmax(logits, axis=1) == y)
|
|
115
|
+
loss_value = np.mean(cross_entropy_loss(y, logits))
|
|
116
|
+
|
|
117
|
+
# Use aggregation weight equal to batch size to get real mean over the validation dataset
|
|
118
|
+
batch_size = x.shape[0]
|
|
119
|
+
table.update(
|
|
120
|
+
"valid loss",
|
|
121
|
+
loss_value,
|
|
122
|
+
weight=batch_size,
|
|
123
|
+
aggregate="mean",
|
|
124
|
+
color="red",
|
|
125
|
+
)
|
|
126
|
+
table.update(
|
|
127
|
+
"valid accuracy",
|
|
128
|
+
accuracy,
|
|
129
|
+
weight=batch_size,
|
|
130
|
+
aggregate="mean",
|
|
131
|
+
color="red bold",
|
|
132
|
+
)
|
|
133
|
+
table.next_row(split=run_validation)
|
|
134
|
+
table.close()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
main()
|
|
@@ -15,12 +15,17 @@ import time
|
|
|
15
15
|
from collections.abc import Callable, Iterable, Iterator, Sized
|
|
16
16
|
from dataclasses import dataclass
|
|
17
17
|
from threading import Thread
|
|
18
|
-
from typing import TextIO
|
|
18
|
+
from typing import Any, TextIO
|
|
19
19
|
|
|
20
20
|
from colorama import Style
|
|
21
21
|
|
|
22
22
|
from progress_table import styles
|
|
23
|
-
from progress_table.common import
|
|
23
|
+
from progress_table.common import (
|
|
24
|
+
CURSOR_UP,
|
|
25
|
+
ColorFormat,
|
|
26
|
+
ColorFormatTuple,
|
|
27
|
+
maybe_convert_to_colorama,
|
|
28
|
+
)
|
|
24
29
|
|
|
25
30
|
######################
|
|
26
31
|
## HELPER FUNCTIONS ##
|
|
@@ -83,8 +88,8 @@ def get_aggregate_fn(aggregate: None | str | Callable) -> Callable:
|
|
|
83
88
|
raise ValueError(msg)
|
|
84
89
|
|
|
85
90
|
|
|
86
|
-
def get_default_format_fn(decimal_places: int) -> Callable[[
|
|
87
|
-
def fmt(x:
|
|
91
|
+
def get_default_format_fn(decimal_places: int) -> Callable[[Any], str]:
|
|
92
|
+
def fmt(x: Any) -> str:
|
|
88
93
|
if isinstance(x, int):
|
|
89
94
|
return str(x)
|
|
90
95
|
try:
|
|
@@ -104,13 +109,14 @@ def get_default_format_fn(decimal_places: int) -> Callable[[object], str]:
|
|
|
104
109
|
class DataRow:
|
|
105
110
|
"""Basic unit of data storage for the table."""
|
|
106
111
|
|
|
107
|
-
values: dict[str,
|
|
112
|
+
values: dict[str, Any]
|
|
108
113
|
weights: dict[str, float]
|
|
109
114
|
colors: dict[str, str]
|
|
115
|
+
user: bool = False
|
|
110
116
|
|
|
111
117
|
def is_empty(self) -> bool:
|
|
112
118
|
"""Check if the row is empty."""
|
|
113
|
-
return not any(self.values)
|
|
119
|
+
return not any(self.values) and not self.user
|
|
114
120
|
|
|
115
121
|
|
|
116
122
|
class ProgressTable:
|
|
@@ -144,7 +150,7 @@ class ProgressTable:
|
|
|
144
150
|
pbar_style_embed: str | styles.PbarStyleBase = "cdots",
|
|
145
151
|
print_header_on_top: bool = True,
|
|
146
152
|
print_header_every_n_rows: int = 30,
|
|
147
|
-
custom_cell_format: Callable[[
|
|
153
|
+
custom_cell_format: Callable[[Any], str] | None = None,
|
|
148
154
|
table_style: str | styles.TableStyleBase = "round",
|
|
149
155
|
file: TextIO | list[TextIO] | tuple[TextIO] | None = None,
|
|
150
156
|
# DEPRECATED ARGUMENTS
|
|
@@ -387,7 +393,7 @@ class ProgressTable:
|
|
|
387
393
|
def update(
|
|
388
394
|
self,
|
|
389
395
|
name: str,
|
|
390
|
-
value:
|
|
396
|
+
value: Any,
|
|
391
397
|
*,
|
|
392
398
|
row: int = -1,
|
|
393
399
|
weight: float = 1.0,
|
|
@@ -429,8 +435,12 @@ class ProgressTable:
|
|
|
429
435
|
if self.interactive > 0:
|
|
430
436
|
self._append_or_update_display_row(data_row_index)
|
|
431
437
|
|
|
432
|
-
def __setitem__(self, key: str | tuple[str, int], value:
|
|
438
|
+
def __setitem__(self, key: str | tuple[str, int], value: Any) -> None:
|
|
433
439
|
"""Update value in the current row. Calls 'update'."""
|
|
440
|
+
if isinstance(key, slice):
|
|
441
|
+
msg = "slicing not supported! Did you want to use 'table.at[:]' indexer?"
|
|
442
|
+
raise IndexError(msg)
|
|
443
|
+
|
|
434
444
|
if isinstance(key, tuple):
|
|
435
445
|
name, row = key
|
|
436
446
|
if isinstance(name, int) and isinstance(row, str):
|
|
@@ -441,8 +451,12 @@ class ProgressTable:
|
|
|
441
451
|
assert isinstance(row, int), f"Row {row} has to be an integer, not {type(row)}!"
|
|
442
452
|
self.update(name, value, row=row, weight=1)
|
|
443
453
|
|
|
444
|
-
def __getitem__(self, key: str | tuple[str, int]) ->
|
|
454
|
+
def __getitem__(self, key: str | tuple[str, int]) -> Any:
|
|
445
455
|
"""Get the value from the current row in table."""
|
|
456
|
+
if isinstance(key, slice):
|
|
457
|
+
msg = "slicing not supported! Did you want to use 'table.at[:]' indexer?"
|
|
458
|
+
raise IndexError(msg)
|
|
459
|
+
|
|
446
460
|
if isinstance(key, tuple):
|
|
447
461
|
name, row = key
|
|
448
462
|
if isinstance(name, int) and isinstance(row, int):
|
|
@@ -454,7 +468,7 @@ class ProgressTable:
|
|
|
454
468
|
assert isinstance(row, int), f"Row {row} has to be an integer, not {type(row)}!"
|
|
455
469
|
return self._data_rows[row].values.get(name, None)
|
|
456
470
|
|
|
457
|
-
def update_from_dict(self, dictionary: dict[str,
|
|
471
|
+
def update_from_dict(self, dictionary: dict[str, Any]) -> None:
|
|
458
472
|
"""Update multiple values in the current row."""
|
|
459
473
|
for key, value in dictionary.items():
|
|
460
474
|
self.update(key, value)
|
|
@@ -500,7 +514,7 @@ class ProgressTable:
|
|
|
500
514
|
row.colors = {**self._resolve_row_color_dict(color), **row.colors}
|
|
501
515
|
|
|
502
516
|
# Refreshing the existing row is necessary to apply colors
|
|
503
|
-
# Or - if row is
|
|
517
|
+
# Or - if row is new - this will add it to display rows
|
|
504
518
|
self._append_or_update_display_row(data_row_index)
|
|
505
519
|
|
|
506
520
|
self._append_new_empty_data_row()
|
|
@@ -513,9 +527,18 @@ class ProgressTable:
|
|
|
513
527
|
|
|
514
528
|
def add_row(self, *values, **kwds) -> None:
|
|
515
529
|
"""Mimicking rich.table behavior for adding full rows in one call."""
|
|
530
|
+
if not self._data_rows[-1].is_empty():
|
|
531
|
+
self.next_row(**kwds)
|
|
532
|
+
|
|
516
533
|
for key, value in zip(self.column_names, values):
|
|
517
534
|
self.update(key, value)
|
|
518
|
-
|
|
535
|
+
|
|
536
|
+
# The row was explicitly added, make sure it is displayed
|
|
537
|
+
data_row_index = len(self._data_rows) - 1
|
|
538
|
+
self._append_or_update_display_row(data_row_index)
|
|
539
|
+
|
|
540
|
+
# Mark row as explicitly added by the user
|
|
541
|
+
self._data_rows[data_row_index].user = True
|
|
519
542
|
|
|
520
543
|
def add_rows(self, *rows, **kwds) -> None:
|
|
521
544
|
"""Like `add_row` but adds multiple rows at once.
|
|
@@ -574,14 +597,14 @@ class ProgressTable:
|
|
|
574
597
|
|
|
575
598
|
self._append_or_update_display_row("USER WRITE " + line)
|
|
576
599
|
|
|
577
|
-
def to_list(self) -> list[list[
|
|
600
|
+
def to_list(self) -> list[list[Any]]:
|
|
578
601
|
"""Convert to Python nested list."""
|
|
579
602
|
values = [[row.values.get(col, None) for col in self.column_names] for row in self._data_rows]
|
|
580
603
|
if self._data_rows[-1].is_empty():
|
|
581
604
|
values.pop(-1)
|
|
582
605
|
return values
|
|
583
606
|
|
|
584
|
-
def to_numpy(self) ->
|
|
607
|
+
def to_numpy(self) -> Any:
|
|
585
608
|
"""Convert to numpy array.
|
|
586
609
|
|
|
587
610
|
Numpy library is required.
|
|
@@ -590,7 +613,7 @@ class ProgressTable:
|
|
|
590
613
|
|
|
591
614
|
return np.array(self.to_list())
|
|
592
615
|
|
|
593
|
-
def to_df(self) ->
|
|
616
|
+
def to_df(self) -> Any:
|
|
594
617
|
"""Convert to pandas DataFrame.
|
|
595
618
|
|
|
596
619
|
Pandas library is required.
|
|
@@ -809,7 +832,7 @@ class ProgressTable:
|
|
|
809
832
|
color_colorama = {column: maybe_convert_to_colorama(color) for column, color in color.items()}
|
|
810
833
|
return {col: self.column_colors[col] + color_colorama[col] for col in color}
|
|
811
834
|
|
|
812
|
-
def _apply_cell_formatting(self, value:
|
|
835
|
+
def _apply_cell_formatting(self, value: Any, column_name: str, color: str) -> str:
|
|
813
836
|
str_value = self.custom_cell_format(value)
|
|
814
837
|
width = self.column_widths[column_name]
|
|
815
838
|
alignment = self.column_alignments[column_name]
|
|
@@ -1284,7 +1307,7 @@ class TableAtIndexer:
|
|
|
1284
1307
|
column_names = self.table.column_names[cols] if isinstance(cols, slice) else [self.table.column_names[cols]]
|
|
1285
1308
|
return row_indices, column_names, mode
|
|
1286
1309
|
|
|
1287
|
-
def __setitem__(self, key: slice | tuple, value:
|
|
1310
|
+
def __setitem__(self, key: slice | tuple, value: Any) -> None:
|
|
1288
1311
|
"""Set the values, colors, or weights of a slice in the table."""
|
|
1289
1312
|
row_indices, column_names, edit_mode = self._parse_index(key)
|
|
1290
1313
|
if edit_mode == "colors":
|
|
@@ -32,8 +32,6 @@ def _parse_colors_from_description(description: str) -> tuple[str, str, str]:
|
|
|
32
32
|
class UnknownStyleError(ValueError):
|
|
33
33
|
"""Raised when style description is not recognized."""
|
|
34
34
|
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
35
|
|
|
38
36
|
def parse_pbar_style(description: str | PbarStyleBase) -> PbarStyleBase:
|
|
39
37
|
"""Parse progress bar style description and return a style object.
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.12",
|
|
24
24
|
"Programming Language :: Python :: 3.13",
|
|
25
25
|
]
|
|
26
|
-
readme = "
|
|
26
|
+
readme = "README_pypi.md"
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
29
29
|
Home = "https://github.com/gahaalt/progress-table"
|
|
@@ -36,7 +36,7 @@ dev = ["black", "isort", "build", "twine"]
|
|
|
36
36
|
path = "progress_table/__init__.py"
|
|
37
37
|
|
|
38
38
|
[tool.hatch.build.targets.sdist]
|
|
39
|
-
include = ["progress_table", "README.md"]
|
|
39
|
+
include = ["progress_table", "examples", "docs", "README.md"]
|
|
40
40
|
|
|
41
41
|
[tool.hatch.build.targets.wheel]
|
|
42
42
|
packages = ["progress_table"]
|
|
@@ -44,8 +44,7 @@ packages = ["progress_table"]
|
|
|
44
44
|
[tool.hatch.metadata.hooks.custom]
|
|
45
45
|
path = "hooks.py"
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
# %%
|
|
47
|
+
# %% TOOLS
|
|
49
48
|
|
|
50
49
|
[tool.pyright]
|
|
51
50
|
typeCheckingMode = "standard"
|
|
@@ -53,8 +52,23 @@ exclude = ["devel", "build", "dist"]
|
|
|
53
52
|
|
|
54
53
|
[tool.ruff]
|
|
55
54
|
line-length = 120
|
|
56
|
-
fix = true
|
|
57
55
|
target-version = "py37"
|
|
58
56
|
|
|
59
57
|
[tool.ruff.lint]
|
|
60
58
|
select = ["E", "F", "I", "B"]
|
|
59
|
+
|
|
60
|
+
# %%
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
pythonpath = ["."]
|
|
64
|
+
|
|
65
|
+
[tool.mypy]
|
|
66
|
+
ignore_missing_imports = true
|
|
67
|
+
exclude = ["devel", "build", "dist"]
|
|
68
|
+
|
|
69
|
+
[tool.isort]
|
|
70
|
+
profile = "black"
|
|
71
|
+
line_length = 120
|
|
72
|
+
|
|
73
|
+
[tool.black]
|
|
74
|
+
line_length = 120
|
|
File without changes
|
|
File without changes
|