abspc 0.2.1__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.
abspc/README.md ADDED
@@ -0,0 +1,514 @@
1
+ # abspc — Python SPC Charts
2
+
3
+ The `abspc` Python package provides publication-ready Statistical Process
4
+ Control (SPC) charts following the NHS
5
+ [Making Data Count](https://www.england.nhs.uk/publication/making-data-count/)
6
+ methodology.
7
+
8
+ Built on **matplotlib**, it produces high-quality static images suitable for
9
+ board reports, dashboards, and quality-improvement publications.
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install abspc
17
+ ```
18
+
19
+ Or install the development version from source:
20
+
21
+ ```bash
22
+ git clone https://github.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals
23
+ cd biu_Making_Data_Count_CustomVisuals
24
+ pip install -e ".[dev]"
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Quick Start
30
+
31
+ ```python
32
+ import pandas as pd
33
+ from abspc import plot_spc_chart, plot_run_chart
34
+
35
+ # Minimal XmR chart
36
+ data = pd.DataFrame({"value": [48, 52, 49, 55, 47, 51, 53, 50, 48, 54]})
37
+ fig, ax = plot_spc_chart(data, chart_type="XmR")
38
+ fig.savefig("my_xmr_chart.png")
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Chart Types
44
+
45
+ ### XmR Chart
46
+
47
+ The XmR (individuals / moving-range) chart is the most common SPC chart type.
48
+ It is suitable for individual measurements collected over time.
49
+
50
+ ```python
51
+ import numpy as np
52
+ import pandas as pd
53
+ from abspc import plot_spc_chart
54
+
55
+ data = pd.DataFrame({"value": np.random.normal(50, 3, 24)})
56
+
57
+ fig, ax = plot_spc_chart(
58
+ data,
59
+ chart_type="XmR",
60
+ title="XmR Chart – Individual Measurements",
61
+ xlabel="Month",
62
+ ylabel="Value",
63
+ shade_band=True,
64
+ improvement_direction="high",
65
+ )
66
+ ```
67
+
68
+ ![XmR Chart](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_xmr.png)
69
+
70
+ ---
71
+
72
+ ### p Chart
73
+
74
+ The p chart is for proportion data (e.g., percentage of patients waiting
75
+ > 4 hours). It requires a `subgroup_size` column containing the denominator.
76
+
77
+ ```python
78
+ data = pd.DataFrame({
79
+ "value": [0.10, 0.12, 0.08, 0.15, 0.09, 0.11, 0.10, 0.13, 0.07, 0.12,
80
+ 0.09, 0.11, 0.10, 0.08, 0.14, 0.12, 0.09, 0.11, 0.08, 0.10,
81
+ 0.12, 0.09, 0.11, 0.10],
82
+ "subgroup_size": [200] * 24,
83
+ })
84
+
85
+ fig, ax = plot_spc_chart(
86
+ data,
87
+ chart_type="p",
88
+ title="p Chart – Proportion",
89
+ xlabel="Month",
90
+ ylabel="Proportion",
91
+ improvement_direction="low",
92
+ )
93
+ ```
94
+
95
+ ![p Chart](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_p.png)
96
+
97
+ You can also pass a **numerator column** and a **denominator column**:
98
+
99
+ ```python
100
+ data = pd.DataFrame({
101
+ "events": [20, 24, 16, 30, 18],
102
+ "population": [200, 200, 200, 200, 200],
103
+ })
104
+ fig, ax = plot_spc_chart(
105
+ data,
106
+ chart_type="p",
107
+ value_col="population",
108
+ numerator_col="events",
109
+ improvement_direction="low",
110
+ )
111
+ ```
112
+
113
+ ---
114
+
115
+ ### c Chart
116
+
117
+ The c chart is for counts of events in a fixed sample size (e.g., adverse
118
+ events per ward per month).
119
+
120
+ ```python
121
+ data = pd.DataFrame({"value": [3, 5, 2, 6, 4, 3, 7, 5, 4, 6,
122
+ 5, 3, 4, 6, 5, 4, 3, 5, 6, 4,
123
+ 3, 5, 4, 6]})
124
+
125
+ fig, ax = plot_spc_chart(
126
+ data,
127
+ chart_type="c",
128
+ title="c Chart – Count of Events",
129
+ xlabel="Month",
130
+ ylabel="Count",
131
+ improvement_direction="low",
132
+ )
133
+ ```
134
+
135
+ ![c Chart](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_c.png)
136
+
137
+ ---
138
+
139
+ ### Run Chart
140
+
141
+ The run chart plots data against time with a **median** centre line and no
142
+ control limits. It uses run-chart rules to detect signals (8-point shift and
143
+ 6-point trend).
144
+
145
+ ```python
146
+ from abspc import plot_run_chart
147
+
148
+ data = pd.DataFrame({"value": np.random.normal(40, 4, 24)})
149
+
150
+ fig, ax = plot_run_chart(
151
+ data,
152
+ title="Run Chart – Median Centre Line",
153
+ xlabel="Month",
154
+ ylabel="Value",
155
+ improvement_direction="high",
156
+ )
157
+ ```
158
+
159
+ ![Run Chart](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_run.png)
160
+
161
+ `plot_spc_chart` also accepts `chart_type="run"` and will automatically
162
+ delegate to `plot_run_chart`:
163
+
164
+ ```python
165
+ fig, ax = plot_spc_chart(data, chart_type="run")
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Features
171
+
172
+ ### Logo Placement
173
+
174
+ Pass any image (PNG, JPEG, etc.) via `logo_path` to display your
175
+ organisation's logo at the **top-right of the chart, level with the title**.
176
+
177
+ ```python
178
+ fig, ax = plot_spc_chart(
179
+ data,
180
+ chart_type="XmR",
181
+ title="A&E 4-Hour Waits – Aneurin Bevan UHB",
182
+ logo_path="path/to/logo.png",
183
+ logo_zoom=0.08,
184
+ )
185
+ ```
186
+
187
+ ![Chart with Logo](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_with_logo.png)
188
+
189
+ > **Note:** `logo_path` places the logo in the title margin (top-right).
190
+ > The legacy `nhs_logo_path` parameter places an image inside the plot area.
191
+
192
+ ---
193
+
194
+ ### Date Axis
195
+
196
+ All chart functions automatically detect datetime data on the x-axis and
197
+ apply smart date tick formatting.
198
+
199
+ **Option 1 — `DatetimeIndex` (auto-detected):**
200
+
201
+ ```python
202
+ dates = pd.date_range("2022-01-01", periods=30, freq="MS")
203
+ data = pd.DataFrame({"value": np.random.normal(75, 6, 30)}, index=dates)
204
+
205
+ fig, ax = plot_spc_chart(data, chart_type="XmR", title="Monthly Date Axis")
206
+ ```
207
+
208
+ ![Date Axis](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_date_axis.png)
209
+
210
+ **Option 2 — Explicit date column:**
211
+
212
+ ```python
213
+ fig, ax = plot_run_chart(data, x_col="period", date_format="%b %Y")
214
+ ```
215
+
216
+ | `date_format` | Example output |
217
+ |---------------|----------------|
218
+ | `"%b %Y"` | Jan 2024 |
219
+ | `"%Y-%m"` | 2024-01 |
220
+ | `"%d/%m/%Y"` | 01/01/2024 |
221
+ | `None` *(default)* | Auto (ConciseDateFormatter) |
222
+
223
+ ---
224
+
225
+ ### Change-Point Annotations
226
+
227
+ Mark known process changes with vertical lines and labels:
228
+
229
+ ```python
230
+ fig, ax = plot_spc_chart(
231
+ data,
232
+ chart_type="XmR",
233
+ change_points=[
234
+ {"x": 9, "label": "New protocol"},
235
+ {"x": 20, "label": "Staff training"},
236
+ ],
237
+ )
238
+ ```
239
+
240
+ ![Change Points](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_change_points.png)
241
+
242
+ ---
243
+
244
+ ### Auto-Rebase on Sustained Improvement
245
+
246
+ When ≥ 8 consecutive points show sustained improvement, control limits can be
247
+ automatically recalculated for the new phase:
248
+
249
+ ```python
250
+ fig, ax = plot_spc_chart(
251
+ data,
252
+ chart_type="XmR",
253
+ improvement_direction="high",
254
+ auto_rebase=True,
255
+ )
256
+ ```
257
+
258
+ ![Auto-Rebase](https://raw.githubusercontent.com/Aneurin-Bevan-University-Health-Board/biu_Making_Data_Count_CustomVisuals/main/docs/images/chart_auto_rebase.png)
259
+
260
+ Use `rebase_control_limits` for programmatic access without plotting:
261
+
262
+ ```python
263
+ from abspc import rebase_control_limits
264
+
265
+ result = rebase_control_limits(data, chart_type="XmR", improvement_direction="high")
266
+ ```
267
+
268
+ > Auto-rebase is supported for XmR, p, u, and c charts (not run charts).
269
+
270
+ ---
271
+
272
+ ### MDC Variation & Assurance Icons
273
+
274
+ Set `show_icons=True` to display the official Making Data Count variation and
275
+ assurance icons at the top-left of the chart:
276
+
277
+ ```python
278
+ fig, ax = plot_spc_chart(
279
+ data,
280
+ chart_type="XmR",
281
+ improvement_direction="high",
282
+ target=60,
283
+ show_target=True,
284
+ show_icons=True,
285
+ )
286
+ ```
287
+
288
+ **Programmatic access:**
289
+
290
+ ```python
291
+ from abspc import (
292
+ calculate_control_limits,
293
+ detect_special_causes,
294
+ determine_variation_type,
295
+ determine_assurance_type,
296
+ )
297
+
298
+ result = detect_special_causes(calculate_control_limits(data, chart_type="XmR"))
299
+ variation = determine_variation_type(result, value_col="value", improvement_direction="high")
300
+ assurance = determine_assurance_type(result, target=60, improvement_direction="high")
301
+ ```
302
+
303
+ > For run charts only the variation icon is shown (no control limits means
304
+ > assurance cannot be calculated).
305
+
306
+ ---
307
+
308
+ ### MDC Summary Table
309
+
310
+ `plot_mdc_summary_table` renders an NHS MDC-style summary table showing
311
+ multiple measures at a glance:
312
+
313
+ ```python
314
+ from abspc import plot_mdc_summary_table
315
+
316
+ fig, ax = plot_mdc_summary_table(
317
+ [
318
+ {
319
+ "data": df,
320
+ "chart_type": "XmR",
321
+ "measure": "A&E 4-Hour Waits",
322
+ "description": "% patients seen within 4 hours",
323
+ "value_col": "value",
324
+ "improvement_direction": "high",
325
+ "target": 95,
326
+ },
327
+ {
328
+ "data": df_infections,
329
+ "chart_type": "p",
330
+ "measure": "Infection Rate",
331
+ "description": "Proportion of infections per month",
332
+ "value_col": "value",
333
+ "improvement_direction": "low",
334
+ "target": 0.05,
335
+ "subgroup_col": "subgroup_size",
336
+ },
337
+ ],
338
+ title="MDC Summary — Board Report",
339
+ )
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Special-Cause Rules
345
+
346
+ Four NHS MDC rules (aligned with NHSRplotthedots):
347
+
348
+ | Rule | Name | Description |
349
+ |------|------|-------------|
350
+ | **1** | Astronomical point | Single value outside 3σ limits |
351
+ | **2** | Shift | ≥ 8 consecutive points above or below the mean |
352
+ | **3** | Trend | ≥ 6 consecutive points all rising or all falling |
353
+ | **4** | Two-in-three | 2 of 3 consecutive points in the warning zone |
354
+
355
+ Use the detection functions directly:
356
+
357
+ ```python
358
+ from abspc import calculate_control_limits, detect_special_causes
359
+
360
+ result = calculate_control_limits(data, chart_type="XmR")
361
+ flags = detect_special_causes(result)
362
+ print(flags[["value", "mean", "ucl", "lcl", "rule1", "rule2", "rule3", "rule4", "special_cause"]])
363
+ ```
364
+
365
+ ---
366
+
367
+ ## API Reference
368
+
369
+ ### `plot_spc_chart`
370
+
371
+ ```python
372
+ fig, ax = plot_spc_chart(
373
+ data,
374
+ chart_type, # "XmR" | "p" | "u" | "c" | "run"
375
+ value_col="value",
376
+ subgroup_col="subgroup_size",
377
+ numerator_col=None,
378
+ x_col=None,
379
+ title=None,
380
+ xlabel="Observation",
381
+ ylabel="Value",
382
+ improvement_direction="high",
383
+ target=None,
384
+ show_target=False,
385
+ shade_band=False,
386
+ shade_color="#41B6E6",
387
+ nhs_logo_path=None,
388
+ ax=None,
389
+ figsize=(12, 5),
390
+ show_legend=True,
391
+ change_points=None,
392
+ auto_rebase=False,
393
+ date_format=None,
394
+ logo_path=None,
395
+ logo_zoom=0.07,
396
+ show_icons=False,
397
+ icon_zoom=0.06,
398
+ )
399
+ ```
400
+
401
+ | Parameter | Type | Default | Description |
402
+ |-----------|------|---------|-------------|
403
+ | `data` | `pd.DataFrame` | *(required)* | Input DataFrame with at least the `value_col` column. |
404
+ | `chart_type` | `str` | *(required)* | `"XmR"`, `"p"`, `"u"`, `"c"`, or `"run"` (case-insensitive). |
405
+ | `value_col` | `str` | `"value"` | Column containing the measured values. |
406
+ | `subgroup_col` | `str \| None` | `"subgroup_size"` | Column with subgroup sizes. Required for `"p"` and `"u"`. |
407
+ | `numerator_col` | `str \| None` | `None` | For `"p"` charts: column with event counts when `value_col` holds the denominator. |
408
+ | `x_col` | `str \| None` | `None` | Column for the x-axis. Auto-detects `DatetimeIndex` if `None`. |
409
+ | `title` | `str \| None` | `None` | Chart title. Auto-generated if omitted. |
410
+ | `xlabel` | `str` | `"Observation"` | X-axis label. |
411
+ | `ylabel` | `str` | `"Value"` | Y-axis label. |
412
+ | `improvement_direction` | `str` | `"high"` | `"high"` or `"low"`. Controls point colouring. |
413
+ | `target` | `float \| None` | `None` | Optional target value for the target line and assurance calculation. |
414
+ | `show_target` | `bool` | `False` | Draw a dashed target line at `target`. |
415
+ | `shade_band` | `bool` | `False` | Fill between UCL and LCL with a translucent band. |
416
+ | `shade_color` | `str` | `"#41B6E6"` | Colour for tolerance-band shading. |
417
+ | `nhs_logo_path` | `str \| None` | `None` | Logo inside the axes (legacy). Use `logo_path` instead. |
418
+ | `ax` | `Axes \| None` | `None` | Existing axes to draw on. Creates a new figure when `None`. |
419
+ | `figsize` | `tuple` | `(12, 5)` | Figure size in inches. Ignored when `ax` is provided. |
420
+ | `show_legend` | `bool` | `True` | Add a colour legend. |
421
+ | `change_points` | `list[dict] \| None` | `None` | Vertical annotation lines. Each dict needs `"x"` and `"label"`. |
422
+ | `auto_rebase` | `bool` | `False` | Auto-detect sustained improvement and recalculate limits. |
423
+ | `date_format` | `str \| None` | `None` | `strftime`-style format for datetime x-axis. |
424
+ | `logo_path` | `str \| None` | `None` | Logo image at top-right of figure. |
425
+ | `logo_zoom` | `float` | `0.07` | Logo height as fraction of figure height. |
426
+ | `show_icons` | `bool` | `False` | Display MDC variation & assurance icons. |
427
+ | `icon_zoom` | `float` | `0.06` | Icon height as fraction of figure height. |
428
+
429
+ **Returns:** `(fig, ax)` — `matplotlib.figure.Figure` and `matplotlib.axes.Axes`.
430
+
431
+ ---
432
+
433
+ ### `plot_run_chart`
434
+
435
+ ```python
436
+ fig, ax = plot_run_chart(
437
+ data,
438
+ value_col="value",
439
+ x_col=None,
440
+ title=None,
441
+ xlabel="Observation",
442
+ ylabel="Value",
443
+ improvement_direction="high",
444
+ target=None,
445
+ show_target=False,
446
+ nhs_logo_path=None,
447
+ ax=None,
448
+ figsize=(12, 5),
449
+ show_legend=True,
450
+ change_points=None,
451
+ date_format=None,
452
+ logo_path=None,
453
+ logo_zoom=0.07,
454
+ show_icons=False,
455
+ icon_zoom=0.06,
456
+ )
457
+ ```
458
+
459
+ Same parameter semantics as `plot_spc_chart` (without `chart_type`,
460
+ `subgroup_col`, `numerator_col`, `shade_band`, `shade_color`, `auto_rebase`).
461
+
462
+ **Returns:** `(fig, ax)`.
463
+
464
+ ---
465
+
466
+ ### `calculate_control_limits`
467
+
468
+ Returns the input DataFrame extended with `mean`, `ucl`, `lcl`, `uwl`, `lwl`
469
+ columns (or just `mean` for run charts).
470
+
471
+ ### `detect_special_causes`
472
+
473
+ Returns the DataFrame extended with boolean columns `rule1`, `rule2`, `rule3`,
474
+ `rule4`, and `special_cause`.
475
+
476
+ ### `detect_run_chart_signals`
477
+
478
+ Returns the DataFrame extended with `run_shift`, `run_trend`, and `run_signal`.
479
+
480
+ ### `rebase_control_limits`
481
+
482
+ Returns the DataFrame with limits recalculated per improvement phase and a
483
+ `rebase_phase` integer column.
484
+
485
+ ### `show_summary`
486
+
487
+ Generates a programmatic summary dictionary for a chart, including variation
488
+ type, assurance status, descriptive statistics, triggered SPC rules, and a
489
+ list of signal points. Pass `show_summary=True` to `plot_spc_chart` or
490
+ `plot_run_chart` to render the summary as an additional figure.
491
+
492
+ ```python
493
+ from abspc import show_summary
494
+
495
+ summary = show_summary(data, chart_type="XmR", improvement_direction="high", target=60)
496
+ print(summary["variation"], summary["assurance"])
497
+ ```
498
+
499
+ ### `plot_mdc_summary_table`
500
+
501
+ Renders an NHS MDC-style summary table for one or more measures (see the
502
+ [MDC Summary Table](#mdc-summary-table) section above).
503
+
504
+ ---
505
+
506
+ ## Running Tests
507
+
508
+ ```bash
509
+ pip install -e ".[dev]"
510
+ pytest
511
+ ```
512
+
513
+ 160 unit tests covering all chart types, SPC rules, run-chart signals,
514
+ auto-rebase, change-point annotations, summary generation, and plotting.
abspc/__init__.py ADDED
@@ -0,0 +1,58 @@
1
+ """
2
+ abspc
3
+ =====
4
+ A Python package for Statistical Process Control (SPC) charts following
5
+ the NHS Making Data Count (MDC) methodology.
6
+
7
+ Supported chart types
8
+ ---------------------
9
+ * ``"XmR"`` – Individuals / moving-range chart
10
+ * ``"p"`` – Proportion chart
11
+ * ``"u"`` – Counts-per-unit chart
12
+ * ``"c"`` – Counts in a fixed population
13
+ * ``"t"`` – Time between rare events (Nelson transformation)
14
+ * ``"g"`` – Opportunities between rare events (geometric distribution)
15
+ * ``"run"`` – Basic run chart (median centre line, no control limits)
16
+
17
+ Quick start
18
+ -----------
19
+ >>> import pandas as pd
20
+ >>> from abspc import plot_spc_chart, plot_run_chart
21
+ >>> data = pd.DataFrame({"value": [10, 12, 11, 15, 9, 13, 11, 14, 10, 12]})
22
+ >>> plot_spc_chart(data, chart_type="XmR")
23
+ >>> plot_run_chart(data)
24
+ """
25
+
26
+ from .spc import (
27
+ calculate_control_limits,
28
+ detect_special_causes,
29
+ detect_run_chart_signals,
30
+ rebase_control_limits,
31
+ determine_point_colours,
32
+ determine_variation_type,
33
+ determine_assurance_type,
34
+ show_summary,
35
+ SPC_MIN_DATA_POINTS,
36
+ )
37
+ from .plot import plot_spc_chart, plot_run_chart, plot_mdc_summary_table
38
+ from .utils import validate_data, add_logo, add_annotation
39
+
40
+ __all__ = [
41
+ "calculate_control_limits",
42
+ "detect_special_causes",
43
+ "detect_run_chart_signals",
44
+ "rebase_control_limits",
45
+ "determine_point_colours",
46
+ "determine_variation_type",
47
+ "determine_assurance_type",
48
+ "show_summary",
49
+ "plot_spc_chart",
50
+ "plot_run_chart",
51
+ "plot_mdc_summary_table",
52
+ "validate_data",
53
+ "add_logo",
54
+ "add_annotation",
55
+ "SPC_MIN_DATA_POINTS",
56
+ ]
57
+
58
+ __version__ = "0.2.1"
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file