SciStack
Spend more time on your science, and less time on the code around it.
SciStack is made for scientists by scientists. It takes the analysis functions you already write in Python or MATLAB and handles everything around them: looping over every subject, session, or trial; saving and loading results; and keeping a record of how each result was produced.
pip install scistack # one install for Python, MATLAB, the CLI, and the GUI backend
SciStack does not do any analysis itself. It helps you run code-based analyses more effectively, whether you use your own code or build on someone else's.
📖 Documentation: Code · With GUI
Who is SciStack for?
SciStack is for researchers who use code to get to their results but don't want to become software engineers. It suits you if:
- Your data is nested:
- participants have sessions, sessions have trials
- players have seasons, seasons have games.
- You run a series of steps on every piece of that data: load, filter, compute a metric, summarize.
- You work in Python, MATLAB, or both.
- You've ever found a file called
results_v2_final_FINAL.csvand weren't sure what made it.
No database or software background is needed. If you can write a function, you can use SciStack.
The problem
Most analysis code starts out like this:
for subject in ["S01", "S02", "S03"]:
for session in ["pre", "post"]:
data = scipy.io.loadmat(f"data/{subject}/{session}/raw.mat")
filtered = lowpass_filter(data["force"], cutoff_hz=10)
peak = np.max(filtered)
np.save(f"results/{subject}_{session}_peak_v2_final.npy", peak)
That works for a while. Then the script grows to hundreds of lines. The analysis gets tangled up with loops, file paths, and naming conventions. Change the cutoff and the old results are overwritten, or you end up with another _v3 file. Six months later, nobody can say which settings produced the figure in the paper. Sharing the code means sharing hard-coded paths that only work on your computer.
What SciStack gives you
The same analysis with SciStack:
def peak_force(force, cutoff_hz):
return np.max(lowpass_filter(force, cutoff_hz))
scidb.for_each(peak_force,
inputs={"force": RawForce, "cutoff_hz": 10},
outputs=[PeakForce],
subject=[], session=[]) # [] means "every subject and session"
Your function only contains the science. SciStack:
- Runs it once for every subject and session, handing it one piece of data at a time.
- Saves each result in a database file, at its subject and session.
- Records how each result was made: which function, which inputs, and which settings.
- Keeps both versions when you change
cutoff_hzto 15, instead of overwriting the first. - Lets you load any result later by asking for it:
PeakForce.load(subject="S01", session="pre").
Three ways to use it
SciStack is a stack of tools. Start with the lightest one that does the job, and move up when you want more.
| Tool | What it does | Good for |
|---|---|---|
scifor |
Replaces nested for loops. Works on tables in memory, with no database. |
Quick analyses, or adding structure to an existing script |
scidb |
Everything in scifor, plus a database that saves every result along with how it was made. |
Real projects you'll come back to, change, and share |
| SciStack GUI | A VS Code extension that draws your scidb pipeline as a diagram you can run, inspect, and plot from. |
Seeing the whole pipeline at once, and working without writing loop code |
All three work from Python and MATLAB. Data saved from one language can be loaded in the other.
Quick start: the GUI
- Install the SciStack Pipeline GUI extension in VS Code.
- Pick a Python environment (
Python: Select Interpreterin the Command Palette) and runpip install scistackin it. - Run
SciStack: Open Pipelinefrom the Command Palette. - Create a new database: give it a name and list your schema keys, the levels your data is organized by, such as
subject, session, trial.
That's it. You'll see a blank canvas, ready for your first function. The With GUI docs walk through the rest.
Quick start: code
Step 1: Replace your loops with scifor
No database and no setup beyond naming your schema keys:
import scifor
scifor.set_schema(["subject", "session"])
results = scifor.for_each(
peak_force,
inputs={"force": force_table, "cutoff_hz": 10},
subject=[1, 2, 3],
session=["pre", "post"],
)
For each of the 6 combinations, scifor takes the matching rows of force_table, passes the force column to peak_force, and collects the answers into one table with subject, session, and result columns.
scifor.set_schema(["subject", "session"]);
results = scifor.for_each(@peak_force, ...
struct("force", force_table, "cutoff_hz", 10), ...
subject=[1, 2, 3], session=["pre", "post"]);
Step 2: Keep your results with scidb
One hundred shoppers each rated three brands of popcorn from 1 to 10. Which brand is best?
import numpy as np
import pandas as pd
import scidb
from scidb import BaseVariable
# A variable is a named kind of data. Declaring one takes one line.
class Rating(BaseVariable):
"""One participant's rating of one brand."""
class BrandSummary(BaseVariable):
"""The mean and SD of one brand's ratings."""
def load_ratings(csv_path):
return pd.read_csv(csv_path)
def summarize(ratings):
r = np.ravel(ratings)
return pd.DataFrame({"mean": [r.mean()], "sd": [r.std(ddof=1)]})
# Create (or open) the database. Each rating lives at a participant and a brand.
scidb.configure_database("popcorn.duckdb", ["participant", "brand"])
# Import the CSV: each row becomes its own Rating record
scidb.for_each(load_ratings, inputs={"csv_path": "popcorn_ratings.csv"}, outputs=[Rating])
# One summary per brand
scidb.for_each(summarize, inputs={"ratings": Rating["rating"]},
outputs=[BrandSummary], brand=[])
Close Python, come back next week, and everything is still there:
db = scidb.configure_database("popcorn.duckdb", ["participant", "brand"])
BrandSummary.load(brand="KettleKing").data # the saved result
db.get_provenance(BrandSummary, brand="KettleKing") # how it was made
Running the script again doesn't create duplicates, so you can re-run after any change without cleaning up first.
The Best Popcorn and Best Player examples go through complete analyses, in both Python and MATLAB.
Big wins, small effort
These features take a line or two of code (or a click) and save the most time:
| You write... | You get... |
|---|---|
subject=[], session=[] |
Your function runs on every combination, with no loops to maintain. Add a subject and it's included on the next run. |
outputs=[MyResult] |
Every result is saved at its subject, session, or trial. No file names, no folders, no overwriting. |
| Nothing extra | Every result records the function, inputs, and settings that produced it, so you can trace any number back to the raw file it came from. |
EachOf(10, 15, 20) |
One run tries every setting. All the results are kept and labelled with their settings, so you can compare them side by side. |
scidb.exclude_schema(subject=3, reason="withdrew") |
Subject 3 is skipped by every step, including ones you write later. Nothing is deleted, the reason is saved, and you can undo it. |
scidb inventory --keys subject,session |
A list of which subject and session combinations have data and which are missing. |
scidb trace PeakForce subject=S01 session=pre |
The full history of one result, from the terminal. |
Your data isn't locked away
Everything (your data and its history) lives in one DuckDB file next to your code. Each variable gets a readable table you can open in DBeaver, the DuckDB command line, or any SQL tool:
SELECT participant, brand, rating FROM Rating;
You don't need Python or MATLAB to look at your results, and you're not tied to SciStack to get them back out. Your functions are plain functions: they take normal arrays and tables and return normal values, so they work with or without SciStack.
The GUI at a glance
The SciStack GUI runs inside VS Code, next to your code:
- See the whole pipeline. Functions and variables are drawn as a diagram, connected by what feeds into what.
- Know what's up to date. Each step is coloured by whether its results are current, so you can see what needs re-running after you edit a function.
- Run from the canvas. Run one step or the whole pipeline, and watch the progress.
- Plot without writing plotting code. Plot Studio turns any variable into a publication-ready figure: group by condition, split into panels, choose colours, and save.
- Compare versions. When a change in settings or code produces several versions of a result, the Variants view lists them side by side.
- Find missing data. The Inventory shows which combinations of your schema keys have no data.
- Share a project. Export the project as a bundle that a colleague can import and re-run.
Learn more
- Installation, including one-time MATLAB setup
- What's a schema?: the one design decision to make before you start
- Create a new project (or with the GUI)
- Example projects: complete analyses, start to finish
- API reference
Contributing
SciStack is in beta and under active development. To work on it locally:
git clone https://github.com/mtillman14/scistack
cd scistack
./dev-install.sh
See the contributing guide for details. Bug reports and feature requests are welcome on the issue tracker.
Metadata
Release files for scistack 0.1.41
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scistack-0.1.41-py3-none-any.whl | Python 3 | none | any | Details |
Release files / scistack-0.1.41-py3-none-any.whl
| Download URL | scistack-0.1.41-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9a628d67cef08cc558114029828cd1d52e71b93b35d24a9f8b6e8e5c02912520
|
|
BLAKE2b-256 checksum How to use checksums |
5b0d4df0159bc837b9010b0cdca8b16bbbd662cc4d97ad80aead99901ba44397
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for scistack 0.1.41
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 1 file in this release has been uploaded with provenance metadata.
Attestation for scistack-0.1.41-py3-none-any.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.