psienced
Small in-house helpers for experimental data — the pieces that otherwise get rewritten at the top of every analysis script.
- Robust statistics — MAD, standard error, CV, t-based confidence intervals, one-call summaries.
- Outlier detection — MAD, Tukey/IQR and z-score, with masking that keeps plate positions intact.
- Rescaling — z-score, robust scale, min–max, percent-of-control, log.
- Units — case-sensitive conversion across seven dimensions, plus the
C1V1 = C2V2dilution arithmetic. - Significant figures — rounding and value ± uncertainty reporting.
- Uncertainty propagation — a
Measurementtype that carries error bars through ordinary Python arithmetic.
Everything is NaN-tolerant by default, because missing wells and failed reads are the normal state of bench data rather than an error condition.
Install
pip install -e .
The only runtime dependency is NumPy. For the test suite:
pip install -e ".[test]"
Use
import psienced as ps
readings = [10.1, 9.8, 10.0, 10.3, 45.0, float("nan"), 9.9, 10.2]
ps.outliers_mad(readings) # -> [F, F, F, F, True, F, F, F]
clean = ps.mask_outliers(readings) # spike becomes NaN, positions preserved
ps.summary(clean)["mean"] # 10.05 (the 45.0 never reaches it)
ps.confidence_interval(clean) # (9.85, 10.25)
Outliers
outliers_mad is the default for a reason. The classic mean/SD z-score is
included for compatibility with existing protocols, but on the small n typical
of replicate sets a single extreme value inflates the SD enough to hide itself:
spiked = [10.0, 10.1, 9.9, 10.2, 9.8, 10.05, 45.0]
ps.outliers_mad(spiked)[-1] # True — caught
ps.outliers_zscore(spiked)[-1] # False — masked by its own effect on the SD
One caveat in the other direction: if more than half the values are identical
the MAD is exactly zero and outliers_mad flags nothing at all, even against an
obvious spike. outliers_iqr still catches that case.
Use mask_outliers (NaN in place) when position carries meaning — plate wells,
time points, paired conditions — and drop_outliers when it does not.
outlier_report returns the indices and values for a QC log.
Units
Names are case-sensitive on purpose: mm is millimetres, mM is millimolar,
and folding those together is precisely the mistake this module exists to catch.
Both micro signs (U+00B5 and U+03BC) are accepted, since which one a file
contains depends on the instrument that wrote it.
ps.convert(1.0, "mM", "uM") # 1000.0
ps.convert(37.0, "C", "F") # 98.6 (affine, not scaled)
ps.convert(1.0, "mm", "mM") # ValueError: different physical dimensions
ps.dilution_volume(c1=100.0, c2=5.0, v2=20.0) # 1.0 mL of stock
ps.molarity_to_mass(0.1, molar_mass=58.44) # 5.844 g/L NaCl
Uncertainty
absorbance = ps.Measurement.from_replicates([0.412, 0.408, 0.415])
extinction = ps.Measurement(6220.0, 30.0)
path_length = ps.Measurement(1.0, 0.005)
concentration = absorbance / (extinction * path_length)
print(concentration.format(unit="M")) # '0.0000662 +/- 0.0000006 M'
Rendering is fixed-point, so results far from unity read better after rescaling. Multiplying by a plain number scales the error bar with the value:
print((concentration * 1e6).format(unit="uM")) # '66.2 +/- 0.6 uM'
Two assumptions are built in, and both matter:
- Independence. Operands are treated as uncorrelated, so
m - mreports a nonzero uncertainty even though the answer is exactly zero. Simplify algebraically before wrapping. - Linearity. The usual first-order approximation, which degrades once the relative uncertainty passes roughly 10%. Reach for Monte Carlo beyond that.
Reporting
ps.format_measurement(10.14159, 0.523, unit="mM") # '10.1 +/- 0.5 mM'
ps.format_sig(1.5, 4) # '1.500' (keeps the zeros)
Layout
| Module | Contents |
|---|---|
psienced.stats |
mad, sem, cv, robust_zscore, confidence_interval, summary |
psienced.outliers |
outliers_mad, outliers_iqr, outliers_zscore, flag_outliers, mask_outliers, drop_outliers, outlier_report |
psienced.normalize |
zscore, robust_scale, minmax, percent_of_control, log_transform |
psienced.units |
convert, known_units, dilution_volume, dilution_factor, molarity_to_mass, mass_to_molarity |
psienced.sigfig |
round_sig, format_sig, round_to_uncertainty, format_measurement |
psienced.propagation |
Measurement, sqrt, log, log10, exp |
Every name is re-exported at the top level, so import psienced as ps is enough.
Conventions
- NaN is ignored, never silently dropped.
summaryreportsnandn_missingseparately, and no detector flags a NaN as an outlier — a missing well is an absence, not an anomaly. - Sample statistics use
ddof=1. A set of replicates is a sample from a process, not a population. Override per call where that is wrong. - Shape is preserved. Transforms and masks return arrays shaped like their input, so results can be written back over the raw data.
- Errors are raised, not guessed at. Mismatched dimensions, impossible dilutions and negative uncertainties fail loudly.
Tests
pytest
The suite covers each module plus doctests, and checks the t-distribution implementation against published critical-value tables.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file psienced-0.1.0.tar.gz.
File metadata
- Download URL: psienced-0.1.0.tar.gz
- Upload date:
- Size: 26.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b48029dca5fcbde5902a39533d30adede1d015d7d15334a0c6f3a60ee1240786
|
|
| MD5 |
b31bbd17c24b842e70481d760093e27e
|
|
| BLAKE2b-256 |
bb0d353337559fb23e72d9507005dcbd508a43d8349cf0d65d0d3a13e00f1d51
|
File details
Details for the file psienced-0.1.0-py3-none-any.whl.
File metadata
- Download URL: psienced-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43923a8b99b3b4949f51466ffc4158b0aeb00e7fda15c1de9865931a246c8074
|
|
| MD5 |
97e170a611e7356f0732f237a8675dea
|
|
| BLAKE2b-256 |
8843e9b4e291bd9e165b0d58fb657655f6d3438d5a1ec989fe6d2dccd0cd20f1
|