scistack 0.1.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.
@@ -0,0 +1,406 @@
1
+ Metadata-Version: 2.4
2
+ Name: scistack
3
+ Version: 0.1.0
4
+ Summary: Scientific data pipeline framework — versioning, lineage, and caching for research workflows
5
+ License: MIT
6
+ Keywords: data-management,lineage,pipeline,provenance,reproducibility,scientific-computing
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Database
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.9
18
+ Requires-Dist: scidb>=0.1.0
19
+ Provides-Extra: all
20
+ Requires-Dist: sci-matlab>=0.1.0; extra == 'all'
21
+ Requires-Dist: scidb-net>=0.1.0; extra == 'all'
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
24
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
25
+ Provides-Extra: matlab
26
+ Requires-Dist: sci-matlab>=0.1.0; extra == 'matlab'
27
+ Provides-Extra: net
28
+ Requires-Dist: scidb-net>=0.1.0; extra == 'net'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # SciDB
32
+
33
+ ## Better Research Tools, Better Research Outcomes
34
+
35
+ SciDB is a database framework purpose-built for scientific data analysis. It gives you a structured, versioned, and queryable home for every piece of data your pipeline produces — from raw signals to final results — with near zero infrastructure code on your part, and **zero changes to your analysis code**.
36
+
37
+ It works natively in both **Python** and **MATLAB**.
38
+
39
+ ## The Problem
40
+
41
+ Every scientist who writes analysis code eventually builds the same thing: a tangle of folders, naming conventions, and bookkeeping scripts to track which data came from where, which version of a function produced it, and whether it's already been computed.
42
+
43
+ That infrastructure code is never the point. But it eats weeks of your time, it's fragile, and it's different in every lab.
44
+
45
+ **Scientists want to focus on the science, not data management.**
46
+
47
+ SciDB replaces all of it with three ideas:
48
+
49
+ - **Named variable types** — instead of files on disk, your data lives in typed database tables you can query by metadata
50
+ - **Automatic lineage** — a simple decorator records exactly what function and inputs produced each result
51
+ - **Computation caching** — if you've already computed something, SciDB knows and skips it
52
+
53
+ With SciDB, your analysis scripts contain _only_ analysis logic. The infrastructure is handled for you.
54
+
55
+ ## Quick Start
56
+
57
+ ### Installation
58
+
59
+ ```bash
60
+ pip install scidb
61
+ ```
62
+
63
+ This pulls in all core dependencies (`sciduckdb`, `thunk`, `scipathgen`, `canonicalhash`, `scirun`).
64
+
65
+ For development (editable installs of all packages):
66
+
67
+ ```bash
68
+ git clone https://github.com/mtillman14/general-sqlite-database
69
+ cd general-sqlite-database
70
+ ./dev-install.sh
71
+ ```
72
+
73
+ ### One-Time Setup
74
+
75
+ Every project starts by configuring a database. You do this once.
76
+
77
+ ```python
78
+ from scidb import configure_database
79
+
80
+ db = configure_database(
81
+ "my_experiment_data.duckdb", # DuckDB file for data + lineage
82
+ dataset_schema_keys=["subject", "session"], # How your dataset is organized
83
+ )
84
+ ```
85
+
86
+ `dataset_schema_keys` describes the structure of your experiment. If your data is organized by subject and session, say so — SciDB uses this to let you save and query data naturally.
87
+
88
+ ### Define Your Variable Types
89
+
90
+ Each kind of data in your pipeline gets its own type. This is just a one-liner:
91
+
92
+ ```python
93
+ from scidb import BaseVariable
94
+
95
+ class RawEMG(BaseVariable):
96
+ pass
97
+
98
+ class FilteredEMG(BaseVariable):
99
+ pass
100
+
101
+ class MaxActivation(BaseVariable):
102
+ pass
103
+ ```
104
+
105
+ That's it. No configuration, no serialization code. SciDB handles numpy arrays, scalars, lists, dicts, and DataFrames natively.
106
+
107
+ ### Save and Load Data
108
+
109
+ ```python
110
+ import numpy as np
111
+
112
+ # Save with metadata that matches your schema
113
+ RawEMG.save(np.random.randn(1000), subject=1, session="baseline")
114
+
115
+ # Load it back — by the metadata you care about
116
+ raw = RawEMG.load(subject=1, session="baseline")
117
+ print(raw.data) # your numpy array
118
+ ```
119
+
120
+ ### Track Lineage Automatically
121
+
122
+ Which function created that variable? Were the most recent settings used the last time I ran this?
123
+
124
+ Wrap your analysis functions with `@thunk` and SciDB records which functions produced what **and the input variable values** — automatically:
125
+
126
+ ```python
127
+ from scidb import thunk
128
+
129
+ @thunk
130
+ def bandpass_filter(signal, low_hz, high_hz):
131
+ # your filtering logic
132
+ return filtered_signal
133
+
134
+ @thunk
135
+ def compute_max(signal):
136
+ return float(np.max(np.abs(signal)))
137
+
138
+ # Run the pipeline — lineage is tracked behind the scenes
139
+ raw = RawEMG.load(subject=1, session="baseline")
140
+ filtered = bandpass_filter(raw, low_hz=20, high_hz=450)
141
+ max_val = compute_max(filtered)
142
+
143
+ # Save results
144
+ FilteredEMG.save(filtered, subject=1, session="baseline")
145
+ MaxActivation.save(max_val, subject=1, session="baseline")
146
+
147
+ # Later: "What function produced this, and with what settings?"
148
+ provenance = db.get_provenance(FilteredEMG, subject=1, session="baseline")
149
+ print(provenance["function_name"]) # "bandpass_filter"
150
+ print(provenance["constants"]) # {"low_hz": 20, "high_hz": 450}
151
+ ```
152
+
153
+ Your functions stay clean, no boilerplate required. They receive normal numpy arrays and return normal values. The `@thunk` decorator handles all the bookkeeping at the boundary.
154
+
155
+ If the `@thunk` decorator is still too close to your code for your test, wrap it in a `Thunk()` call later on:
156
+
157
+ ```python
158
+
159
+ from scidb.thunk import Thunk
160
+
161
+ compute_max = Thunk(compute_max)
162
+ ```
163
+
164
+ **Run the same pipeline again and every step is skipped** — SciDB recognizes the same function + same inputs and returns the cached result instantly.
165
+
166
+ ## Scaling Up with `for_each()`
167
+
168
+ Real experiments can have dozens of subjects and conditions, or thousands. SciDB can handle it all, using `for_each()` runs your pipeline over every combination automatically:
169
+
170
+ ```python
171
+ from scidb import for_each
172
+
173
+ # 5 subjects
174
+ for_each(
175
+ bandpass_filter,
176
+ inputs={"signal": RawEMG},
177
+ outputs=[FilteredEMG],
178
+ subject=[1, 2, 3, 4, 5],
179
+ session=["baseline", "post"],
180
+ )
181
+
182
+ # 10,000 subjects
183
+ for_each(
184
+ bandpass_filter,
185
+ inputs={"signal": RawEMG},
186
+ outputs=[FilteredEMG],
187
+ subject=range(1,10000),
188
+ session=["baseline", "post"],
189
+ )
190
+
191
+ # Specify subject list of any size
192
+ subject_list = config["subjects"] # Load from some configuration file
193
+ for_each(
194
+ bandpass_filter,
195
+ inputs={"signal": RawEMG},
196
+ outputs=[FilteredEMG],
197
+ subject=subject_list,
198
+ session=["baseline", "post"],
199
+ )
200
+ ```
201
+
202
+ This loads `RawEMG` for each subject/session combination, runs `bandpass_filter`, and saves the result as `FilteredEMG` — multiple iterations, zero boilerplate. If a subject is missing data, that iteration is skipped gracefully. In the future, logging support is planned to document what ran successfully and what failed, and why.
203
+
204
+ Need one input to stay fixed while others iterate? Use `Fixed`:
205
+
206
+ ```python
207
+ from scidb import Fixed
208
+
209
+ for_each(
210
+ compare_to_baseline,
211
+ inputs={
212
+ "baseline": Fixed(RawEMG, session="baseline"), # always load baseline
213
+ "current": RawEMG, # iterates normally
214
+ },
215
+ outputs=[Delta],
216
+ subject=[1, 2, 3, 4, 5],
217
+ session=["post_1", "post_2", "post_3"],
218
+ )
219
+ ```
220
+
221
+ ## Powerful Querying
222
+
223
+ Because your data lives in a real database (not scattered files), querying is simple and powerful:
224
+
225
+ ```python
226
+ # Load one specific record
227
+ emg = FilteredEMG.load(subject=3, session="post")
228
+
229
+ # Load all sessions for a subject — returns a list
230
+ all_sessions = FilteredEMG.load(subject=3)
231
+ for var in all_sessions:
232
+ print(var.metadata["session"], var.data.shape)
233
+
234
+ # Load everything as a DataFrame for analysis
235
+ import pandas as pd
236
+ df = MaxActivation.load_all(as_df=True)
237
+ # subject session data
238
+ # 1 baseline 0.82
239
+ # 1 post 1.47
240
+ # 2 baseline 0.91
241
+ # ...
242
+ ```
243
+
244
+ No folder traversal. No filename parsing. No `results_v2_final_FINAL.csv`. Just ask for what you want by the metadata that matters.
245
+
246
+ ### Your Data Is Not Locked Away
247
+
248
+ Worried that putting data in a database means you can't see or inspect it? Don't be. SciDB uses [DuckDB](https://duckdb.org/) under the hood, and every variable type gets a human-readable **view** that you can query directly with SQL — in DBeaver, the DuckDB CLI, or any tool that speaks SQL.
249
+
250
+ For example, the `MaxActivation` view looks like this:
251
+
252
+ | subject | session | value |
253
+ | ------- | -------- | ----- |
254
+ | 1 | baseline | 0.82 |
255
+ | 1 | post | 1.47 |
256
+ | 2 | baseline | 0.91 |
257
+ | 2 | post | 1.38 |
258
+ | 3 | baseline | 0.76 |
259
+ | 3 | post | 1.22 |
260
+
261
+ You can query it directly:
262
+
263
+ ```sql
264
+ SELECT subject, session, value
265
+ FROM MaxActivation;
266
+ ```
267
+
268
+ Or use database viewer tools like [DBeaver](https://dbeaver.com) to view the database directly.
269
+
270
+ Your data is always one SQL query or visualization away — no Python or MATLAB required.
271
+
272
+ ## Works in MATLAB Too
273
+
274
+ SciDB isn't Python-only. The entire framework works in MATLAB with a nearly identical API:
275
+
276
+ ```matlab
277
+ % One-time setup
278
+ scidb.configure_database("my_experiment.duckdb", ["subject", "session"], "pipeline.db");
279
+
280
+ % Save and load
281
+ RawEMG().save(randn(1000, 1), subject=1, session="baseline");
282
+ raw = RawEMG().load(subject=1, session="baseline");
283
+
284
+ % Lineage-tracked functions
285
+ filter_fn = scidb.Thunk(@bandpass_filter);
286
+ filtered = filter_fn(raw, 20, 450);
287
+ FilteredEMG().save(filtered, subject=1, session="baseline");
288
+
289
+ % Batch processing
290
+ scidb.for_each(@bandpass_filter, ...
291
+ struct('signal', RawEMG()), ...
292
+ {FilteredEMG()}, ...
293
+ subject=[1 2 3 4 5], ...
294
+ session=["baseline" "post"]);
295
+ ```
296
+
297
+ Data saved from MATLAB can be loaded in Python and vice versa. Lineage chains are continuous across languages. Use whichever language fits the task.
298
+
299
+ ## What a Full Pipeline Looks Like
300
+
301
+ Here's a complete, realistic pipeline from setup to results:
302
+
303
+ ```python
304
+ from scidb import BaseVariable, configure_database, thunk, for_each
305
+
306
+ # --- Setup (once per project) ---
307
+ db = configure_database("gait_study.duckdb", # Where your data is stored
308
+ ["subject", "session", "trial"], # How your dataset is organized
309
+ "pipeline.db" # Where lineage is tracked
310
+ )
311
+
312
+ # --- Define variable types ---
313
+ class RawKinematicData(BaseVariable):
314
+ pass
315
+
316
+ class StepLength(BaseVariable):
317
+ pass
318
+
319
+ class MeanStepLength(BaseVariable):
320
+ pass
321
+
322
+ class RawForce(BaseVariable):
323
+ pass
324
+
325
+ class FilteredForce(BaseVariable):
326
+ pass
327
+
328
+ # --- Define processing functions ---
329
+ @thunk
330
+ def extract_step_length(kinematic_data):
331
+ # your biomechanics logic here
332
+ return step_lengths
333
+
334
+ @thunk
335
+ def compute_mean(values):
336
+ return float(np.mean(values))
337
+
338
+ # --- Run the pipeline ---
339
+ for_each(
340
+ extract_step_length,
341
+ inputs={"kinematic_data": RawKinematicData},
342
+ outputs=[StepLength],
343
+ subject=[1, 2, 3],
344
+ session=["pre", "post"],
345
+ trial=[1, 2, 3, 4, 5],
346
+ )
347
+
348
+ for_each(
349
+ compute_mean,
350
+ inputs={"values": StepLength},
351
+ outputs=[MeanStepLength],
352
+ subject=[1, 2, 3],
353
+ session=["pre", "post"],
354
+ trial=[1, 2, 3, 4, 5],
355
+ )
356
+
357
+ for_each(
358
+ filter_data,
359
+ inputs={"values": RawForce, "smoothing": 0.2},
360
+ outputs=[FilteredForce],
361
+ subject=[1, 2, 3],
362
+ session=["pre", "post"],
363
+ trial=[1, 2, 3, 4, 5],
364
+ )
365
+
366
+ # --- Analyze results ---
367
+ df = MeanStepLength.load_all(as_df=True)
368
+ print(df.groupby("session")["data"].mean())
369
+ ```
370
+
371
+ That's the entire pipeline. No file I/O code. No path management. No version tracking logic. Just the science.
372
+
373
+ Want to change the function logic? SciDB will automatically detect the change, and will re-run that processing step on the next run of the script. Want to change a setting to the function? SciDB will detect that too, and re-run the processing step. Data will be saved to the database, **and the previous data will be preserved**. Understanding the effect of analysis decisions on our results has never been easier.
374
+
375
+ ```python
376
+ for_each(
377
+ filter_data,
378
+ inputs={"values": RawForce, "smoothing": 0.3}, # Changed from smoothing=0.2
379
+ outputs=[FilteredForce],
380
+ subject=[1, 2, 3],
381
+ session=["pre", "post"],
382
+ trial=[1, 2, 3, 4, 5],
383
+ )
384
+
385
+ # Get the FilteredForce created with smoothing=0.2
386
+ filtered_force0_2 = FilteredForce.load(smoothing=0.2) # Returns all subjects, sessions, and trials.
387
+
388
+ # Get the FilteredForce created with smoothing=0.3
389
+ filtered_force0_3 = FilteredForce.load(smoothing=0.3) # Returns all subjects, sessions, and trials.
390
+ ```
391
+
392
+ ## The Bigger Picture: Shareable Pipelines
393
+
394
+ By abstracting away all infrastructure — file paths, storage formats, naming conventions — SciDB decouples your analysis logic from your local environment. Your pipeline code contains _only_ the scientific computation.
395
+
396
+ This opens the door to **truly portable, shareable data processing pipelines.** When a pipeline is just a sequence of typed functions with declared inputs and outputs, it can be shared, reproduced, and built upon by anyone — regardless of how their data is organized on disk.
397
+
398
+ Today, sharing a pipeline means sharing a pile of scripts with hardcoded paths and implicit assumptions. With SciDB, the pipeline _is_ the science, and the infrastructure adapts to wherever it runs.
399
+
400
+ ## Learn More
401
+
402
+ - [Quickstart Guide](docs/quickstart.md) — Get running in 5 minutes
403
+ - [VO2 Max Walkthrough](docs/guide/walkthrough.md) — Full example pipeline with design explanations
404
+ - [Variables Guide](docs/guide/variables.md) — Deep dive into variable types
405
+ - [Lineage Guide](docs/guide/lineage.md) — How provenance tracking works
406
+ - [API Reference](docs/api.md) — Complete API documentation
@@ -0,0 +1,3 @@
1
+ scistack-0.1.0.dist-info/METADATA,sha256=Rq91weifxOo8FK_E9HfgmohrqcPlKEBL4GA9KSkAYYs,13780
2
+ scistack-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
3
+ scistack-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any