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.
@@ -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"]