Metadata-Version: 2.4
Name: sdtoolkit
Version: 0.1.0
Summary: Dependency-free standard deviation, variance and relative standard deviation for Python
Author: Ramesh-xyz
License-Expression: MIT
Project-URL: Homepage, https://standarddeviationcalculator.net/
Project-URL: Documentation, https://ramesh-xyz.github.io/statistics-calculator-toolkit/python.html
Project-URL: Source, https://github.com/Ramesh-xyz/statistics-calculator-toolkit
Project-URL: Issues, https://github.com/Ramesh-xyz/statistics-calculator-toolkit/issues
Project-URL: Changelog, https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/CHANGELOG.md
Keywords: standard deviation,variance,coefficient of variation,statistics
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# sdtoolkit for Python

Part of [statistics-calculator-toolkit](https://github.com/Ramesh-xyz/statistics-calculator-toolkit), which ships the same API for
[several languages](https://ramesh-xyz.github.io/statistics-calculator-toolkit/).

`sdtoolkit` is a small, dependency-free Python package for standard
deviation, variance, sum of squares, relative standard deviation (RSD) and
coefficient of variation, with a command-line interface. It uses only the
standard library and supports Python 3.9 and later.

## Problems it solves

- **Picking the wrong formula.** Population and sample versions have
  separate, explicitly named functions, and the CLI prints which divisor it
  used.
- **Silent precision loss.** The shortcut `Σx² − (Σx)²/n` can return zero or
  even a negative variance for data far from zero. sdtoolkit uses a two-pass
  algorithm with `math.fsum`.
- **Garbage in, number out.** `None`, `NaN`, infinities, booleans and strings
  raise a clear error instead of producing a plausible-looking result. Missing
  values are never skipped silently.
- **Undefined RSD.** A zero mean raises an error instead of returning
  infinity or dividing by zero.

## Features

| function | returns |
|---|---|
| `mean(data)` | arithmetic mean |
| `sum_of_squares(data)` | SS = Σ(x − x̄)² |
| `population_variance(data)` | SS / N |
| `sample_variance(data)` | SS / (n − 1) |
| `population_stdev(data)` | √(SS / N) |
| `sample_stdev(data)` | √(SS / (n − 1)) |
| `relative_std_dev(data, population=False)` | 100 × SD / \|mean\|, in percent |
| `coefficient_of_variation(data, population=False)` | SD / \|mean\|, as a ratio |
| `describe(data, population=False)` | `Summary` dataclass with all of the above plus min, max, range |

All errors are raised as `sdtoolkit.StatisticsError`, a subclass of
`ValueError`.

## Install

```
pip install sdtoolkit
```

Or from a clone, without installing:

```
PYTHONPATH=src python -m sdtoolkit 1 2 3
```

## Usage

### Python

```python
>>> import sdtoolkit as sd
>>> data = [2, 4, 4, 4, 5, 5, 7, 9]
>>> sd.mean(data)
5.0
>>> sd.sum_of_squares(data)
32.0
>>> sd.population_variance(data), sd.population_stdev(data)
(4.0, 2.0)
>>> sd.sample_variance(data), sd.sample_stdev(data)
(4.571428571428571, 2.138089935299395)
>>> sd.relative_std_dev(data)
42.7617987059879
>>> sd.coefficient_of_variation(data, population=True)
0.4
>>> sd.describe(data)
Summary(n=8, mean=5.0, minimum=2.0, maximum=9.0, range=7.0, sum_of_squares=32.0, variance=4.571428571428571, stdev=2.138089935299395, rsd_percent=42.7617987059879, population=False)
>>> sd.describe(data, population=True).as_dict()["stdev"]
2.0
```

### Command line

```console
$ python -m sdtoolkit 2 4 4 4 5 5 7 9
n                8
mean             5
min              2
max              9
range            7
sum of squares   32
sample variance  4.571428571
sample std dev   2.138089935
RSD (%)          42.76179871
(divisor: n - 1)
```

```console
$ python -m sdtoolkit --population 2 4 4 4 5 5 7 9
n                    8
mean                 5
min                  2
max                  9
range                7
sum of squares       32
population variance  4
population std dev   2
RSD (%)              40
(divisor: N)
```

Read one column of a CSV file (lines starting with `#` are treated as
comments):

```console
$ python -m sdtoolkit --csv examples/sample-datasets.csv --column lab_replicate_mg_l
n                10
mean             12.405
min              12.36
max              12.45
range            0.09
sum of squares   0.00825
sample variance  0.0009166666667
sample std dev   0.03027650354
RSD (%)          0.244066937
(divisor: n - 1)
```

Errors go to stderr with exit status 1:

```console
$ python -m sdtoolkit 1 2 x
error: argument 3: 'x' is not a number
$ python -m sdtoolkit 5
error: sample statistics need at least 2 data points, got 1
```

After `pip install .` the same CLI is also available as `sdtoolkit`.

## Population vs sample

Use the population functions when your data are the entire group you are
describing. Use the sample functions when your data are drawn from a larger
group you want to draw conclusions about. The sample formulas divide by
n − 1 (Bessel's correction), which makes the sample variance an unbiased
estimate of the population variance, and they need at least two values.

`describe`, `relative_std_dev`, `coefficient_of_variation` and the CLI
default to the sample formulas. Details:
[Sample vs population](https://ramesh-xyz.github.io/statistics-calculator-toolkit/sample-vs-population.html).

## Formulas

```
x̄   = Σxᵢ / n
SS   = Σ (xᵢ − x̄)²
σ²   = SS / N              σ = √σ²
s²   = SS / (n − 1)        s = √s²
CV   = s / |x̄|
RSD  = 100 × s / |x̄|  %
```

A step-by-step walk-through of these formulas is on the
[standard deviation formula reference](https://standarddeviationcalculator.net/standard-deviation-formula/).

Further reading:

- [Standard deviation](https://ramesh-xyz.github.io/statistics-calculator-toolkit/standard-deviation.html)
- [Variance](https://ramesh-xyz.github.io/statistics-calculator-toolkit/variance.html)
- [Sample vs population](https://ramesh-xyz.github.io/statistics-calculator-toolkit/sample-vs-population.html)
- [Relative standard deviation](https://ramesh-xyz.github.io/statistics-calculator-toolkit/relative-standard-deviation.html)
- [Outliers](https://ramesh-xyz.github.io/statistics-calculator-toolkit/outliers.html)

## Example datasets

[`examples/sample-datasets.csv`](https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/examples/sample-datasets.csv) has three
small columns: lab replicate readings, exam scores and daily temperatures.
They are **illustrative values made up for teaching, not real research
data.** [`examples/worked-examples.md`](https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/examples/worked-examples.md) works
through each one by hand and checks the result with the code.

## Accuracy and edge cases

- **Algorithm.** Two passes: the mean, then the squared deviations from it,
  each summed with `math.fsum`. A correction term removes the rounding error
  left in the mean. The test suite compares results with Python's
  `statistics.pvariance`, `variance`, `pstdev` and `stdev` (which use exact
  rational arithmetic) to a relative tolerance of 1e-12, including data
  offset by 10⁹ and 10¹².
- **Accepted input.** Any iterable of `int`, `float`, `decimal.Decimal` or
  `fractions.Fraction`. Decimals and fractions are converted to `float`, so
  their extra precision is not kept and results are always `float`.
- **Rejected input.** `bool`, `None`, strings, NaN, ±infinity, complex
  numbers, and integers too large for a float raise `StatisticsError` naming
  the offending item.
- **Missing values are not skipped.** `None` raises, and an empty CSV cell
  raises with its row number. Remove or impute missing data yourself so the
  choice is visible in your code.
- **Empty input** raises. **Sample statistics with one value** raise.
  Population variance of one value is 0.
- **Zero mean.** `relative_std_dev` and `coefficient_of_variation` raise;
  `describe` sets `rsd_percent` to `None`. RSD is only meaningful for
  ratio-scale data; see
  [Relative standard deviation](https://ramesh-xyz.github.io/statistics-calculator-toolkit/relative-standard-deviation.html).

```python
>>> sd.mean([1, None, 3])
Traceback (most recent call last):
  ...
sdtoolkit.core.StatisticsError: item 1: None (missing value); remove or fill it before computing
>>> sd.sample_stdev([1e9 + 4, 1e9 + 7, 1e9 + 13, 1e9 + 16]) ** 2
30.0
```

## Tests

```
PYTHONPATH=src python -m unittest discover -s tests
```

The suite uses only `unittest`. GitHub Actions runs it on Python 3.9 to 3.13
([workflow](https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/.github/workflows/test-python.yml)).

## Contributing

See [CONTRIBUTING.md](https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/CONTRIBUTING.md).

## License

MIT. See [LICENSE](https://github.com/Ramesh-xyz/statistics-calculator-toolkit/blob/main/LICENSE).
