arrscope 0.2.0__tar.gz → 0.3.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.3.0/PKG-INFO +158 -0
- arrscope-0.3.0/README.md +131 -0
- arrscope-0.3.0/examples/renders.py +71 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/main.py +2 -2
- {arrscope-0.2.0 → arrscope-0.3.0}/pyproject.toml +1 -1
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/_api.py +29 -79
- arrscope-0.3.0/src/arrscope/_config.py +10 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/_types.py +11 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/renderers/html.py +2 -17
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/renderers/terminal.py +17 -31
- {arrscope-0.2.0 → arrscope-0.3.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.3.0}/.gitignore +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/docs/ADR-001-visual-grammar.md +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/docs/ADR-002-architecture.md +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/docs/ADR-003-api-design.md +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/docs/GLOSSARY.md +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/__main__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/_format.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/_layout.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/adapters/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/adapters/_core.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/src/arrscope/renderers/__init__.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/test.ipynb +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/tests/test_adapter.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/tests/test_format.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/tests/test_layout.py +0 -0
- {arrscope-0.2.0 → arrscope-0.3.0}/tests/test_stats.py +0 -0
arrscope-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arrscope
|
|
3
|
+
Version: 0.3.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, and color-coded value maps.
|
|
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
|
+
- **Three 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
|
+
- **Two render styles**:
|
|
48
|
+
- `tree` (default) — hierarchical branch view with colored guide lines
|
|
49
|
+
- `mosaic` — all 2D sub-slices tiled side by side as numeric tables
|
|
50
|
+
- **Method chaining**: `scope(arr, mode="heatmap").tree().mosaic()` — switch styles without re-specifying
|
|
51
|
+
- **Stats overlay**: min, max, mean, std, zero%, NaN count (always shown)
|
|
52
|
+
- **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
|
|
53
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
54
|
+
- **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
|
|
55
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
|
|
56
|
+
- **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install arrscope
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from arrscope import scope
|
|
70
|
+
import numpy as np
|
|
71
|
+
|
|
72
|
+
# Auto-detect — last 2 dims form the grid
|
|
73
|
+
scope(np.random.rand(3, 4, 5))
|
|
74
|
+
|
|
75
|
+
# Named axes
|
|
76
|
+
scope(
|
|
77
|
+
np.random.rand(2, 8, 32, 32),
|
|
78
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
79
|
+
grid=['h', 'w'],
|
|
80
|
+
title='Attention heads',
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Custom grid dims
|
|
84
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Color modes
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
scope(arr, mode='dtype') # default
|
|
91
|
+
scope(arr, mode='heatmap') # diverging colormap
|
|
92
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Render styles
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
scope(arr) # tree (default) — click-to-expand layers
|
|
99
|
+
scope(arr, render_style='mosaic') # all sub-slices tiled side by side
|
|
100
|
+
|
|
101
|
+
# Method chaining for switching styles
|
|
102
|
+
r = scope(arr, mode='heatmap')
|
|
103
|
+
print(r.tree())
|
|
104
|
+
print(r.mosaic())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## CLI
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
arrscope 3x4x5
|
|
111
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
112
|
+
arrscope 20x4x5 --max-height 6
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Framework support
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import torch
|
|
119
|
+
scope(torch.randn(2, 3, 4))
|
|
120
|
+
|
|
121
|
+
import tensorflow as tf
|
|
122
|
+
scope(tf.random.uniform((2, 3, 4)))
|
|
123
|
+
|
|
124
|
+
import jax.numpy as jnp
|
|
125
|
+
scope(jnp.array([[1, 2], [3, 4]]))
|
|
126
|
+
|
|
127
|
+
from tinygrad import Tensor
|
|
128
|
+
scope(Tensor.randn(3, 4))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
scope(
|
|
135
|
+
arr,
|
|
136
|
+
axes=None, # list[str] — name each dimension
|
|
137
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
138
|
+
title=None, # str — heading above the visualization
|
|
139
|
+
max_height=20, # int | None — rows before truncation, None disables
|
|
140
|
+
fmt=None, # str — format spec like '.4f'
|
|
141
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
142
|
+
render_style='tree', # 'tree' | 'mosaic'
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git clone https://github.com/vizarray/arrscope
|
|
150
|
+
cd arrscope
|
|
151
|
+
uv sync
|
|
152
|
+
uv run pytest
|
|
153
|
+
uv run python main.py
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
arrscope-0.3.0/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# arrscope
|
|
2
|
+
|
|
3
|
+
Visualize n-dimensional arrays in the terminal and Jupyter — with structural trees, tiled mosaics, and color-coded value maps.
|
|
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
|
+
- **Three 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
|
+
- **Two render styles**:
|
|
21
|
+
- `tree` (default) — hierarchical branch view with colored guide lines
|
|
22
|
+
- `mosaic` — all 2D sub-slices tiled side by side as numeric tables
|
|
23
|
+
- **Method chaining**: `scope(arr, mode="heatmap").tree().mosaic()` — switch styles without re-specifying
|
|
24
|
+
- **Stats overlay**: min, max, mean, std, zero%, NaN count (always shown)
|
|
25
|
+
- **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
|
|
26
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
27
|
+
- **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
|
|
28
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
|
|
29
|
+
- **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install arrscope
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from arrscope import scope
|
|
43
|
+
import numpy as np
|
|
44
|
+
|
|
45
|
+
# Auto-detect — last 2 dims form the grid
|
|
46
|
+
scope(np.random.rand(3, 4, 5))
|
|
47
|
+
|
|
48
|
+
# Named axes
|
|
49
|
+
scope(
|
|
50
|
+
np.random.rand(2, 8, 32, 32),
|
|
51
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
52
|
+
grid=['h', 'w'],
|
|
53
|
+
title='Attention heads',
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Custom grid dims
|
|
57
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Color modes
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
scope(arr, mode='dtype') # default
|
|
64
|
+
scope(arr, mode='heatmap') # diverging colormap
|
|
65
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Render styles
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
scope(arr) # tree (default) — click-to-expand layers
|
|
72
|
+
scope(arr, render_style='mosaic') # all sub-slices tiled side by side
|
|
73
|
+
|
|
74
|
+
# Method chaining for switching styles
|
|
75
|
+
r = scope(arr, mode='heatmap')
|
|
76
|
+
print(r.tree())
|
|
77
|
+
print(r.mosaic())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## CLI
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
arrscope 3x4x5
|
|
84
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
85
|
+
arrscope 20x4x5 --max-height 6
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Framework support
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import torch
|
|
92
|
+
scope(torch.randn(2, 3, 4))
|
|
93
|
+
|
|
94
|
+
import tensorflow as tf
|
|
95
|
+
scope(tf.random.uniform((2, 3, 4)))
|
|
96
|
+
|
|
97
|
+
import jax.numpy as jnp
|
|
98
|
+
scope(jnp.array([[1, 2], [3, 4]]))
|
|
99
|
+
|
|
100
|
+
from tinygrad import Tensor
|
|
101
|
+
scope(Tensor.randn(3, 4))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## API
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
scope(
|
|
108
|
+
arr,
|
|
109
|
+
axes=None, # list[str] — name each dimension
|
|
110
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
111
|
+
title=None, # str — heading above the visualization
|
|
112
|
+
max_height=20, # int | None — rows before truncation, None disables
|
|
113
|
+
fmt=None, # str — format spec like '.4f'
|
|
114
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
115
|
+
render_style='tree', # 'tree' | 'mosaic'
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/vizarray/arrscope
|
|
123
|
+
cd arrscope
|
|
124
|
+
uv sync
|
|
125
|
+
uv run pytest
|
|
126
|
+
uv run python main.py
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
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)
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
from typing import Literal
|
|
4
2
|
|
|
5
|
-
from arrscope._config import config
|
|
6
3
|
from arrscope._format import format_value
|
|
7
4
|
from arrscope._layout import build_layout, compute_stats
|
|
8
5
|
from arrscope._types import VizOutput
|
|
@@ -19,14 +16,10 @@ def scope(
|
|
|
19
16
|
axes: list[str] | None = None,
|
|
20
17
|
grid: list[str | int] | None = None,
|
|
21
18
|
title: str | None = None,
|
|
22
|
-
|
|
23
|
-
max_height: int | None = None,
|
|
19
|
+
max_height: int | None = 20,
|
|
24
20
|
fmt: str | None = None,
|
|
25
|
-
color: bool | None = None,
|
|
26
|
-
style: Literal["auto", "terminal", "html"] = "auto",
|
|
27
21
|
mode: Mode | None = None,
|
|
28
22
|
render_style: RenderStyle | None = None,
|
|
29
|
-
stats: bool | None = None,
|
|
30
23
|
) -> VizOutput:
|
|
31
24
|
"""Visualize an n-dimensional array in the terminal and/or Jupyter.
|
|
32
25
|
|
|
@@ -45,41 +38,30 @@ 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.
|
|
66
|
-
|
|
67
|
-
render_style: Visual layout style:
|
|
52
|
+
values in bold.
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
grids nested at the leaves. Each depth level gets a
|
|
71
|
-
distinct branch color for readability.
|
|
72
|
-
- ``"mosaic"`` — All 2D sub-slices tiled side by side in a
|
|
73
|
-
contact-sheet grid. Best for comparing slices in 3D/4D
|
|
74
|
-
arrays (batches, attention heads, channels).
|
|
54
|
+
render_style: Visual layout:
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
- ``"tree"`` (default) — Hierarchical branch view with
|
|
57
|
+
click-to-expand nested layers.
|
|
58
|
+
- ``"mosaic"`` — All 2D sub-slices tiled side by side as
|
|
59
|
+
numeric tables.
|
|
78
60
|
|
|
79
61
|
Returns:
|
|
80
|
-
A ``VizOutput``
|
|
81
|
-
|
|
82
|
-
|
|
62
|
+
A ``VizOutput`` with ``.ansi`` and ``.html`` attributes.
|
|
63
|
+
Use ``.tree()`` or ``.mosaic()`` to switch render styles
|
|
64
|
+
without re-specifying parameters.
|
|
83
65
|
|
|
84
66
|
Examples:
|
|
85
67
|
>>> import numpy as np
|
|
@@ -96,12 +78,8 @@ def scope(
|
|
|
96
78
|
"""
|
|
97
79
|
arr_np = to_numpy(arr)
|
|
98
80
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
max_height = max_height or config.max_height
|
|
102
|
-
mode = mode or config.mode
|
|
103
|
-
rs = render_style or config.render_style
|
|
104
|
-
show_stats = config.stats if stats is None else stats
|
|
81
|
+
mode = mode or "dtype"
|
|
82
|
+
rs = render_style or "tree"
|
|
105
83
|
|
|
106
84
|
if mode not in ("dtype", "heatmap", "sparsity"):
|
|
107
85
|
raise ValueError(f"Unknown mode '{mode}'. Expected one of: dtype, heatmap, sparsity")
|
|
@@ -110,37 +88,25 @@ def scope(
|
|
|
110
88
|
raise ValueError(f"Unknown render_style '{rs}'. Expected one of: tree, mosaic")
|
|
111
89
|
|
|
112
90
|
grid_dims = _resolve_grid_dims(arr_np, axes, grid)
|
|
91
|
+
use_html = _in_jupyter()
|
|
113
92
|
|
|
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)
|
|
93
|
+
def _render(style: str) -> VizOutput:
|
|
94
|
+
node = build_layout(arr_np, grid_dims=grid_dims, max_height=max_height)
|
|
95
|
+
gs = compute_stats(arr_np)
|
|
96
|
+
if gs:
|
|
97
|
+
node.stats = gs
|
|
126
98
|
|
|
127
|
-
|
|
99
|
+
out = VizOutput(_rerender=_render)
|
|
100
|
+
if use_html:
|
|
101
|
+
out.html = _wrap_html(render_html(node, mode=mode, global_stats=gs, render_style=style), title)
|
|
102
|
+
out.ansi = _wrap_ansi(render_terminal(node, mode=mode, global_stats=gs, render_style=style), title)
|
|
103
|
+
return out
|
|
128
104
|
|
|
129
|
-
|
|
130
|
-
output.html = _wrap_html(
|
|
131
|
-
render_html(node, color=color, mode=mode, global_stats=global_stats, render_style=rs), title
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if use_terminal:
|
|
135
|
-
output.ansi = _wrap_ansi(
|
|
136
|
-
render_terminal(node, color=color, mode=mode, global_stats=global_stats, render_style=rs), title
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
return output
|
|
105
|
+
return _render(rs)
|
|
140
106
|
|
|
141
107
|
|
|
142
108
|
def _resolve_grid_dims(
|
|
143
|
-
arr
|
|
109
|
+
arr,
|
|
144
110
|
axes: list[str] | None,
|
|
145
111
|
grid: list[str | int] | None,
|
|
146
112
|
) -> list[int] | None:
|
|
@@ -166,22 +132,6 @@ def _resolve_grid_dims(
|
|
|
166
132
|
return resolved
|
|
167
133
|
|
|
168
134
|
|
|
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
135
|
def _in_jupyter() -> bool:
|
|
186
136
|
try:
|
|
187
137
|
from IPython import get_ipython
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Callable
|
|
3
4
|
from dataclasses import dataclass, field
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
@@ -18,11 +19,15 @@ class VizNode:
|
|
|
18
19
|
stats: dict | None = None
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
_RerenderFn = Callable[[str], "VizOutput"]
|
|
23
|
+
|
|
24
|
+
|
|
21
25
|
@dataclass
|
|
22
26
|
class VizOutput:
|
|
23
27
|
html: str = ""
|
|
24
28
|
ansi: str = ""
|
|
25
29
|
metadata: dict = field(default_factory=dict)
|
|
30
|
+
_rerender: _RerenderFn | None = None
|
|
26
31
|
|
|
27
32
|
def _repr_html_(self) -> str:
|
|
28
33
|
return self.html
|
|
@@ -37,3 +42,9 @@ class VizOutput:
|
|
|
37
42
|
|
|
38
43
|
def __repr__(self) -> str:
|
|
39
44
|
return self.ansi
|
|
45
|
+
|
|
46
|
+
def tree(self) -> VizOutput:
|
|
47
|
+
return self._rerender("tree") if self._rerender else self
|
|
48
|
+
|
|
49
|
+
def mosaic(self) -> VizOutput:
|
|
50
|
+
return self._rerender("mosaic") if self._rerender else self
|
|
@@ -10,7 +10,6 @@ from arrscope._types import VizNode
|
|
|
10
10
|
|
|
11
11
|
def render_html(
|
|
12
12
|
node: VizNode,
|
|
13
|
-
color: bool = True,
|
|
14
13
|
mode: str = "dtype",
|
|
15
14
|
global_stats: dict | None = None,
|
|
16
15
|
render_style: str = "tree",
|
|
@@ -94,13 +93,6 @@ def _css() -> str:
|
|
|
94
93
|
}
|
|
95
94
|
.arrscope-row { display: flex; flex-wrap: wrap; gap: 12px; }
|
|
96
95
|
.arrscope-tree { margin-left: 16px; }
|
|
97
|
-
.arrscope-level-0 { }
|
|
98
|
-
.arrscope-level-1 { border-left: 2px solid #00bcd4; padding-left: 8px; }
|
|
99
|
-
.arrscope-level-2 { border-left: 2px solid #4caf50; padding-left: 8px; }
|
|
100
|
-
.arrscope-level-3 { border-left: 2px solid #ffc107; padding-left: 8px; }
|
|
101
|
-
.arrscope-level-4 { border-left: 2px solid #e91e63; padding-left: 8px; }
|
|
102
|
-
.arrscope-level-5 { border-left: 2px solid #9c27b0; padding-left: 8px; }
|
|
103
|
-
.arrscope-level-6 { border-left: 2px solid #2196f3; padding-left: 8px; }
|
|
104
96
|
.arrscope-ellipsis { color: #999; font-style: italic; margin: 2px 0; }
|
|
105
97
|
.arrscope-header {
|
|
106
98
|
font-weight: 600;
|
|
@@ -133,12 +125,6 @@ details.arrscope-details[open] > summary {
|
|
|
133
125
|
.arrscope-shape { color: #777; }
|
|
134
126
|
.arrscope-stats { color: #999; }
|
|
135
127
|
.arrscope-header { border-bottom-color: #444; }
|
|
136
|
-
.arrscope-level-1 { border-left-color: #26c6da; }
|
|
137
|
-
.arrscope-level-2 { border-left-color: #66bb6a; }
|
|
138
|
-
.arrscope-level-3 { border-left-color: #ffd54f; }
|
|
139
|
-
.arrscope-level-4 { border-left-color: #f06292; }
|
|
140
|
-
.arrscope-level-5 { border-left-color: #ab47bc; }
|
|
141
|
-
.arrscope-level-6 { border-left-color: #42a5f5; }
|
|
142
128
|
}
|
|
143
129
|
"""
|
|
144
130
|
|
|
@@ -169,7 +155,7 @@ def _render_node_html(
|
|
|
169
155
|
if not node.children and node.truncated:
|
|
170
156
|
return f'<div class="arrscope-ellipsis">{html_mod.escape(node.label)}</div>'
|
|
171
157
|
|
|
172
|
-
is_large_tree = depth > 0 and len(node.children) >
|
|
158
|
+
is_large_tree = depth > 0 and len(node.children) > 0
|
|
173
159
|
|
|
174
160
|
parts: list[str] = []
|
|
175
161
|
if depth == 0:
|
|
@@ -184,8 +170,7 @@ def _render_node_html(
|
|
|
184
170
|
parts.append(
|
|
185
171
|
f"<summary>{html_mod.escape(node.label or _format_header(node))}</summary>"
|
|
186
172
|
)
|
|
187
|
-
|
|
188
|
-
parts.append(f'<div class="arrscope-tree {level_class}">')
|
|
173
|
+
parts.append('<div class="arrscope-tree">')
|
|
189
174
|
|
|
190
175
|
for child in node.children:
|
|
191
176
|
parts.append(_render_node_html(child, depth + 1, mode, global_stats))
|
|
@@ -17,24 +17,23 @@ BRANCH_COLORS = ["grey50", "cyan", "green", "yellow", "magenta", "blue"]
|
|
|
17
17
|
|
|
18
18
|
def render_terminal(
|
|
19
19
|
node: VizNode,
|
|
20
|
-
color: bool = True,
|
|
21
20
|
mode: str = "dtype",
|
|
22
21
|
global_stats: dict | None = None,
|
|
23
22
|
render_style: str = "tree",
|
|
24
23
|
) -> str:
|
|
25
24
|
from rich.console import Console
|
|
26
25
|
|
|
27
|
-
console = Console(width=120, color_system="truecolor"
|
|
26
|
+
console = Console(width=120, color_system="truecolor")
|
|
28
27
|
parts: list = []
|
|
29
28
|
|
|
30
29
|
if render_style == "mosaic":
|
|
31
|
-
body = _render_mosaic(node,
|
|
30
|
+
body = _render_mosaic(node, mode, global_stats)
|
|
32
31
|
else:
|
|
33
|
-
body = _render_node(node, 0,
|
|
32
|
+
body = _render_node(node, 0, mode, global_stats)
|
|
34
33
|
parts.append(body)
|
|
35
34
|
|
|
36
35
|
if global_stats and node.stats:
|
|
37
|
-
parts.append(_stats_text(global_stats
|
|
36
|
+
parts.append(_stats_text(global_stats))
|
|
38
37
|
|
|
39
38
|
with console.capture() as capture:
|
|
40
39
|
for p in parts:
|
|
@@ -53,7 +52,6 @@ def _collect_leaves(node: VizNode) -> list[VizNode]:
|
|
|
53
52
|
|
|
54
53
|
def _render_mosaic(
|
|
55
54
|
node: VizNode,
|
|
56
|
-
color: bool,
|
|
57
55
|
mode: str,
|
|
58
56
|
global_stats: dict | None,
|
|
59
57
|
) -> Columns | Text:
|
|
@@ -64,14 +62,14 @@ def _render_mosaic(
|
|
|
64
62
|
tables: list[Table] = []
|
|
65
63
|
for leaf in leaves:
|
|
66
64
|
if leaf.grid_data is not None:
|
|
67
|
-
t = _render_grid(leaf.grid_data, leaf.label,
|
|
65
|
+
t = _render_grid(leaf.grid_data, leaf.label, mode, global_stats)
|
|
68
66
|
tables.append(t)
|
|
69
67
|
|
|
70
68
|
return Columns(tables, equal=True, expand=False)
|
|
71
69
|
|
|
72
70
|
|
|
73
|
-
def _stats_text(stats: dict
|
|
74
|
-
dim = Style(dim=True, color="grey50")
|
|
71
|
+
def _stats_text(stats: dict) -> Text:
|
|
72
|
+
dim = Style(dim=True, color="grey50")
|
|
75
73
|
t = Text()
|
|
76
74
|
fmt = ".4f" if stats.get("dtype", "").startswith(("float", "int")) else ".4f"
|
|
77
75
|
|
|
@@ -99,12 +97,11 @@ def _stats_text(stats: dict, color: bool) -> Text:
|
|
|
99
97
|
def _render_node(
|
|
100
98
|
node: VizNode,
|
|
101
99
|
depth: int,
|
|
102
|
-
color: bool,
|
|
103
100
|
mode: str,
|
|
104
101
|
global_stats: dict | None,
|
|
105
102
|
) -> Tree | Table | Text:
|
|
106
103
|
if node.grid_data is not None:
|
|
107
|
-
return _render_grid(node.grid_data, node.label,
|
|
104
|
+
return _render_grid(node.grid_data, node.label, mode, global_stats)
|
|
108
105
|
|
|
109
106
|
if not node.children and node.truncated:
|
|
110
107
|
t = Text(f" {node.label}")
|
|
@@ -112,25 +109,22 @@ def _render_node(
|
|
|
112
109
|
return t
|
|
113
110
|
|
|
114
111
|
guide_color = BRANCH_COLORS[(depth) % len(BRANCH_COLORS)]
|
|
115
|
-
guide = Style(dim=True, color=guide_color)
|
|
112
|
+
guide = Style(dim=True, color=guide_color)
|
|
116
113
|
|
|
117
|
-
tree = Tree(
|
|
118
|
-
_make_header(node, color),
|
|
119
|
-
guide_style=guide,
|
|
120
|
-
)
|
|
114
|
+
tree = Tree(_make_header(node), guide_style=guide)
|
|
121
115
|
for child in node.children:
|
|
122
|
-
child_renderable = _render_node(child, depth + 1,
|
|
116
|
+
child_renderable = _render_node(child, depth + 1, mode, global_stats)
|
|
123
117
|
tree.add(child_renderable)
|
|
124
118
|
return tree
|
|
125
119
|
|
|
126
120
|
|
|
127
|
-
def _make_header(node: VizNode
|
|
121
|
+
def _make_header(node: VizNode) -> Text:
|
|
128
122
|
parts = []
|
|
129
123
|
if node.label:
|
|
130
|
-
parts.append((node.label, "bold"
|
|
124
|
+
parts.append((node.label, "bold"))
|
|
131
125
|
shape_str = " × ".join(str(s) for s in node.shape)
|
|
132
126
|
if shape_str:
|
|
133
|
-
dim_style = Style(dim=True, color="grey50")
|
|
127
|
+
dim_style = Style(dim=True, color="grey50")
|
|
134
128
|
parts.append((f" ({shape_str})", dim_style))
|
|
135
129
|
t = Text()
|
|
136
130
|
for text, style in parts:
|
|
@@ -141,7 +135,6 @@ def _make_header(node: VizNode, color: bool) -> Text:
|
|
|
141
135
|
def _render_grid(
|
|
142
136
|
data: np.ndarray,
|
|
143
137
|
label: str,
|
|
144
|
-
color: bool,
|
|
145
138
|
mode: str,
|
|
146
139
|
global_stats: dict | None,
|
|
147
140
|
) -> Text | Table:
|
|
@@ -149,21 +142,20 @@ def _render_grid(
|
|
|
149
142
|
return Text(format_value(data.item()))
|
|
150
143
|
|
|
151
144
|
if data.ndim == 1:
|
|
152
|
-
return _render_1d(data,
|
|
145
|
+
return _render_1d(data, mode, global_stats)
|
|
153
146
|
|
|
154
|
-
return _render_2d(data, label,
|
|
147
|
+
return _render_2d(data, label, mode, global_stats)
|
|
155
148
|
|
|
156
149
|
|
|
157
150
|
def _render_1d(
|
|
158
151
|
data: np.ndarray,
|
|
159
|
-
color: bool,
|
|
160
152
|
mode: str,
|
|
161
153
|
global_stats: dict | None,
|
|
162
154
|
) -> Text:
|
|
163
155
|
items = []
|
|
164
156
|
for v in data:
|
|
165
157
|
text = _format_cell(v, mode)
|
|
166
|
-
items.append(_style_value(text, v,
|
|
158
|
+
items.append(_style_value(text, v, mode, global_stats))
|
|
167
159
|
|
|
168
160
|
t = Text("[")
|
|
169
161
|
for i, item in enumerate(items):
|
|
@@ -177,7 +169,6 @@ def _render_1d(
|
|
|
177
169
|
def _render_2d(
|
|
178
170
|
data: np.ndarray,
|
|
179
171
|
label: str,
|
|
180
|
-
color: bool,
|
|
181
172
|
mode: str,
|
|
182
173
|
global_stats: dict | None,
|
|
183
174
|
) -> Table:
|
|
@@ -199,7 +190,6 @@ def _render_2d(
|
|
|
199
190
|
_style_value(
|
|
200
191
|
_format_cell(data[row_idx, col_idx], mode),
|
|
201
192
|
data[row_idx, col_idx],
|
|
202
|
-
color,
|
|
203
193
|
mode,
|
|
204
194
|
global_stats,
|
|
205
195
|
)
|
|
@@ -229,13 +219,9 @@ def _is_zero(val) -> bool:
|
|
|
229
219
|
def _style_value(
|
|
230
220
|
text: str,
|
|
231
221
|
val,
|
|
232
|
-
color: bool,
|
|
233
222
|
mode: str,
|
|
234
223
|
global_stats: dict | None,
|
|
235
224
|
) -> Text:
|
|
236
|
-
if not color:
|
|
237
|
-
return Text(text)
|
|
238
|
-
|
|
239
225
|
if mode == "heatmap":
|
|
240
226
|
cell_style = _heatmap_color(val, global_stats)
|
|
241
227
|
elif mode == "sparsity":
|
|
@@ -18,7 +18,7 @@ wheels = [
|
|
|
18
18
|
|
|
19
19
|
[[package]]
|
|
20
20
|
name = "arrscope"
|
|
21
|
-
version = "0.
|
|
21
|
+
version = "0.2.0"
|
|
22
22
|
source = { editable = "." }
|
|
23
23
|
dependencies = [
|
|
24
24
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
arrscope-0.2.0/PKG-INFO
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: arrscope
|
|
3
|
-
Version: 0.2.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
|
-
Beautiful n-dimensional array visualization for Python — in the terminal and Jupyter.
|
|
31
|
-
|
|
32
|
-
```python
|
|
33
|
-
from arrscope import scope
|
|
34
|
-
import numpy as np
|
|
35
|
-
|
|
36
|
-
scope(np.random.rand(2, 3, 8, 8), axes=['batch', 'heads', 'h', 'w'])
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
├── [0] (3, 8, 8)
|
|
41
|
-
│ ├── [0,0]: 8×8 grid
|
|
42
|
-
│ ├── [0,1]: 8×8 grid
|
|
43
|
-
│ └── [0,2]: 8×8 grid
|
|
44
|
-
└── [1] (3, 8, 8)
|
|
45
|
-
...
|
|
46
|
-
min=0.001 max=0.999 mean=0.5 std=0.29 zeros=0.0%
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Features
|
|
50
|
-
|
|
51
|
-
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, collapsed hierarchies
|
|
52
|
-
- **Named axes**: Attach semantics to dimensions (`batch`, `heads`, `h`, `w`)
|
|
53
|
-
- **Configurable grid**: Pick which axes form the leaf 2D matrix
|
|
54
|
-
- **Three color modes**:
|
|
55
|
-
- `dtype` — semantic colors by data type (float=blue, int=green, bool=magenta, …)
|
|
56
|
-
- `heatmap` — diverging colormap (red→light→blue) by value magnitude
|
|
57
|
-
- `sparsity` — zeros as `·`, non-zeros highlighted in bold
|
|
58
|
-
- **Stats overlay**: min, max, mean, std, zero%, NaN count
|
|
59
|
-
- **Head/tail truncation**: large dimensions show first/last N slices with `…`
|
|
60
|
-
- **Smart precision**: auto-detects significant figures for floats
|
|
61
|
-
- **Terminal + Jupyter**: Rich ANSI output + static HTML/CSS with dark mode
|
|
62
|
-
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX (lazy imports, no hard deps)
|
|
63
|
-
|
|
64
|
-
## Install
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
pip install arrscope
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Only requires `numpy` + `rich`. Torch/TF/JAX are optional — pass any array type, it just works.
|
|
71
|
-
|
|
72
|
-
## Quick start
|
|
73
|
-
|
|
74
|
-
```python
|
|
75
|
-
from arrscope import scope
|
|
76
|
-
import numpy as np
|
|
77
|
-
|
|
78
|
-
# Auto-detect — last 2 dims form the grid
|
|
79
|
-
scope(np.random.rand(3, 4, 5))
|
|
80
|
-
|
|
81
|
-
# Named axes for clarity
|
|
82
|
-
scope(
|
|
83
|
-
np.random.rand(2, 8, 32, 32),
|
|
84
|
-
axes=['batch', 'heads', 'h', 'w'],
|
|
85
|
-
grid=['h', 'w'],
|
|
86
|
-
title='Attention heads',
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
# Pick any dims as the leaf grid
|
|
90
|
-
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Color modes
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
scope(arr, mode='dtype') # default — blue floats, green ints, etc.
|
|
97
|
-
scope(arr, mode='heatmap') # diverging colormap by value
|
|
98
|
-
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Stats overlay
|
|
102
|
-
|
|
103
|
-
Stats are shown by default. Hide with:
|
|
104
|
-
|
|
105
|
-
```python
|
|
106
|
-
scope(arr, stats=False)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Shows: `min=0.0 max=1.0 mean=0.5 std=0.29 zeros=12.5%`
|
|
110
|
-
|
|
111
|
-
## Truncation
|
|
112
|
-
|
|
113
|
-
```python
|
|
114
|
-
scope(np.random.rand(100, 32, 32), max_height=8)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Shows first 4 + last 4 slices with `… (92 more)` in between.
|
|
118
|
-
|
|
119
|
-
## Custom formatting
|
|
120
|
-
|
|
121
|
-
```python
|
|
122
|
-
scope(arr, fmt='.2f') # fixed precision
|
|
123
|
-
scope(arr, color=False) # monochrome
|
|
124
|
-
scope(arr, style='html') # force HTML output in terminal
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## CLI
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
# Install globally (ships with the library)
|
|
131
|
-
pip install arrscope
|
|
132
|
-
|
|
133
|
-
# Use from anywhere
|
|
134
|
-
arrscope 3x4x5
|
|
135
|
-
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
136
|
-
arrscope 20x4x5 --max-height 6 --no-stats
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Framework support
|
|
140
|
-
|
|
141
|
-
Pass any array-like — conversion is automatic:
|
|
142
|
-
|
|
143
|
-
```python
|
|
144
|
-
import torch
|
|
145
|
-
scope(torch.randn(2, 3, 4)) # PyTorch
|
|
146
|
-
|
|
147
|
-
import tensorflow as tf
|
|
148
|
-
scope(tf.random.uniform((2, 3, 4))) # TensorFlow
|
|
149
|
-
|
|
150
|
-
import jax.numpy as jnp
|
|
151
|
-
scope(jnp.array([[1, 2], [3, 4]])) # JAX
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## API
|
|
155
|
-
|
|
156
|
-
```python
|
|
157
|
-
scope(
|
|
158
|
-
arr, # np.ndarray | torch.Tensor | tf.Tensor | jax.Array
|
|
159
|
-
axes=None, # list[str] — name each dimension
|
|
160
|
-
grid=None, # list[str | int] — which dims form the leaf grid
|
|
161
|
-
title=None, # str — heading above the visualization
|
|
162
|
-
max_width=None, # int — max characters wide
|
|
163
|
-
max_height=None, # int — max rows before truncation
|
|
164
|
-
fmt=None, # str — format spec like '.4f'
|
|
165
|
-
color=True, # bool — enable/disable color
|
|
166
|
-
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
167
|
-
stats=True, # bool — show min/max/mean/std/zeros
|
|
168
|
-
style='auto', # 'auto' | 'terminal' | 'html'
|
|
169
|
-
)
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Development
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
git clone https://github.com/vizarray/arrscope
|
|
176
|
-
cd arrscope
|
|
177
|
-
uv sync
|
|
178
|
-
uv run pytest
|
|
179
|
-
uv run python main.py # demo script
|
|
180
|
-
uv run jupyter notebook test.ipynb # notebook demo
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## License
|
|
184
|
-
|
|
185
|
-
MIT
|
arrscope-0.2.0/README.md
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# arrscope
|
|
2
|
-
|
|
3
|
-
Beautiful n-dimensional array visualization for Python — in the terminal and Jupyter.
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from arrscope import scope
|
|
7
|
-
import numpy as np
|
|
8
|
-
|
|
9
|
-
scope(np.random.rand(2, 3, 8, 8), axes=['batch', 'heads', 'h', 'w'])
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
├── [0] (3, 8, 8)
|
|
14
|
-
│ ├── [0,0]: 8×8 grid
|
|
15
|
-
│ ├── [0,1]: 8×8 grid
|
|
16
|
-
│ └── [0,2]: 8×8 grid
|
|
17
|
-
└── [1] (3, 8, 8)
|
|
18
|
-
...
|
|
19
|
-
min=0.001 max=0.999 mean=0.5 std=0.29 zeros=0.0%
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, collapsed hierarchies
|
|
25
|
-
- **Named axes**: Attach semantics to dimensions (`batch`, `heads`, `h`, `w`)
|
|
26
|
-
- **Configurable grid**: Pick which axes form the leaf 2D matrix
|
|
27
|
-
- **Three color modes**:
|
|
28
|
-
- `dtype` — semantic colors by data type (float=blue, int=green, bool=magenta, …)
|
|
29
|
-
- `heatmap` — diverging colormap (red→light→blue) by value magnitude
|
|
30
|
-
- `sparsity` — zeros as `·`, non-zeros highlighted in bold
|
|
31
|
-
- **Stats overlay**: min, max, mean, std, zero%, NaN count
|
|
32
|
-
- **Head/tail truncation**: large dimensions show first/last N slices with `…`
|
|
33
|
-
- **Smart precision**: auto-detects significant figures for floats
|
|
34
|
-
- **Terminal + Jupyter**: Rich ANSI output + static HTML/CSS with dark mode
|
|
35
|
-
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX (lazy imports, no hard deps)
|
|
36
|
-
|
|
37
|
-
## Install
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
pip install arrscope
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Only requires `numpy` + `rich`. Torch/TF/JAX are optional — pass any array type, it just works.
|
|
44
|
-
|
|
45
|
-
## Quick start
|
|
46
|
-
|
|
47
|
-
```python
|
|
48
|
-
from arrscope import scope
|
|
49
|
-
import numpy as np
|
|
50
|
-
|
|
51
|
-
# Auto-detect — last 2 dims form the grid
|
|
52
|
-
scope(np.random.rand(3, 4, 5))
|
|
53
|
-
|
|
54
|
-
# Named axes for clarity
|
|
55
|
-
scope(
|
|
56
|
-
np.random.rand(2, 8, 32, 32),
|
|
57
|
-
axes=['batch', 'heads', 'h', 'w'],
|
|
58
|
-
grid=['h', 'w'],
|
|
59
|
-
title='Attention heads',
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
# Pick any dims as the leaf grid
|
|
63
|
-
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Color modes
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
scope(arr, mode='dtype') # default — blue floats, green ints, etc.
|
|
70
|
-
scope(arr, mode='heatmap') # diverging colormap by value
|
|
71
|
-
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Stats overlay
|
|
75
|
-
|
|
76
|
-
Stats are shown by default. Hide with:
|
|
77
|
-
|
|
78
|
-
```python
|
|
79
|
-
scope(arr, stats=False)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Shows: `min=0.0 max=1.0 mean=0.5 std=0.29 zeros=12.5%`
|
|
83
|
-
|
|
84
|
-
## Truncation
|
|
85
|
-
|
|
86
|
-
```python
|
|
87
|
-
scope(np.random.rand(100, 32, 32), max_height=8)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Shows first 4 + last 4 slices with `… (92 more)` in between.
|
|
91
|
-
|
|
92
|
-
## Custom formatting
|
|
93
|
-
|
|
94
|
-
```python
|
|
95
|
-
scope(arr, fmt='.2f') # fixed precision
|
|
96
|
-
scope(arr, color=False) # monochrome
|
|
97
|
-
scope(arr, style='html') # force HTML output in terminal
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## CLI
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
# Install globally (ships with the library)
|
|
104
|
-
pip install arrscope
|
|
105
|
-
|
|
106
|
-
# Use from anywhere
|
|
107
|
-
arrscope 3x4x5
|
|
108
|
-
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
109
|
-
arrscope 20x4x5 --max-height 6 --no-stats
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Framework support
|
|
113
|
-
|
|
114
|
-
Pass any array-like — conversion is automatic:
|
|
115
|
-
|
|
116
|
-
```python
|
|
117
|
-
import torch
|
|
118
|
-
scope(torch.randn(2, 3, 4)) # PyTorch
|
|
119
|
-
|
|
120
|
-
import tensorflow as tf
|
|
121
|
-
scope(tf.random.uniform((2, 3, 4))) # TensorFlow
|
|
122
|
-
|
|
123
|
-
import jax.numpy as jnp
|
|
124
|
-
scope(jnp.array([[1, 2], [3, 4]])) # JAX
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## API
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
scope(
|
|
131
|
-
arr, # np.ndarray | torch.Tensor | tf.Tensor | jax.Array
|
|
132
|
-
axes=None, # list[str] — name each dimension
|
|
133
|
-
grid=None, # list[str | int] — which dims form the leaf grid
|
|
134
|
-
title=None, # str — heading above the visualization
|
|
135
|
-
max_width=None, # int — max characters wide
|
|
136
|
-
max_height=None, # int — max rows before truncation
|
|
137
|
-
fmt=None, # str — format spec like '.4f'
|
|
138
|
-
color=True, # bool — enable/disable color
|
|
139
|
-
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
140
|
-
stats=True, # bool — show min/max/mean/std/zeros
|
|
141
|
-
style='auto', # 'auto' | 'terminal' | 'html'
|
|
142
|
-
)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Development
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
git clone https://github.com/vizarray/arrscope
|
|
149
|
-
cd arrscope
|
|
150
|
-
uv sync
|
|
151
|
-
uv run pytest
|
|
152
|
-
uv run python main.py # demo script
|
|
153
|
-
uv run jupyter notebook test.ipynb # notebook demo
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## License
|
|
157
|
-
|
|
158
|
-
MIT
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@dataclass
|
|
5
|
-
class Config:
|
|
6
|
-
max_width: int | None = None
|
|
7
|
-
max_height: int | None = None
|
|
8
|
-
color: bool = True
|
|
9
|
-
precision: int | None = None
|
|
10
|
-
mode: str = "dtype"
|
|
11
|
-
render_style: str = "tree"
|
|
12
|
-
stats: bool = True
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
config: Config = Config()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|