scitex 2.4.3__py3-none-any.whl → 2.5.0__py3-none-any.whl
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.
- scitex/__version__.py +1 -1
- scitex/io/_load.py +5 -0
- scitex/io/_load_modules/_canvas.py +171 -0
- scitex/io/_save.py +8 -0
- scitex/io/_save_modules/_canvas.py +356 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
- scitex/plt/utils/__init__.py +10 -0
- scitex/plt/utils/_collect_figure_metadata.py +14 -12
- scitex/plt/utils/_csv_column_naming.py +237 -0
- scitex/session/_decorator.py +13 -1
- scitex/vis/README.md +246 -615
- scitex/vis/__init__.py +138 -78
- scitex/vis/canvas.py +423 -0
- scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
- scitex/vis/editor/__init__.py +1 -1
- scitex/vis/editor/_dearpygui_editor.py +1830 -0
- scitex/vis/editor/_defaults.py +40 -1
- scitex/vis/editor/_edit.py +54 -18
- scitex/vis/editor/_flask_editor.py +37 -0
- scitex/vis/editor/_qt_editor.py +865 -0
- scitex/vis/editor/flask_editor/__init__.py +21 -0
- scitex/vis/editor/flask_editor/bbox.py +216 -0
- scitex/vis/editor/flask_editor/core.py +152 -0
- scitex/vis/editor/flask_editor/plotter.py +130 -0
- scitex/vis/editor/flask_editor/renderer.py +184 -0
- scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
- scitex/vis/editor/flask_editor/templates/html.py +295 -0
- scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
- scitex/vis/editor/flask_editor/templates/styles.py +549 -0
- scitex/vis/editor/flask_editor/utils.py +81 -0
- scitex/vis/io/__init__.py +84 -21
- scitex/vis/io/canvas.py +226 -0
- scitex/vis/io/data.py +204 -0
- scitex/vis/io/directory.py +202 -0
- scitex/vis/io/export.py +460 -0
- scitex/vis/io/panel.py +424 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/RECORD +42 -21
- scitex/vis/DJANGO_INTEGRATION.md +0 -677
- scitex/vis/editor/_web_editor.py +0 -1440
- scitex/vis/tmp.txt +0 -239
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-12-
|
|
3
|
+
# Timestamp: "2025-12-08 18:45:00 (ywatanabe)"
|
|
4
4
|
# File: ./src/scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py
|
|
5
5
|
|
|
6
6
|
"""CSV formatter for matplotlib plot() calls."""
|
|
@@ -12,6 +12,48 @@ import numpy as np
|
|
|
12
12
|
import pandas as pd
|
|
13
13
|
import xarray as xr
|
|
14
14
|
|
|
15
|
+
from scitex.plt.utils._csv_column_naming import get_csv_column_name
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _parse_tracking_id(id: str) -> tuple:
|
|
19
|
+
"""Parse tracking ID to extract axes position and trace index.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
id : str
|
|
24
|
+
Tracking ID like "ax_00_plot_0" or "plot_0"
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
tuple
|
|
29
|
+
(ax_row, ax_col, trace_index)
|
|
30
|
+
"""
|
|
31
|
+
ax_row, ax_col, trace_index = 0, 0, 0
|
|
32
|
+
|
|
33
|
+
if id.startswith('ax_'):
|
|
34
|
+
parts = id.split('_')
|
|
35
|
+
if len(parts) >= 2:
|
|
36
|
+
ax_pos = parts[1]
|
|
37
|
+
if len(ax_pos) >= 2:
|
|
38
|
+
try:
|
|
39
|
+
ax_row = int(ax_pos[0])
|
|
40
|
+
ax_col = int(ax_pos[1])
|
|
41
|
+
except ValueError:
|
|
42
|
+
pass
|
|
43
|
+
# Extract trace index from the rest (e.g., "plot_0" -> 0)
|
|
44
|
+
if len(parts) >= 4 and parts[2] == 'plot':
|
|
45
|
+
try:
|
|
46
|
+
trace_index = int(parts[3])
|
|
47
|
+
except ValueError:
|
|
48
|
+
pass
|
|
49
|
+
elif id.startswith('plot_'):
|
|
50
|
+
try:
|
|
51
|
+
trace_index = int(id.split('_')[1])
|
|
52
|
+
except (ValueError, IndexError):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
return ax_row, ax_col, trace_index
|
|
56
|
+
|
|
15
57
|
|
|
16
58
|
def _format_plot(
|
|
17
59
|
id: str,
|
|
@@ -30,7 +72,7 @@ def _format_plot(
|
|
|
30
72
|
Parameters
|
|
31
73
|
----------
|
|
32
74
|
id : str
|
|
33
|
-
Identifier prefix for the output columns (e.g., "
|
|
75
|
+
Identifier prefix for the output columns (e.g., "ax_00_plot_0").
|
|
34
76
|
tracked_dict : dict or None
|
|
35
77
|
Dictionary containing tracked data. May include:
|
|
36
78
|
- 'plot_df': Pre-formatted DataFrame from wrapper
|
|
@@ -41,25 +83,40 @@ def _format_plot(
|
|
|
41
83
|
Returns
|
|
42
84
|
-------
|
|
43
85
|
pd.DataFrame
|
|
44
|
-
Formatted data with columns
|
|
45
|
-
For 1D data:
|
|
46
|
-
For 2D data: {id}_plot_x00, {id}_plot_y00, {id}_plot_x01, ...
|
|
86
|
+
Formatted data with columns using single source of truth naming.
|
|
87
|
+
For 1D data: ax_00_plot_0_plot_x, ax_00_plot_0_plot_y
|
|
47
88
|
"""
|
|
48
89
|
# Check if tracked_dict is empty or not a dictionary
|
|
49
90
|
if not tracked_dict or not isinstance(tracked_dict, dict):
|
|
50
91
|
return pd.DataFrame()
|
|
51
92
|
|
|
93
|
+
# Parse the tracking ID to get axes position and trace index
|
|
94
|
+
ax_row, ax_col, trace_index = _parse_tracking_id(id)
|
|
95
|
+
|
|
52
96
|
# For stx_line, we expect a 'plot_df' key
|
|
53
97
|
if 'plot_df' in tracked_dict:
|
|
54
98
|
plot_df = tracked_dict['plot_df']
|
|
55
99
|
if isinstance(plot_df, pd.DataFrame):
|
|
56
|
-
#
|
|
57
|
-
|
|
100
|
+
# Rename columns using single source of truth
|
|
101
|
+
renamed = {}
|
|
102
|
+
for col in plot_df.columns:
|
|
103
|
+
if col == 'plot_x':
|
|
104
|
+
renamed[col] = get_csv_column_name('plot_x', ax_row, ax_col, trace_index=trace_index)
|
|
105
|
+
elif col == 'plot_y':
|
|
106
|
+
renamed[col] = get_csv_column_name('plot_y', ax_row, ax_col, trace_index=trace_index)
|
|
107
|
+
else:
|
|
108
|
+
# For other columns, just prefix with id
|
|
109
|
+
renamed[col] = f"{id}_{col}"
|
|
110
|
+
return plot_df.rename(columns=renamed)
|
|
58
111
|
|
|
59
112
|
# Handle raw args from __getattr__ proxied calls
|
|
60
113
|
if 'args' in tracked_dict:
|
|
61
114
|
args = tracked_dict['args']
|
|
62
115
|
if isinstance(args, tuple) and len(args) > 0:
|
|
116
|
+
# Get column names from single source of truth
|
|
117
|
+
x_col = get_csv_column_name('plot_x', ax_row, ax_col, trace_index=trace_index)
|
|
118
|
+
y_col = get_csv_column_name('plot_y', ax_row, ax_col, trace_index=trace_index)
|
|
119
|
+
|
|
63
120
|
# Handle single argument: plot(y) or plot(data_2d)
|
|
64
121
|
if len(args) == 1:
|
|
65
122
|
args_value = args[0]
|
|
@@ -72,14 +129,14 @@ def _format_plot(
|
|
|
72
129
|
# 2D array: extract x and y columns
|
|
73
130
|
if hasattr(args_value, 'ndim') and args_value.ndim == 2:
|
|
74
131
|
x, y = args_value[:, 0], args_value[:, 1]
|
|
75
|
-
df = pd.DataFrame({
|
|
132
|
+
df = pd.DataFrame({x_col: x, y_col: y})
|
|
76
133
|
return df
|
|
77
134
|
|
|
78
135
|
# 1D array: generate x from indices (common case: plot(y))
|
|
79
136
|
elif hasattr(args_value, 'ndim') and args_value.ndim == 1:
|
|
80
137
|
x = np.arange(len(args_value))
|
|
81
138
|
y = args_value
|
|
82
|
-
df = pd.DataFrame({
|
|
139
|
+
df = pd.DataFrame({x_col: x, y_col: y})
|
|
83
140
|
return df
|
|
84
141
|
|
|
85
142
|
# Handle two arguments: plot(x, y)
|
|
@@ -94,22 +151,20 @@ def _format_plot(
|
|
|
94
151
|
if hasattr(y, 'ndim') and y.ndim == 2:
|
|
95
152
|
out = OrderedDict()
|
|
96
153
|
for ii in range(y.shape[1]):
|
|
97
|
-
|
|
98
|
-
|
|
154
|
+
x_col_i = get_csv_column_name(f'plot_x{ii:02d}', ax_row, ax_col, trace_index=trace_index)
|
|
155
|
+
y_col_i = get_csv_column_name(f'plot_y{ii:02d}', ax_row, ax_col, trace_index=trace_index)
|
|
156
|
+
out[x_col_i] = x
|
|
157
|
+
out[y_col_i] = y[:, ii]
|
|
99
158
|
df = pd.DataFrame(out)
|
|
100
159
|
return df
|
|
101
160
|
|
|
102
161
|
# Handle DataFrame y
|
|
103
162
|
if isinstance(y_arg, pd.DataFrame):
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
for ii, col in enumerate(y_arg.columns)
|
|
110
|
-
},
|
|
111
|
-
}
|
|
112
|
-
)
|
|
163
|
+
result = {x_col: x}
|
|
164
|
+
for ii, col in enumerate(y_arg.columns):
|
|
165
|
+
y_col_i = get_csv_column_name(f'plot_y{ii:02d}', ax_row, ax_col, trace_index=trace_index)
|
|
166
|
+
result[y_col_i] = np.array(y_arg[col])
|
|
167
|
+
df = pd.DataFrame(result)
|
|
113
168
|
return df
|
|
114
169
|
|
|
115
170
|
# Handle 1D arrays (most common case: plot(x, y))
|
|
@@ -117,11 +172,11 @@ def _format_plot(
|
|
|
117
172
|
# Flatten x if needed
|
|
118
173
|
x_flat = np.ravel(x)
|
|
119
174
|
y_flat = np.ravel(y)
|
|
120
|
-
df = pd.DataFrame({
|
|
175
|
+
df = pd.DataFrame({x_col: x_flat, y_col: y_flat})
|
|
121
176
|
return df
|
|
122
177
|
|
|
123
178
|
# Fallback for list-like y
|
|
124
|
-
df = pd.DataFrame({
|
|
179
|
+
df = pd.DataFrame({x_col: np.ravel(x), y_col: np.ravel(y)})
|
|
125
180
|
return df
|
|
126
181
|
|
|
127
182
|
# Default empty DataFrame if we can't process the input
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<!-- ---
|
|
2
|
+
!-- Timestamp: 2025-12-08 16:05:55
|
|
3
|
+
!-- Author: ywatanabe
|
|
4
|
+
!-- File: /home/ywatanabe/proj/scitex-code/src/scitex/plt/docs/FIGURE_ARCHITECTURE.md
|
|
5
|
+
!-- --- -->
|
|
6
|
+
|
|
7
|
+
# Figure Architecture for scitex.plt
|
|
8
|
+
|
|
9
|
+
## Terminology
|
|
10
|
+
|
|
11
|
+
| Term | Meaning | In Code |
|
|
12
|
+
|------------|------------------------------|--------------------------------|
|
|
13
|
+
| **Figure** | A matplotlib figure object | `fig, ax = stx.plt.subplots()` |
|
|
14
|
+
| **Axes** | A single subplot/axes | `ax.plot(x, y)` |
|
|
15
|
+
| **Panel** | Used in `scitex.vis` context | See CANVAS_ARCHITECTURE.md |
|
|
16
|
+
|
|
17
|
+
## Output Format
|
|
18
|
+
|
|
19
|
+
`stx.plt` outputs **3 files per figure**:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
output_dir/
|
|
23
|
+
├── 01_plot.png # Rendered image
|
|
24
|
+
├── 01_plot.json # Metadata (dimensions, axes, traces, styles)
|
|
25
|
+
└── 01_plot.csv # Raw data (columns referenced in JSON)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Save Patterns
|
|
29
|
+
|
|
30
|
+
**Flat (default):**
|
|
31
|
+
```python
|
|
32
|
+
fig.savefig("./01_plot.png")
|
|
33
|
+
# Creates: 01_plot.png, 01_plot.json, 01_plot.csv
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Organized by extension:**
|
|
37
|
+
```python
|
|
38
|
+
fig.savefig("./png/01_plot.png")
|
|
39
|
+
# Creates: png/01_plot.png, json/01_plot.json, csv/01_plot.csv
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## JSON Schema (panel.json)
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"metadata_version": "1.1.0",
|
|
47
|
+
"scitex": {
|
|
48
|
+
"version": "2.4.3",
|
|
49
|
+
"created_at": "2025-12-08T15:40:58.453762",
|
|
50
|
+
"created_with": "scitex.plt.subplots (mm-control)",
|
|
51
|
+
"mode": "publication",
|
|
52
|
+
"axes_size_mm": [40, 28],
|
|
53
|
+
"position_in_grid": [0, 0],
|
|
54
|
+
"style_mm": {
|
|
55
|
+
"axis_thickness_mm": 0.2,
|
|
56
|
+
"tick_length_mm": 0.8,
|
|
57
|
+
"tick_thickness_mm": 0.2,
|
|
58
|
+
"trace_thickness_mm": 0.2,
|
|
59
|
+
"marker_size_mm": 0.8,
|
|
60
|
+
"axis_font_size_pt": 7,
|
|
61
|
+
"tick_font_size_pt": 7,
|
|
62
|
+
"title_font_size_pt": 8,
|
|
63
|
+
"legend_font_size_pt": 6,
|
|
64
|
+
"font_family": "Arial",
|
|
65
|
+
"n_ticks": 4
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"matplotlib": {
|
|
69
|
+
"version": "3.10.3"
|
|
70
|
+
},
|
|
71
|
+
"id": "01_plot",
|
|
72
|
+
"dimensions": {
|
|
73
|
+
"figure_size_mm": [80.0, 68.0],
|
|
74
|
+
"figure_size_inch": [3.15, 2.68],
|
|
75
|
+
"figure_size_px": [944, 803],
|
|
76
|
+
"axes_size_mm": [40.0, 28.0],
|
|
77
|
+
"axes_size_inch": [1.57, 1.10],
|
|
78
|
+
"axes_size_px": [472, 330],
|
|
79
|
+
"axes_position": [0.25, 0.29, 0.5, 0.41],
|
|
80
|
+
"dpi": 300
|
|
81
|
+
},
|
|
82
|
+
"margins_mm": {
|
|
83
|
+
"left": 20.0,
|
|
84
|
+
"bottom": 20.0,
|
|
85
|
+
"right": 20.0,
|
|
86
|
+
"top": 20.0
|
|
87
|
+
},
|
|
88
|
+
"axes_bbox_px": {
|
|
89
|
+
"x0": 236,
|
|
90
|
+
"y0": 236,
|
|
91
|
+
"x1": 708,
|
|
92
|
+
"y1": 566,
|
|
93
|
+
"width": 472,
|
|
94
|
+
"height": 330
|
|
95
|
+
},
|
|
96
|
+
"axes_bbox_mm": {
|
|
97
|
+
"x0": 20.0,
|
|
98
|
+
"y0": 20.0,
|
|
99
|
+
"x1": 60.0,
|
|
100
|
+
"y1": 48.0,
|
|
101
|
+
"width": 40.0,
|
|
102
|
+
"height": 28.0
|
|
103
|
+
},
|
|
104
|
+
"axes": {
|
|
105
|
+
"x": {
|
|
106
|
+
"label": "Time",
|
|
107
|
+
"unit": "s",
|
|
108
|
+
"scale": "linear",
|
|
109
|
+
"lim": [-0.31, 6.60],
|
|
110
|
+
"n_ticks": 4
|
|
111
|
+
},
|
|
112
|
+
"y": {
|
|
113
|
+
"label": "Amplitude",
|
|
114
|
+
"unit": "a.u.",
|
|
115
|
+
"scale": "linear",
|
|
116
|
+
"lim": [-1.10, 1.10],
|
|
117
|
+
"n_ticks": 4
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"title": "ax.plot(x, y)",
|
|
121
|
+
"plot_type": "line",
|
|
122
|
+
"method": "plot",
|
|
123
|
+
"traces": [
|
|
124
|
+
{
|
|
125
|
+
"id": "sine",
|
|
126
|
+
"label": "sin(x)",
|
|
127
|
+
"color": "#0000ff",
|
|
128
|
+
"linestyle": "-",
|
|
129
|
+
"linewidth": 0.57,
|
|
130
|
+
"csv_columns": {
|
|
131
|
+
"x": "ax_00_sine_plot_x",
|
|
132
|
+
"y": "ax_00_sine_plot_y"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": "cosine",
|
|
137
|
+
"label": "cos(x)",
|
|
138
|
+
"color": "#ff0000",
|
|
139
|
+
"linestyle": "--",
|
|
140
|
+
"linewidth": 0.57,
|
|
141
|
+
"csv_columns": {
|
|
142
|
+
"x": "ax_00_cosine_plot_x",
|
|
143
|
+
"y": "ax_00_cosine_plot_y"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"legend": {
|
|
148
|
+
"visible": true,
|
|
149
|
+
"loc": 0,
|
|
150
|
+
"frameon": false,
|
|
151
|
+
"labels": ["sin(x)", "cos(x)"]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## CSV Format
|
|
157
|
+
|
|
158
|
+
Column naming convention: `ax_{ax_idx}_{trace_id}_{method}_{dim}`
|
|
159
|
+
|
|
160
|
+
```csv
|
|
161
|
+
ax_00_sine_plot_x,ax_00_sine_plot_y,ax_00_cosine_plot_x,ax_00_cosine_plot_y
|
|
162
|
+
0.0,0.0,0.0,1.0
|
|
163
|
+
0.063,0.063,0.063,0.998
|
|
164
|
+
0.127,0.127,0.127,0.992
|
|
165
|
+
...
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Key Features
|
|
169
|
+
|
|
170
|
+
### Publication Mode
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
fig, ax = stx.plt.subplots(
|
|
174
|
+
fig_mm={"width": 80, "height": 68},
|
|
175
|
+
axes_mm={"width": 40, "height": 28},
|
|
176
|
+
mode="publication"
|
|
177
|
+
)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- All dimensions in **millimeters** for publication standards
|
|
181
|
+
- Consistent styling across figures
|
|
182
|
+
- Automatic metadata embedding
|
|
183
|
+
|
|
184
|
+
### Automatic Export
|
|
185
|
+
|
|
186
|
+
On `fig.savefig()`:
|
|
187
|
+
1. PNG/PDF/SVG rendered
|
|
188
|
+
2. JSON metadata exported
|
|
189
|
+
3. CSV data exported
|
|
190
|
+
|
|
191
|
+
### Trace Tracking
|
|
192
|
+
|
|
193
|
+
All plotting calls are tracked:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
ax.plot(x, y, id="sine", label="sin(x)") # Tracked
|
|
197
|
+
ax.scatter(x, y, id="points") # Tracked
|
|
198
|
+
ax.stx_line(x, y, id="trace") # Tracked
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Supported Plot Methods
|
|
202
|
+
|
|
203
|
+
### Standard Matplotlib
|
|
204
|
+
- `plot`, `scatter`, `bar`, `barh`
|
|
205
|
+
- `hist`, `hist2d`, `hexbin`
|
|
206
|
+
- `boxplot`, `violinplot`
|
|
207
|
+
- `fill_between`, `fill_betweenx`
|
|
208
|
+
- `errorbar`, `contour`, `contourf`
|
|
209
|
+
- `imshow`, `matshow`, `pie`
|
|
210
|
+
- `quiver`, `streamplot`
|
|
211
|
+
- `stem`, `step`, `eventplot`
|
|
212
|
+
|
|
213
|
+
### SciTeX Extensions
|
|
214
|
+
- `stx_line`, `stx_shaded_line`
|
|
215
|
+
- `stx_mean_std`, `stx_mean_ci`, `stx_median_iqr`
|
|
216
|
+
- `stx_kde`, `stx_ecdf`
|
|
217
|
+
- `stx_box`, `stx_violin`
|
|
218
|
+
- `stx_bar`, `stx_barh`
|
|
219
|
+
- `stx_scatter`, `stx_scatter_hist`
|
|
220
|
+
- `stx_heatmap`, `stx_conf_mat`
|
|
221
|
+
- `stx_image`, `stx_imshow`
|
|
222
|
+
- `stx_fillv`, `stx_contour`
|
|
223
|
+
- `stx_raster`, `stx_joyplot`
|
|
224
|
+
- `stx_rectangle`
|
|
225
|
+
|
|
226
|
+
### Seaborn Integration
|
|
227
|
+
- `sns_lineplot`, `sns_scatterplot`
|
|
228
|
+
- `sns_barplot`, `sns_boxplot`, `sns_violinplot`
|
|
229
|
+
- `sns_stripplot`, `sns_swarmplot`
|
|
230
|
+
- `sns_histplot`, `sns_kdeplot`
|
|
231
|
+
- `sns_heatmap`, `sns_jointplot`, `sns_pairplot`
|
|
232
|
+
|
|
233
|
+
## Integration with scitex.vis
|
|
234
|
+
|
|
235
|
+
stx.plt outputs can be used as `scitex` type panels in a canvas:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
canvas/panels/panel_a/
|
|
239
|
+
├── panel.json # Renamed from 01_plot.json
|
|
240
|
+
├── panel.csv # Renamed from 01_plot.csv
|
|
241
|
+
└── panel.png # Renamed from 01_plot.png
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The JSON structure is compatible - canvas.json references the panel data via relative paths with hash verification.
|
|
245
|
+
|
|
246
|
+
## Summary
|
|
247
|
+
|
|
248
|
+
| Aspect | Description |
|
|
249
|
+
|-------------|------------------------------------|
|
|
250
|
+
| Output | PNG + JSON + CSV per figure |
|
|
251
|
+
| Units | Millimeters for publication |
|
|
252
|
+
| Tracking | All traces tracked with IDs |
|
|
253
|
+
| Metadata | Dimensions, styles, axes info |
|
|
254
|
+
| Data | CSV with column references in JSON |
|
|
255
|
+
| Integration | Direct use as canvas panels |
|
|
256
|
+
|
|
257
|
+
<!-- EOF -->
|
scitex/plt/utils/__init__.py
CHANGED
|
@@ -22,6 +22,12 @@ from ._figure_from_axes_mm import (
|
|
|
22
22
|
)
|
|
23
23
|
from ._units import inch_to_mm, mm_to_inch, mm_to_pt, pt_to_mm
|
|
24
24
|
from ._collect_figure_metadata import collect_figure_metadata
|
|
25
|
+
from ._csv_column_naming import (
|
|
26
|
+
get_csv_column_name,
|
|
27
|
+
get_csv_column_prefix,
|
|
28
|
+
parse_csv_column_name,
|
|
29
|
+
sanitize_trace_id,
|
|
30
|
+
)
|
|
25
31
|
|
|
26
32
|
__all__ = [
|
|
27
33
|
"HistogramBinManager",
|
|
@@ -47,6 +53,8 @@ __all__ = [
|
|
|
47
53
|
"cross_ref",
|
|
48
54
|
"enhance_scitex_save_with_captions",
|
|
49
55
|
"export_captions",
|
|
56
|
+
"get_csv_column_name",
|
|
57
|
+
"get_csv_column_prefix",
|
|
50
58
|
"get_dimension_info",
|
|
51
59
|
"get_scitex_config",
|
|
52
60
|
"histogram_bin_manager",
|
|
@@ -57,9 +65,11 @@ __all__ = [
|
|
|
57
65
|
"mk_patches",
|
|
58
66
|
"mm_to_inch",
|
|
59
67
|
"mm_to_pt",
|
|
68
|
+
"parse_csv_column_name",
|
|
60
69
|
"print_dimension_info",
|
|
61
70
|
"pt_to_mm",
|
|
62
71
|
"quick_caption",
|
|
72
|
+
"sanitize_trace_id",
|
|
63
73
|
"save_with_caption",
|
|
64
74
|
"view_dimensions",
|
|
65
75
|
]
|
|
@@ -349,14 +349,15 @@ def _extract_traces(ax) -> list:
|
|
|
349
349
|
and csv_columns mapping
|
|
350
350
|
"""
|
|
351
351
|
import matplotlib.colors as mcolors
|
|
352
|
+
from ._csv_column_naming import get_csv_column_name, sanitize_trace_id
|
|
352
353
|
|
|
353
354
|
traces = []
|
|
354
355
|
|
|
355
356
|
# Get axes position for CSV column naming
|
|
356
|
-
|
|
357
|
+
ax_row, ax_col = 0, 0 # Default for single axes
|
|
357
358
|
if hasattr(ax, '_scitex_metadata') and 'position_in_grid' in ax._scitex_metadata:
|
|
358
359
|
pos = ax._scitex_metadata['position_in_grid']
|
|
359
|
-
|
|
360
|
+
ax_row, ax_col = pos[0], pos[1]
|
|
360
361
|
|
|
361
362
|
for i, line in enumerate(ax.lines):
|
|
362
363
|
trace = {}
|
|
@@ -369,14 +370,16 @@ def _extract_traces(ax) -> list:
|
|
|
369
370
|
label = line.get_label()
|
|
370
371
|
|
|
371
372
|
# Determine trace_id for CSV column matching
|
|
373
|
+
# Use index-based ID to match CSV export (single source of truth)
|
|
374
|
+
trace_id_for_csv = None # Will use trace_index in get_csv_column_name
|
|
375
|
+
|
|
376
|
+
# Store display id/label separately
|
|
372
377
|
if scitex_id:
|
|
373
|
-
|
|
378
|
+
trace["id"] = scitex_id
|
|
374
379
|
elif not label.startswith('_'):
|
|
375
|
-
|
|
380
|
+
trace["id"] = label
|
|
376
381
|
else:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
trace["id"] = trace_id
|
|
382
|
+
trace["id"] = f"line_{i}"
|
|
380
383
|
|
|
381
384
|
# Label (for legend) - use label if not internal
|
|
382
385
|
if not label.startswith('_'):
|
|
@@ -404,12 +407,11 @@ def _extract_traces(ax) -> list:
|
|
|
404
407
|
trace["marker"] = marker
|
|
405
408
|
trace["markersize"] = line.get_markersize()
|
|
406
409
|
|
|
407
|
-
# CSV column mapping -
|
|
408
|
-
#
|
|
409
|
-
# The id should match the id= kwarg passed to ax.plot()
|
|
410
|
+
# CSV column mapping - use single source of truth
|
|
411
|
+
# Uses trace_index to match what _export_as_csv generates
|
|
410
412
|
trace["csv_columns"] = {
|
|
411
|
-
"x":
|
|
412
|
-
"y":
|
|
413
|
+
"x": get_csv_column_name("plot_x", ax_row, ax_col, trace_index=i),
|
|
414
|
+
"y": get_csv_column_name("plot_y", ax_row, ax_col, trace_index=i),
|
|
413
415
|
}
|
|
414
416
|
|
|
415
417
|
traces.append(trace)
|