arrscope 0.2.0__tar.gz → 0.4.0__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.
- arrscope-0.4.0/PKG-INFO +168 -0
- arrscope-0.4.0/README.md +141 -0
- arrscope-0.4.0/examples/renders.py +71 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/main.py +7 -2
- {arrscope-0.2.0 → arrscope-0.4.0}/pyproject.toml +1 -1
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_api.py +53 -87
- arrscope-0.4.0/src/arrscope/_config.py +10 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_format.py +12 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_layout.py +29 -3
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_types.py +21 -7
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/html.py +85 -48
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/terminal.py +93 -56
- {arrscope-0.2.0 → arrscope-0.4.0}/uv.lock +1 -1
- arrscope-0.2.0/PKG-INFO +0 -185
- arrscope-0.2.0/README.md +0 -158
- arrscope-0.2.0/src/arrscope/_config.py +0 -15
- {arrscope-0.2.0 → arrscope-0.4.0}/.gitignore +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-001-visual-grammar.md +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-002-architecture.md +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-003-api-design.md +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/docs/GLOSSARY.md +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/__main__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/adapters/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/adapters/_core.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/test.ipynb +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_adapter.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_format.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_layout.py +0 -0
- {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_stats.py +0 -0
arrscope-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arrscope
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Beautiful n-dimensional array visualization for Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/vizarray/arrscope
|
|
6
|
+
Project-URL: Repository, https://github.com/vizarray/arrscope
|
|
7
|
+
Author: arrscope contributors
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: arrays,data-science,machine-learning,numpy,visualization
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: numpy>=1.24
|
|
19
|
+
Requires-Dist: rich>=13.0
|
|
20
|
+
Provides-Extra: jax
|
|
21
|
+
Requires-Dist: jax>=0.4; extra == 'jax'
|
|
22
|
+
Provides-Extra: tf
|
|
23
|
+
Requires-Dist: tensorflow>=2.12; extra == 'tf'
|
|
24
|
+
Provides-Extra: torch
|
|
25
|
+
Requires-Dist: torch>=2.0; extra == 'torch'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# arrscope
|
|
29
|
+
|
|
30
|
+
Visualize n-dimensional arrays in the terminal and Jupyter — with structural trees, tiled mosaics, array diffing, and distribution sparklines.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from arrscope import scope
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
scope(np.random.rand(3, 4, 5))
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, nested layers
|
|
42
|
+
- **Named axes**: Attach semantics (`batch`, `heads`, `h`, `w`)
|
|
43
|
+
- **Four color modes**:
|
|
44
|
+
- `dtype` — semantic colors by data type (blue=float, green=int, …)
|
|
45
|
+
- `heatmap` — diverging colormap (red → light → blue) by value
|
|
46
|
+
- `sparsity` — zeros as `·`, non-zeros in bold
|
|
47
|
+
- `diff` — compare with a reference array (red=increased, blue=decreased, grey=unchanged)
|
|
48
|
+
- **Two render styles**:
|
|
49
|
+
- `tree` (default) — hierarchical branch view with colored guide lines
|
|
50
|
+
- `mosaic` — all 2D sub-slices tiled side by side as numeric tables
|
|
51
|
+
- **Distribution sparkline**: every output shows a unicode histogram (`▁▂▃▄▅▆▇█`) of value distribution — replaces text stats
|
|
52
|
+
- **Array diffing**: pass `reference=` to compare any two arrays. Color-coded per-cell changes + aggregate metrics (MSE, % changed)
|
|
53
|
+
- **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
|
|
54
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
55
|
+
- **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
|
|
56
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
|
|
57
|
+
- **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install arrscope
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
|
|
66
|
+
|
|
67
|
+
## Quick start
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from arrscope import scope
|
|
71
|
+
import numpy as np
|
|
72
|
+
|
|
73
|
+
# Auto-detect — last 2 dims form the grid
|
|
74
|
+
scope(np.random.rand(3, 4, 5))
|
|
75
|
+
|
|
76
|
+
# Named axes
|
|
77
|
+
scope(
|
|
78
|
+
np.random.rand(2, 8, 32, 32),
|
|
79
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
80
|
+
grid=['h', 'w'],
|
|
81
|
+
title='Attention heads',
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Custom grid dims
|
|
85
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
86
|
+
|
|
87
|
+
# Method chaining
|
|
88
|
+
r = scope(arr, mode='heatmap')
|
|
89
|
+
print(r.tree())
|
|
90
|
+
print(r.mosaic())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Color modes
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
scope(arr, mode='dtype') # default — blue floats, green ints, ...
|
|
97
|
+
scope(arr, mode='heatmap') # diverging colormap by value
|
|
98
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
99
|
+
scope(arr, mode='diff', # red/blue by change direction
|
|
100
|
+
reference=original_array) # Δ -0.5 ▁▂▃▄▅▆▇█ +0.5 mse=0.02 12% changed
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Array diffing
|
|
104
|
+
|
|
105
|
+
Compare any two arrays of the same shape. Values show the current array; color encodes the change:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
before = np.random.rand(3, 4, 5)
|
|
109
|
+
after = before + np.random.normal(0, 0.1, before.shape)
|
|
110
|
+
|
|
111
|
+
scope(after, reference=before, mode="diff")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Diff stats replace the sparkline: range of deltas, MSE, and percentage of elements that changed.
|
|
115
|
+
|
|
116
|
+
## Render styles
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
scope(arr) # tree (default) — click-to-expand layers
|
|
120
|
+
scope(arr, render_style='mosaic') # all sub-slices tiled side by side
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## CLI
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
arrscope 3x4x5
|
|
127
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
128
|
+
arrscope 20x4x5 --max-height 6
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Framework support
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import torch; scope(torch.randn(2, 3, 4))
|
|
135
|
+
import tensorflow as tf; scope(tf.random.uniform((2, 3, 4)))
|
|
136
|
+
import jax.numpy as jnp; scope(jnp.array([[1, 2], [3, 4]]))
|
|
137
|
+
from tinygrad import Tensor; scope(Tensor.randn(3, 4))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## API
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
scope(
|
|
144
|
+
arr,
|
|
145
|
+
axes=None, # list[str] — name each dimension
|
|
146
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
147
|
+
title=None, # str — heading above the visualization
|
|
148
|
+
max_height=20, # int | None — rows before truncation, None disables
|
|
149
|
+
fmt=None, # str — format spec like '.4f'
|
|
150
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity' | 'diff'
|
|
151
|
+
render_style='tree', # 'tree' | 'mosaic'
|
|
152
|
+
reference=None, # array-like — reference for diff mode
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone https://github.com/vizarray/arrscope
|
|
160
|
+
cd arrscope
|
|
161
|
+
uv sync
|
|
162
|
+
uv run pytest
|
|
163
|
+
uv run python main.py
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
arrscope-0.4.0/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# arrscope
|
|
2
|
+
|
|
3
|
+
Visualize n-dimensional arrays in the terminal and Jupyter — with structural trees, tiled mosaics, array diffing, and distribution sparklines.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from arrscope import scope
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
scope(np.random.rand(3, 4, 5))
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, nested layers
|
|
15
|
+
- **Named axes**: Attach semantics (`batch`, `heads`, `h`, `w`)
|
|
16
|
+
- **Four color modes**:
|
|
17
|
+
- `dtype` — semantic colors by data type (blue=float, green=int, …)
|
|
18
|
+
- `heatmap` — diverging colormap (red → light → blue) by value
|
|
19
|
+
- `sparsity` — zeros as `·`, non-zeros in bold
|
|
20
|
+
- `diff` — compare with a reference array (red=increased, blue=decreased, grey=unchanged)
|
|
21
|
+
- **Two render styles**:
|
|
22
|
+
- `tree` (default) — hierarchical branch view with colored guide lines
|
|
23
|
+
- `mosaic` — all 2D sub-slices tiled side by side as numeric tables
|
|
24
|
+
- **Distribution sparkline**: every output shows a unicode histogram (`▁▂▃▄▅▆▇█`) of value distribution — replaces text stats
|
|
25
|
+
- **Array diffing**: pass `reference=` to compare any two arrays. Color-coded per-cell changes + aggregate metrics (MSE, % changed)
|
|
26
|
+
- **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
|
|
27
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
28
|
+
- **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
|
|
29
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
|
|
30
|
+
- **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install arrscope
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from arrscope import scope
|
|
44
|
+
import numpy as np
|
|
45
|
+
|
|
46
|
+
# Auto-detect — last 2 dims form the grid
|
|
47
|
+
scope(np.random.rand(3, 4, 5))
|
|
48
|
+
|
|
49
|
+
# Named axes
|
|
50
|
+
scope(
|
|
51
|
+
np.random.rand(2, 8, 32, 32),
|
|
52
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
53
|
+
grid=['h', 'w'],
|
|
54
|
+
title='Attention heads',
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Custom grid dims
|
|
58
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
59
|
+
|
|
60
|
+
# Method chaining
|
|
61
|
+
r = scope(arr, mode='heatmap')
|
|
62
|
+
print(r.tree())
|
|
63
|
+
print(r.mosaic())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Color modes
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
scope(arr, mode='dtype') # default — blue floats, green ints, ...
|
|
70
|
+
scope(arr, mode='heatmap') # diverging colormap by value
|
|
71
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
72
|
+
scope(arr, mode='diff', # red/blue by change direction
|
|
73
|
+
reference=original_array) # Δ -0.5 ▁▂▃▄▅▆▇█ +0.5 mse=0.02 12% changed
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Array diffing
|
|
77
|
+
|
|
78
|
+
Compare any two arrays of the same shape. Values show the current array; color encodes the change:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
before = np.random.rand(3, 4, 5)
|
|
82
|
+
after = before + np.random.normal(0, 0.1, before.shape)
|
|
83
|
+
|
|
84
|
+
scope(after, reference=before, mode="diff")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Diff stats replace the sparkline: range of deltas, MSE, and percentage of elements that changed.
|
|
88
|
+
|
|
89
|
+
## Render styles
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
scope(arr) # tree (default) — click-to-expand layers
|
|
93
|
+
scope(arr, render_style='mosaic') # all sub-slices tiled side by side
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## CLI
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
arrscope 3x4x5
|
|
100
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
101
|
+
arrscope 20x4x5 --max-height 6
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Framework support
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import torch; scope(torch.randn(2, 3, 4))
|
|
108
|
+
import tensorflow as tf; scope(tf.random.uniform((2, 3, 4)))
|
|
109
|
+
import jax.numpy as jnp; scope(jnp.array([[1, 2], [3, 4]]))
|
|
110
|
+
from tinygrad import Tensor; scope(Tensor.randn(3, 4))
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## API
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
scope(
|
|
117
|
+
arr,
|
|
118
|
+
axes=None, # list[str] — name each dimension
|
|
119
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
120
|
+
title=None, # str — heading above the visualization
|
|
121
|
+
max_height=20, # int | None — rows before truncation, None disables
|
|
122
|
+
fmt=None, # str — format spec like '.4f'
|
|
123
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity' | 'diff'
|
|
124
|
+
render_style='tree', # 'tree' | 'mosaic'
|
|
125
|
+
reference=None, # array-like — reference for diff mode
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
git clone https://github.com/vizarray/arrscope
|
|
133
|
+
cd arrscope
|
|
134
|
+
uv sync
|
|
135
|
+
uv run pytest
|
|
136
|
+
uv run python main.py
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""arrscope render style examples.
|
|
2
|
+
|
|
3
|
+
Run this file to see both render styles in action.
|
|
4
|
+
Use method chaining to switch styles without re-specifying params.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run python examples/renders.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from arrscope import scope
|
|
13
|
+
|
|
14
|
+
# ── data ──────────────────────────────────────────────────────────────
|
|
15
|
+
arr_3d = np.arange(60).reshape(3, 4, 5).astype(float)
|
|
16
|
+
eye = np.eye(8)
|
|
17
|
+
sparse = np.zeros((6, 8), dtype=float)
|
|
18
|
+
sparse[0, 0] = 1.5
|
|
19
|
+
sparse[2, 3] = -2.3
|
|
20
|
+
sparse[4, 5] = 42.0
|
|
21
|
+
sparse[5, 7] = 0.7
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ── individual calls ──────────────────────────────────────────────────
|
|
25
|
+
print("=" * 56)
|
|
26
|
+
print("1 tree — hierarchical branch view (default)")
|
|
27
|
+
print("=" * 56)
|
|
28
|
+
print(scope(arr_3d, render_style="tree"))
|
|
29
|
+
|
|
30
|
+
print("=" * 56)
|
|
31
|
+
print("2 mosaic — numeric tables tiled side by side")
|
|
32
|
+
print("=" * 56)
|
|
33
|
+
print(scope(arr_3d, render_style="mosaic"))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ── method chaining ───────────────────────────────────────────────────
|
|
37
|
+
print("=" * 56)
|
|
38
|
+
print("3 method chaining — scope().tree() / .mosaic()")
|
|
39
|
+
print("=" * 56)
|
|
40
|
+
|
|
41
|
+
r = scope(eye, title="Identity", mode="heatmap")
|
|
42
|
+
|
|
43
|
+
print("--- .tree() ---")
|
|
44
|
+
print(r.tree())
|
|
45
|
+
|
|
46
|
+
print("--- .mosaic() ---")
|
|
47
|
+
print(r.mosaic())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── all three modes with mosaic ──────────────────────────────────────
|
|
51
|
+
print("=" * 56)
|
|
52
|
+
print("4 mosaic + all three color modes")
|
|
53
|
+
print("=" * 56)
|
|
54
|
+
|
|
55
|
+
print("--- dtype ---")
|
|
56
|
+
print(scope(sparse, render_style="mosaic", mode="dtype"))
|
|
57
|
+
|
|
58
|
+
print("--- heatmap ---")
|
|
59
|
+
print(scope(sparse, render_style="mosaic", mode="heatmap"))
|
|
60
|
+
|
|
61
|
+
print("--- sparsity ---")
|
|
62
|
+
print(scope(sparse, render_style="mosaic", mode="sparsity"))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ── large array in mosaic ────────────────────────────────────────────
|
|
66
|
+
print("=" * 56)
|
|
67
|
+
print("5 large array — (8, 8, 8) as mosaic + heatmap")
|
|
68
|
+
print("=" * 56)
|
|
69
|
+
print(scope(np.random.rand(8, 8, 8), render_style="mosaic", mode="heatmap"))
|
|
70
|
+
|
|
71
|
+
print("\nDone!")
|
|
@@ -62,8 +62,8 @@ print(scope(sparse, mode="sparsity"))
|
|
|
62
62
|
print("\n1️⃣2️⃣ HEATMAP + 3D + global stats")
|
|
63
63
|
print(scope(np.random.rand(2, 3, 4), mode="heatmap"))
|
|
64
64
|
|
|
65
|
-
print("\n1️⃣3️⃣
|
|
66
|
-
print(scope(np.
|
|
65
|
+
print("\n1️⃣3️⃣ TRUNCATION OFF (max_height=None)")
|
|
66
|
+
print(scope(np.arange(60).reshape(3, 4, 5).astype(float), max_height=None))
|
|
67
67
|
|
|
68
68
|
print("\n1️⃣4️⃣ MOSAIC — 3×4×5 tiled (no tree)")
|
|
69
69
|
arr = np.arange(60).reshape(3, 4, 5).astype(float)
|
|
@@ -89,4 +89,9 @@ print(
|
|
|
89
89
|
)
|
|
90
90
|
)
|
|
91
91
|
|
|
92
|
+
print("\n1️⃣6️⃣ DIFF — compare two arrays (value = current, color = change)")
|
|
93
|
+
a = np.random.rand(4, 5)
|
|
94
|
+
b = a + np.random.normal(0, 0.1, a.shape)
|
|
95
|
+
print(scope(a, reference=b, mode="diff"))
|
|
96
|
+
|
|
92
97
|
print("\n Done!")
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
from typing import Literal
|
|
4
2
|
|
|
5
|
-
from arrscope._config import config
|
|
6
|
-
from arrscope._format import format_value
|
|
7
3
|
from arrscope._layout import build_layout, compute_stats
|
|
8
4
|
from arrscope._types import VizOutput
|
|
9
5
|
from arrscope.adapters import to_numpy
|
|
10
6
|
from arrscope.renderers import render_html, render_terminal
|
|
11
7
|
|
|
12
|
-
Mode = Literal["dtype", "heatmap", "sparsity"]
|
|
8
|
+
Mode = Literal["dtype", "heatmap", "sparsity", "diff"]
|
|
13
9
|
RenderStyle = Literal["tree", "mosaic"]
|
|
14
10
|
|
|
15
11
|
|
|
@@ -19,14 +15,11 @@ def scope(
|
|
|
19
15
|
axes: list[str] | None = None,
|
|
20
16
|
grid: list[str | int] | None = None,
|
|
21
17
|
title: str | None = None,
|
|
22
|
-
|
|
23
|
-
max_height: int | None = None,
|
|
18
|
+
max_height: int | None = 20,
|
|
24
19
|
fmt: str | None = None,
|
|
25
|
-
color: bool | None = None,
|
|
26
|
-
style: Literal["auto", "terminal", "html"] = "auto",
|
|
27
20
|
mode: Mode | None = None,
|
|
28
21
|
render_style: RenderStyle | None = None,
|
|
29
|
-
|
|
22
|
+
reference=None,
|
|
30
23
|
) -> VizOutput:
|
|
31
24
|
"""Visualize an n-dimensional array in the terminal and/or Jupyter.
|
|
32
25
|
|
|
@@ -45,41 +38,36 @@ def scope(
|
|
|
45
38
|
title: Optional heading displayed above the visualization.
|
|
46
39
|
max_height: Max tree rows before truncation. The first half and
|
|
47
40
|
last half of children are shown with ``... (N more)`` in between.
|
|
48
|
-
|
|
41
|
+
Defaults to 20. Set to ``None`` to disable truncation.
|
|
49
42
|
fmt: Format spec for float values, e.g. ``".4f"``, ``".2e"``.
|
|
50
43
|
Auto-detected from value range when not set.
|
|
51
|
-
color: Enable or disable ANSI / CSS color output.
|
|
52
|
-
Defaults to ``True``.
|
|
53
|
-
style: Output target. ``"auto"`` detects Jupyter and renders
|
|
54
|
-
HTML there, ANSI elsewhere. ``"terminal"`` forces ANSI,
|
|
55
|
-
``"html"`` forces HTML.
|
|
56
44
|
mode: Colorization strategy:
|
|
57
45
|
|
|
58
|
-
- ``"dtype"`` — Semantic colors by data type
|
|
59
|
-
green=int, magenta=bool, yellow=str
|
|
46
|
+
- ``"dtype"`` (default) — Semantic colors by data type
|
|
47
|
+
(blue=float, green=int, magenta=bool, yellow=str).
|
|
60
48
|
Negative values tinted red, positive tinted blue.
|
|
61
49
|
- ``"heatmap"`` — Diverging colormap (red → light → blue)
|
|
62
|
-
scaled to the array's global min/max range.
|
|
63
|
-
spotting value patterns.
|
|
50
|
+
scaled to the array's global min/max range.
|
|
64
51
|
- ``"sparsity"`` — Zeros rendered as dim ``·``, non-zero
|
|
65
|
-
values in bold.
|
|
52
|
+
values in bold.
|
|
53
|
+
- ``"diff"`` — Compare with a reference array. Values
|
|
54
|
+
colored red/blue by how much they increased/decreased.
|
|
55
|
+
Requires ``reference``.
|
|
66
56
|
|
|
67
|
-
render_style: Visual layout
|
|
57
|
+
render_style: Visual layout:
|
|
68
58
|
|
|
69
|
-
- ``"tree"`` (default) — Hierarchical branch view with
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
contact-sheet grid. Best for comparing slices in 3D/4D
|
|
74
|
-
arrays (batches, attention heads, channels).
|
|
59
|
+
- ``"tree"`` (default) — Hierarchical branch view with
|
|
60
|
+
click-to-expand nested layers.
|
|
61
|
+
- ``"mosaic"`` — All 2D sub-slices tiled side by side as
|
|
62
|
+
numeric tables.
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
|
|
64
|
+
reference: A reference array to compare against. Must have
|
|
65
|
+
the same shape as ``arr``. When provided, ``mode`` defaults
|
|
66
|
+
to ``"diff"`` and stats show difference metrics.
|
|
78
67
|
|
|
79
68
|
Returns:
|
|
80
|
-
A ``VizOutput``
|
|
81
|
-
|
|
82
|
-
In the terminal, ``print()`` or ``str()`` renders the ANSI output.
|
|
69
|
+
A ``VizOutput`` with ``.tree()`` and ``.mosaic()`` methods.
|
|
70
|
+
Renders as ANSI in the terminal and as HTML in Jupyter.
|
|
83
71
|
|
|
84
72
|
Examples:
|
|
85
73
|
>>> import numpy as np
|
|
@@ -88,59 +76,53 @@ def scope(
|
|
|
88
76
|
|
|
89
77
|
>>> scope(np.eye(5), title="Identity", mode="sparsity")
|
|
90
78
|
|
|
91
|
-
>>>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
... mode="heatmap",
|
|
95
|
-
... )
|
|
79
|
+
>>> a = np.random.rand(3, 4, 5)
|
|
80
|
+
>>> b = a + np.random.normal(0, 0.1, a.shape)
|
|
81
|
+
>>> scope(a, reference=b, mode="diff")
|
|
96
82
|
"""
|
|
97
83
|
arr_np = to_numpy(arr)
|
|
98
84
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
if reference is not None:
|
|
86
|
+
ref_np = to_numpy(reference)
|
|
87
|
+
if ref_np.shape != arr_np.shape:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"reference shape {ref_np.shape} does not match arr shape {arr_np.shape}"
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
ref_np = None
|
|
93
|
+
|
|
94
|
+
mode = mode or ("diff" if ref_np is not None else "dtype")
|
|
95
|
+
rs = render_style or "tree"
|
|
105
96
|
|
|
106
|
-
if mode not in ("dtype", "heatmap", "sparsity"):
|
|
107
|
-
raise ValueError(f"Unknown mode '{mode}'. Expected
|
|
97
|
+
if mode not in ("dtype", "heatmap", "sparsity", "diff"):
|
|
98
|
+
raise ValueError(f"Unknown mode '{mode}'. Expected: dtype, heatmap, sparsity, diff")
|
|
99
|
+
|
|
100
|
+
if mode == "diff" and ref_np is None:
|
|
101
|
+
raise ValueError("mode='diff' requires a reference array via the `reference` parameter")
|
|
108
102
|
|
|
109
103
|
if rs not in ("tree", "mosaic"):
|
|
110
104
|
raise ValueError(f"Unknown render_style '{rs}'. Expected one of: tree, mosaic")
|
|
111
105
|
|
|
112
106
|
grid_dims = _resolve_grid_dims(arr_np, axes, grid)
|
|
107
|
+
use_html = _in_jupyter()
|
|
113
108
|
|
|
114
|
-
|
|
115
|
-
arr_np,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
global_stats = compute_stats(arr_np) if (show_stats or mode == "heatmap") else None
|
|
121
|
-
if global_stats and show_stats:
|
|
122
|
-
node.stats = global_stats
|
|
123
|
-
|
|
124
|
-
use_html = _use_html(style)
|
|
125
|
-
use_terminal = _use_terminal(style)
|
|
126
|
-
|
|
127
|
-
output = VizOutput()
|
|
128
|
-
|
|
129
|
-
if use_html:
|
|
130
|
-
output.html = _wrap_html(
|
|
131
|
-
render_html(node, color=color, mode=mode, global_stats=global_stats, render_style=rs), title
|
|
132
|
-
)
|
|
109
|
+
def _render(style: str) -> VizOutput:
|
|
110
|
+
node = build_layout(arr_np, grid_dims=grid_dims, max_height=max_height, reference=ref_np)
|
|
111
|
+
gs = compute_stats(arr_np, reference=ref_np)
|
|
112
|
+
if gs:
|
|
113
|
+
node.stats = gs
|
|
133
114
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
115
|
+
out = VizOutput(_rerender=_render)
|
|
116
|
+
if use_html:
|
|
117
|
+
out._html = _wrap_html(render_html(node, mode=mode, global_stats=gs, render_style=style), title)
|
|
118
|
+
out._ansi = _wrap_ansi(render_terminal(node, mode=mode, global_stats=gs, render_style=style), title)
|
|
119
|
+
return out
|
|
138
120
|
|
|
139
|
-
return
|
|
121
|
+
return _render(rs)
|
|
140
122
|
|
|
141
123
|
|
|
142
124
|
def _resolve_grid_dims(
|
|
143
|
-
arr
|
|
125
|
+
arr,
|
|
144
126
|
axes: list[str] | None,
|
|
145
127
|
grid: list[str | int] | None,
|
|
146
128
|
) -> list[int] | None:
|
|
@@ -166,22 +148,6 @@ def _resolve_grid_dims(
|
|
|
166
148
|
return resolved
|
|
167
149
|
|
|
168
150
|
|
|
169
|
-
def _use_html(style: str) -> bool:
|
|
170
|
-
if style == "html":
|
|
171
|
-
return True
|
|
172
|
-
if style == "terminal":
|
|
173
|
-
return False
|
|
174
|
-
return _in_jupyter()
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def _use_terminal(style: str) -> bool:
|
|
178
|
-
if style == "terminal":
|
|
179
|
-
return True
|
|
180
|
-
if style == "html":
|
|
181
|
-
return False
|
|
182
|
-
return True
|
|
183
|
-
|
|
184
|
-
|
|
185
151
|
def _in_jupyter() -> bool:
|
|
186
152
|
try:
|
|
187
153
|
from IPython import get_ipython
|
|
@@ -23,6 +23,18 @@ def format_value(val, fmt: str | None = None) -> str:
|
|
|
23
23
|
return str(val)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def sparkline(stats: dict) -> str:
|
|
27
|
+
"""Return a unicode histogram sparkline from pre-computed bins."""
|
|
28
|
+
bins = stats.get("histogram")
|
|
29
|
+
if not bins:
|
|
30
|
+
return ""
|
|
31
|
+
mx = max(bins)
|
|
32
|
+
if mx == 0:
|
|
33
|
+
return "▁" * len(bins)
|
|
34
|
+
chars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
|
|
35
|
+
return "".join(chars[min(int(b / mx * 7), 7)] for b in bins)
|
|
36
|
+
|
|
37
|
+
|
|
26
38
|
def _format_float(val: float, fmt: str | None) -> str:
|
|
27
39
|
if fmt is not None:
|
|
28
40
|
return f"{val:{fmt}}"
|