scistackplot 0.1.26__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.
- scistackplot-0.1.26/.gitignore +28 -0
- scistackplot-0.1.26/PKG-INFO +212 -0
- scistackplot-0.1.26/README.md +177 -0
- scistackplot-0.1.26/pyproject.toml +71 -0
- scistackplot-0.1.26/src/scistackplot/__init__.py +239 -0
- scistackplot-0.1.26/src/scistackplot/capability.py +656 -0
- scistackplot-0.1.26/src/scistackplot/codegen.py +886 -0
- scistackplot-0.1.26/src/scistackplot/groups.py +110 -0
- scistackplot-0.1.26/src/scistackplot/reduce.py +1475 -0
- scistackplot-0.1.26/src/scistackplot/render/__init__.py +26 -0
- scistackplot-0.1.26/src/scistackplot/render/base.py +262 -0
- scistackplot-0.1.26/src/scistackplot/render/mpl.py +458 -0
- scistackplot-0.1.26/src/scistackplot/render/plotly_.py +537 -0
- scistackplot-0.1.26/src/scistackplot/resolved.py +240 -0
- scistackplot-0.1.26/src/scistackplot/roles.py +413 -0
- scistackplot-0.1.26/src/scistackplot/shape.py +106 -0
- scistackplot-0.1.26/src/scistackplot/sources/__init__.py +7 -0
- scistackplot-0.1.26/src/scistackplot/sources/base.py +256 -0
- scistackplot-0.1.26/src/scistackplot/sources/csv.py +67 -0
- scistackplot-0.1.26/src/scistackplot/sources/frame.py +45 -0
- scistackplot-0.1.26/src/scistackplot/spec.py +698 -0
- scistackplot-0.1.26/src/scistackplot/table.py +328 -0
- scistackplot-0.1.26/src/scistackplot/variants.py +743 -0
- scistackplot-0.1.26/src/scistackplot/xaxis.py +183 -0
- scistackplot-0.1.26/src/scistackplot/ylimits.py +451 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
.DS_Store
|
|
3
|
+
*/*/__pycache__
|
|
4
|
+
others_projects/sciforge
|
|
5
|
+
scistack-gui/extension/node_modules/
|
|
6
|
+
scistack-gui/frontend/node_modules/__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.pyc
|
|
10
|
+
*.pyo
|
|
11
|
+
*.egg-info/
|
|
12
|
+
# Python package build output (rebuilt fresh by CI on every publish; a
|
|
13
|
+
# committed dist/ gets deleted by `rm -rf dist` mid-build and dirties the
|
|
14
|
+
# tree for hatch-vcs). Excludes scistack-gui/extension/dist/, which is an
|
|
15
|
+
# intentionally committed compiled bundle, not a Python build artifact.
|
|
16
|
+
/dist/
|
|
17
|
+
/*/dist/
|
|
18
|
+
# Generated database artifacts (DuckDB data/lineage + write-ahead logs)
|
|
19
|
+
*.duckdb
|
|
20
|
+
*.duckdb.wal
|
|
21
|
+
*.wal
|
|
22
|
+
scistack-gui/frontend/node_modules/
|
|
23
|
+
# mkdocs build output (regenerated by `mkdocs build`)
|
|
24
|
+
/site/
|
|
25
|
+
# Runtime output of code_export_service (pipeline-to-code export) — timestamped
|
|
26
|
+
# per-run files, not source.
|
|
27
|
+
/exports/
|
|
28
|
+
*.log
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: scistackplot
|
|
3
|
+
Version: 0.1.26
|
|
4
|
+
Summary: Spec-driven plotting for long-format scientific data — standalone, GUI-friendly
|
|
5
|
+
Author: SciStack Contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: data-science,matplotlib,plotly,plotting,visualization
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: numpy>=1.21
|
|
20
|
+
Requires-Dist: pandas>=1.5
|
|
21
|
+
Requires-Dist: scistacklog>=0.1.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: matplotlib>=3.6; extra == 'dev'
|
|
24
|
+
Requires-Dist: plotly>=5.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: seaborn>=0.12; extra == 'dev'
|
|
28
|
+
Requires-Dist: tomli-w>=1.0; extra == 'dev'
|
|
29
|
+
Provides-Extra: interactive
|
|
30
|
+
Requires-Dist: plotly>=5.0; extra == 'interactive'
|
|
31
|
+
Provides-Extra: mpl
|
|
32
|
+
Requires-Dist: matplotlib>=3.6; extra == 'mpl'
|
|
33
|
+
Requires-Dist: seaborn>=0.12; extra == 'mpl'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# scistackplot
|
|
37
|
+
|
|
38
|
+
## Build the figure by looking at it, then keep it
|
|
39
|
+
|
|
40
|
+
`scistackplot` turns a long-format table into a figure from a small,
|
|
41
|
+
serializable description — a `PlotSpec`. It works standalone on a CSV or a
|
|
42
|
+
DataFrame with no database and no configuration, and the same `PlotSpec` is
|
|
43
|
+
exactly what the body of a SciDB `plot_` endpoint needs, so an interactive
|
|
44
|
+
exploration can be frozen into a lineage-tracked pipeline step.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install scistackplot
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## The idea
|
|
51
|
+
|
|
52
|
+
A plotting GUI looks like it produces pictures. It doesn't — it produces a
|
|
53
|
+
**specification**, and the picture is a view of it. That is what lets an
|
|
54
|
+
inherently visual tool live inside a reproducible pipeline:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import pandas as pd
|
|
58
|
+
from scistackplot import DataFrameSource, PlotSpec, Role, PlotKind, render
|
|
59
|
+
|
|
60
|
+
source = DataFrameSource(pd.read_csv("gait.csv"))
|
|
61
|
+
spec = PlotSpec(
|
|
62
|
+
measures=["StepLength"],
|
|
63
|
+
roles={"session": Role.X, "limb": Role.COLOR, "subject": Role.FREE},
|
|
64
|
+
kind=PlotKind.BOX,
|
|
65
|
+
)
|
|
66
|
+
figure = render(source, spec)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Every factor does exactly one thing
|
|
70
|
+
|
|
71
|
+
The whole control surface is one rule: each categorical column carries exactly
|
|
72
|
+
one role.
|
|
73
|
+
|
|
74
|
+
| Role | Meaning |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `X` | x-axis position |
|
|
77
|
+
| `COLOR` | one coloured series per level |
|
|
78
|
+
| `FACET` | one subplot per level (arranged by `FacetOptions`) |
|
|
79
|
+
| `ITERATE` | a separate **figure** per level |
|
|
80
|
+
| `AGGREGATE` | collapse — average over this factor |
|
|
81
|
+
| `FREE` | keep as replicate rows |
|
|
82
|
+
|
|
83
|
+
Which plot kinds are available follows from that assignment plus the measure's
|
|
84
|
+
shape, through one pure function:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from scistackplot import available_plots, default_plot, Shape
|
|
88
|
+
|
|
89
|
+
available_plots(Shape.SCALAR, {"session": Role.X}) # scatter, strip
|
|
90
|
+
available_plots(Shape.SCALAR, {"session": Role.X, "trial": Role.FREE}) # + box, violin, bar
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
A distribution needs replicates, and replicates exist only when some factor is
|
|
94
|
+
left `FREE`. That single rule produces both defaults and availability:
|
|
95
|
+
|
|
96
|
+
| Measure shape | no replicates | with replicates |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| scalar | scatter | box / violin / bar + CI |
|
|
99
|
+
| 1-D array | one line per observation | mean line + shaded error band |
|
|
100
|
+
| 2-D | heatmap | mean heatmap |
|
|
101
|
+
|
|
102
|
+
`AGGREGATE` deliberately does *not* count as replicates: it averages its factor
|
|
103
|
+
away before anything is drawn. "Average over trials, then show the spread
|
|
104
|
+
across subjects" is `trial=AGGREGATE, subject=FREE`.
|
|
105
|
+
|
|
106
|
+
## Arranging the subplots
|
|
107
|
+
|
|
108
|
+
Faceted panels flow in order by default, wrapping at `FacetOptions.wrap`. When
|
|
109
|
+
the arrangement matters, describe it with **rules** instead of positions:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from scistackplot import FacetOptions, MatchOp, Matcher, PlotSpec, Role
|
|
113
|
+
|
|
114
|
+
spec = PlotSpec(
|
|
115
|
+
measures=["RawEMG"],
|
|
116
|
+
roles={"ColName": Role.FACET, "subject": Role.COLOR},
|
|
117
|
+
facet=FacetOptions(
|
|
118
|
+
rows=[Matcher(op=MatchOp.STARTS_WITH, value="R"),
|
|
119
|
+
Matcher(op=MatchOp.STARTS_WITH, value="L")],
|
|
120
|
+
cols=[Matcher(op=MatchOp.ENDS_WITH, value="HAM"),
|
|
121
|
+
Matcher(op=MatchOp.ENDS_WITH, value="TA")],
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Rules describe a layout rather than a hand-arrangement, so the same
|
|
127
|
+
`FacetOptions` applies to any variable whose panels are named the same way.
|
|
128
|
+
Ops are `starts_with`, `ends_with`, `contains`, `not_contains`, `equals` and
|
|
129
|
+
`regex`; a panel matching no rule lands in a trailing "other" row or column
|
|
130
|
+
rather than vanishing.
|
|
131
|
+
|
|
132
|
+
Each panel is named on its **y axis**, not by a caption above it. A caption
|
|
133
|
+
spends a strip of every row of the grid on text; the axis title is room the
|
|
134
|
+
panel was already spending, so a 4x3 grid gets that height back for the data.
|
|
135
|
+
The generated seaborn code says the same thing (`g.set_titles("")`), because
|
|
136
|
+
the export must be the figure you previewed.
|
|
137
|
+
|
|
138
|
+
## Ordering is not cosmetic
|
|
139
|
+
|
|
140
|
+
Zero-padded IDs (`"01"`, `"02"`, … `"10"`) sort lexicographically into
|
|
141
|
+
1, 10, 2 under pandas' default — visibly wrong on an axis, and wrong in a way
|
|
142
|
+
that looks like a data problem. `LongTable` carries each factor's real level
|
|
143
|
+
order; sources that know better (SciDB knows its declared `schema_key_types`)
|
|
144
|
+
supply it explicitly, and everything else falls back to a natural sort.
|
|
145
|
+
|
|
146
|
+
## Rendering
|
|
147
|
+
|
|
148
|
+
Two backends translate the same reduced plot, so the interactive view and the
|
|
149
|
+
exported figure cannot disagree:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from scistackplot import resolve, render_matplotlib, render_plotly
|
|
153
|
+
|
|
154
|
+
resolved = resolve(spec, table) # all reduction happens here
|
|
155
|
+
figure = render_matplotlib(resolved[0]) # export / pipeline — a Figure
|
|
156
|
+
payload = render_plotly(resolved[0]) # interactive — a plotly.js dict
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`render_plotly` builds plain JSON and needs no plotly package.
|
|
160
|
+
|
|
161
|
+
## Export: real code, not a call back into this library
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from scistackplot import generate_plot_function
|
|
165
|
+
|
|
166
|
+
print(generate_plot_function(spec, table))
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
def plot_steplength(df, filename):
|
|
171
|
+
import matplotlib.pyplot as plt
|
|
172
|
+
import pandas as pd
|
|
173
|
+
import seaborn as sns
|
|
174
|
+
|
|
175
|
+
g = sns.catplot(
|
|
176
|
+
data=df,
|
|
177
|
+
x='session',
|
|
178
|
+
y='StepLength',
|
|
179
|
+
hue='limb',
|
|
180
|
+
kind="box",
|
|
181
|
+
)
|
|
182
|
+
g.set_axis_labels('session', 'StepLength')
|
|
183
|
+
return g.figure
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Your pipeline gets ordinary seaborn code it can keep, edit, and read — no
|
|
187
|
+
runtime dependency on this package. The spec is embedded in the docstring, so
|
|
188
|
+
the GUI can reopen a figure you have since hand-edited.
|
|
189
|
+
|
|
190
|
+
## Data sources
|
|
191
|
+
|
|
192
|
+
`DataSource` is a three-method protocol (`describe`, `get_table`,
|
|
193
|
+
`joinable_with`). `scistackplot` ships `CsvSource` and `DataFrameSource`;
|
|
194
|
+
[`scistackplotdb`](../scistackplotdb/README.md) ships the SciDB one. Anything
|
|
195
|
+
consuming the protocol — including the Plot Studio panel in the SciStack GUI —
|
|
196
|
+
works identically against a lone CSV and a full project database.
|
|
197
|
+
|
|
198
|
+
## Relationship to SciDB endpoints
|
|
199
|
+
|
|
200
|
+
Recording a figure is SciDB's job and is unchanged: name a function `plot_`,
|
|
201
|
+
return a Figure, and `finalized=True` stores it as a queryable record with an
|
|
202
|
+
embedded provenance stamp. `scistackplot` supplies the body of that function;
|
|
203
|
+
`scistackplotdb` generates the `for_each` call around it.
|
|
204
|
+
|
|
205
|
+
See [`docs/claude/plotting-library-design.md`](../docs/claude/plotting-library-design.md).
|
|
206
|
+
|
|
207
|
+
## Optional extras
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
pip install "scistackplot[mpl]" # matplotlib + seaborn (export)
|
|
211
|
+
pip install "scistackplot[interactive]" # plotly Figure objects
|
|
212
|
+
```
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# scistackplot
|
|
2
|
+
|
|
3
|
+
## Build the figure by looking at it, then keep it
|
|
4
|
+
|
|
5
|
+
`scistackplot` turns a long-format table into a figure from a small,
|
|
6
|
+
serializable description — a `PlotSpec`. It works standalone on a CSV or a
|
|
7
|
+
DataFrame with no database and no configuration, and the same `PlotSpec` is
|
|
8
|
+
exactly what the body of a SciDB `plot_` endpoint needs, so an interactive
|
|
9
|
+
exploration can be frozen into a lineage-tracked pipeline step.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install scistackplot
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## The idea
|
|
16
|
+
|
|
17
|
+
A plotting GUI looks like it produces pictures. It doesn't — it produces a
|
|
18
|
+
**specification**, and the picture is a view of it. That is what lets an
|
|
19
|
+
inherently visual tool live inside a reproducible pipeline:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import pandas as pd
|
|
23
|
+
from scistackplot import DataFrameSource, PlotSpec, Role, PlotKind, render
|
|
24
|
+
|
|
25
|
+
source = DataFrameSource(pd.read_csv("gait.csv"))
|
|
26
|
+
spec = PlotSpec(
|
|
27
|
+
measures=["StepLength"],
|
|
28
|
+
roles={"session": Role.X, "limb": Role.COLOR, "subject": Role.FREE},
|
|
29
|
+
kind=PlotKind.BOX,
|
|
30
|
+
)
|
|
31
|
+
figure = render(source, spec)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Every factor does exactly one thing
|
|
35
|
+
|
|
36
|
+
The whole control surface is one rule: each categorical column carries exactly
|
|
37
|
+
one role.
|
|
38
|
+
|
|
39
|
+
| Role | Meaning |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `X` | x-axis position |
|
|
42
|
+
| `COLOR` | one coloured series per level |
|
|
43
|
+
| `FACET` | one subplot per level (arranged by `FacetOptions`) |
|
|
44
|
+
| `ITERATE` | a separate **figure** per level |
|
|
45
|
+
| `AGGREGATE` | collapse — average over this factor |
|
|
46
|
+
| `FREE` | keep as replicate rows |
|
|
47
|
+
|
|
48
|
+
Which plot kinds are available follows from that assignment plus the measure's
|
|
49
|
+
shape, through one pure function:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from scistackplot import available_plots, default_plot, Shape
|
|
53
|
+
|
|
54
|
+
available_plots(Shape.SCALAR, {"session": Role.X}) # scatter, strip
|
|
55
|
+
available_plots(Shape.SCALAR, {"session": Role.X, "trial": Role.FREE}) # + box, violin, bar
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A distribution needs replicates, and replicates exist only when some factor is
|
|
59
|
+
left `FREE`. That single rule produces both defaults and availability:
|
|
60
|
+
|
|
61
|
+
| Measure shape | no replicates | with replicates |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| scalar | scatter | box / violin / bar + CI |
|
|
64
|
+
| 1-D array | one line per observation | mean line + shaded error band |
|
|
65
|
+
| 2-D | heatmap | mean heatmap |
|
|
66
|
+
|
|
67
|
+
`AGGREGATE` deliberately does *not* count as replicates: it averages its factor
|
|
68
|
+
away before anything is drawn. "Average over trials, then show the spread
|
|
69
|
+
across subjects" is `trial=AGGREGATE, subject=FREE`.
|
|
70
|
+
|
|
71
|
+
## Arranging the subplots
|
|
72
|
+
|
|
73
|
+
Faceted panels flow in order by default, wrapping at `FacetOptions.wrap`. When
|
|
74
|
+
the arrangement matters, describe it with **rules** instead of positions:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from scistackplot import FacetOptions, MatchOp, Matcher, PlotSpec, Role
|
|
78
|
+
|
|
79
|
+
spec = PlotSpec(
|
|
80
|
+
measures=["RawEMG"],
|
|
81
|
+
roles={"ColName": Role.FACET, "subject": Role.COLOR},
|
|
82
|
+
facet=FacetOptions(
|
|
83
|
+
rows=[Matcher(op=MatchOp.STARTS_WITH, value="R"),
|
|
84
|
+
Matcher(op=MatchOp.STARTS_WITH, value="L")],
|
|
85
|
+
cols=[Matcher(op=MatchOp.ENDS_WITH, value="HAM"),
|
|
86
|
+
Matcher(op=MatchOp.ENDS_WITH, value="TA")],
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Rules describe a layout rather than a hand-arrangement, so the same
|
|
92
|
+
`FacetOptions` applies to any variable whose panels are named the same way.
|
|
93
|
+
Ops are `starts_with`, `ends_with`, `contains`, `not_contains`, `equals` and
|
|
94
|
+
`regex`; a panel matching no rule lands in a trailing "other" row or column
|
|
95
|
+
rather than vanishing.
|
|
96
|
+
|
|
97
|
+
Each panel is named on its **y axis**, not by a caption above it. A caption
|
|
98
|
+
spends a strip of every row of the grid on text; the axis title is room the
|
|
99
|
+
panel was already spending, so a 4x3 grid gets that height back for the data.
|
|
100
|
+
The generated seaborn code says the same thing (`g.set_titles("")`), because
|
|
101
|
+
the export must be the figure you previewed.
|
|
102
|
+
|
|
103
|
+
## Ordering is not cosmetic
|
|
104
|
+
|
|
105
|
+
Zero-padded IDs (`"01"`, `"02"`, … `"10"`) sort lexicographically into
|
|
106
|
+
1, 10, 2 under pandas' default — visibly wrong on an axis, and wrong in a way
|
|
107
|
+
that looks like a data problem. `LongTable` carries each factor's real level
|
|
108
|
+
order; sources that know better (SciDB knows its declared `schema_key_types`)
|
|
109
|
+
supply it explicitly, and everything else falls back to a natural sort.
|
|
110
|
+
|
|
111
|
+
## Rendering
|
|
112
|
+
|
|
113
|
+
Two backends translate the same reduced plot, so the interactive view and the
|
|
114
|
+
exported figure cannot disagree:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from scistackplot import resolve, render_matplotlib, render_plotly
|
|
118
|
+
|
|
119
|
+
resolved = resolve(spec, table) # all reduction happens here
|
|
120
|
+
figure = render_matplotlib(resolved[0]) # export / pipeline — a Figure
|
|
121
|
+
payload = render_plotly(resolved[0]) # interactive — a plotly.js dict
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`render_plotly` builds plain JSON and needs no plotly package.
|
|
125
|
+
|
|
126
|
+
## Export: real code, not a call back into this library
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from scistackplot import generate_plot_function
|
|
130
|
+
|
|
131
|
+
print(generate_plot_function(spec, table))
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
def plot_steplength(df, filename):
|
|
136
|
+
import matplotlib.pyplot as plt
|
|
137
|
+
import pandas as pd
|
|
138
|
+
import seaborn as sns
|
|
139
|
+
|
|
140
|
+
g = sns.catplot(
|
|
141
|
+
data=df,
|
|
142
|
+
x='session',
|
|
143
|
+
y='StepLength',
|
|
144
|
+
hue='limb',
|
|
145
|
+
kind="box",
|
|
146
|
+
)
|
|
147
|
+
g.set_axis_labels('session', 'StepLength')
|
|
148
|
+
return g.figure
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Your pipeline gets ordinary seaborn code it can keep, edit, and read — no
|
|
152
|
+
runtime dependency on this package. The spec is embedded in the docstring, so
|
|
153
|
+
the GUI can reopen a figure you have since hand-edited.
|
|
154
|
+
|
|
155
|
+
## Data sources
|
|
156
|
+
|
|
157
|
+
`DataSource` is a three-method protocol (`describe`, `get_table`,
|
|
158
|
+
`joinable_with`). `scistackplot` ships `CsvSource` and `DataFrameSource`;
|
|
159
|
+
[`scistackplotdb`](../scistackplotdb/README.md) ships the SciDB one. Anything
|
|
160
|
+
consuming the protocol — including the Plot Studio panel in the SciStack GUI —
|
|
161
|
+
works identically against a lone CSV and a full project database.
|
|
162
|
+
|
|
163
|
+
## Relationship to SciDB endpoints
|
|
164
|
+
|
|
165
|
+
Recording a figure is SciDB's job and is unchanged: name a function `plot_`,
|
|
166
|
+
return a Figure, and `finalized=True` stores it as a queryable record with an
|
|
167
|
+
embedded provenance stamp. `scistackplot` supplies the body of that function;
|
|
168
|
+
`scistackplotdb` generates the `for_each` call around it.
|
|
169
|
+
|
|
170
|
+
See [`docs/claude/plotting-library-design.md`](../docs/claude/plotting-library-design.md).
|
|
171
|
+
|
|
172
|
+
## Optional extras
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pip install "scistackplot[mpl]" # matplotlib + seaborn (export)
|
|
176
|
+
pip install "scistackplot[interactive]" # plotly Figure objects
|
|
177
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
source = "vcs"
|
|
7
|
+
raw-options = { search_parent_directories = true, local_scheme = "no-local-version" }
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "scistackplot"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
description = "Spec-driven plotting for long-format scientific data — standalone, GUI-friendly"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
license = "MIT"
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "SciStack Contributors" }
|
|
18
|
+
]
|
|
19
|
+
keywords = [
|
|
20
|
+
"plotting",
|
|
21
|
+
"visualization",
|
|
22
|
+
"data-science",
|
|
23
|
+
"matplotlib",
|
|
24
|
+
"plotly",
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 4 - Beta",
|
|
28
|
+
"Intended Audience :: Science/Research",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Operating System :: OS Independent",
|
|
31
|
+
"Programming Language :: Python :: 3",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"pandas>=1.5",
|
|
40
|
+
"numpy>=1.21",
|
|
41
|
+
"scistacklog>=0.1.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
mpl = [
|
|
46
|
+
"matplotlib>=3.6",
|
|
47
|
+
"seaborn>=0.12",
|
|
48
|
+
]
|
|
49
|
+
interactive = [
|
|
50
|
+
"plotly>=5.0",
|
|
51
|
+
]
|
|
52
|
+
dev = [
|
|
53
|
+
"pytest>=7.0",
|
|
54
|
+
"pytest-cov>=4.0",
|
|
55
|
+
"matplotlib>=3.6",
|
|
56
|
+
"seaborn>=0.12",
|
|
57
|
+
"plotly>=5.0",
|
|
58
|
+
"tomli-w>=1.0",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.hatch.build.targets.sdist]
|
|
62
|
+
include = [
|
|
63
|
+
"/src",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[tool.hatch.build.targets.wheel]
|
|
67
|
+
packages = ["src/scistackplot"]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
pythonpath = ["src"]
|