tpattern 0.1.12__tar.gz → 0.1.13__tar.gz
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.
- {tpattern-0.1.12/tpattern.egg-info → tpattern-0.1.13}/PKG-INFO +71 -7
- {tpattern-0.1.12 → tpattern-0.1.13}/README.md +70 -6
- {tpattern-0.1.12 → tpattern-0.1.13}/pyproject.toml +1 -1
- tpattern-0.1.13/tests/test_starter.py +34 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/__init__.py +3 -2
- tpattern-0.1.13/tpattern/starter.py +147 -0
- {tpattern-0.1.12 → tpattern-0.1.13/tpattern.egg-info}/PKG-INFO +71 -7
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern.egg-info/SOURCES.txt +2 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/LICENSE +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/setup.cfg +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_calibrated_pins.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_contrast.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_dedup.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_guided.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_null_compare.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_outputs.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_read_table_validation.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_reference.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_report_dendrogram.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_reproduction.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_sofcoder.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_support_reporting.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tests/test_synthetic.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/advisor.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/ci.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/contrast.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/detect.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/diagnostics.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/guided.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/io.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/methods.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/pattern.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/randomise.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/reference.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/report.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/significance.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/synthetic.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern/viz.py +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern.egg-info/dependency_links.txt +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern.egg-info/requires.txt +0 -0
- {tpattern-0.1.12 → tpattern-0.1.13}/tpattern.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tpattern
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Open, auditable temporal & sequential pattern analysis (T-patterns) for sport and behaviour
|
|
5
5
|
Author-email: Andrew Callaway <acallaway@bournemouth.ac.uk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -54,6 +54,60 @@ analysis companion to the [OpenTag.Studio](https://opentag.studio) tagging tool.
|
|
|
54
54
|
Bring your own data (one row per event — see [`SCHEMA.md`](SCHEMA.md)) or try the
|
|
55
55
|
included [`data/`](data/) example.
|
|
56
56
|
|
|
57
|
+
## Getting started in a minute
|
|
58
|
+
|
|
59
|
+
Install, generate a starter script, point it at your data, run it:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install tpattern
|
|
63
|
+
python -c "import tpattern; tpattern.new_analysis()" # writes my_analysis.py
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Open `my_analysis.py`, change the two lines marked `# <-- EDIT` (your file and its time
|
|
67
|
+
unit), and run `python my_analysis.py`. It loads your events, **checks the file and tells
|
|
68
|
+
you in plain language about any problem**, recommends the analysis settings with reasons,
|
|
69
|
+
calibrates every pattern against a chance model, and writes a folder of results with a
|
|
70
|
+
plain-English verdict for each pattern.
|
|
71
|
+
|
|
72
|
+
Doing something more specific? Generate a template for it:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from tpattern import new_analysis
|
|
76
|
+
new_analysis() # one group: detect, calibrate, report
|
|
77
|
+
new_analysis(template="two_groups") # compare two groups (odds ratios + forest plot)
|
|
78
|
+
new_analysis(template="null_choice") # core vs shell: which null a pattern survives
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Not sure what a setting means? Ask the code — every setting and output column is documented:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from tpattern import explain
|
|
85
|
+
print(explain("min_lag")) # what it does, its range, the recommended value, higher vs lower
|
|
86
|
+
print(explain("fdr_q")) # what an output column means
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## What you get
|
|
90
|
+
|
|
91
|
+
- **Forgiving data import.** `read_table` checks your file as it reads it and reports, in
|
|
92
|
+
plain language, any problem, how much of the file it affects, and how to fix it — instead
|
|
93
|
+
of silently dropping rows. Each check has a stable code (Appendix A of the tutorial; also
|
|
94
|
+
[`PARAMETERS.md`](PARAMETERS.md)).
|
|
95
|
+
- **A proper chance model.** Every pattern is tested against a surrogate null, with p-values
|
|
96
|
+
corrected for repeated testing (Benjamini–Hochberg FDR for screening, Holm family-wise for
|
|
97
|
+
confirmatory claims), stratified by hierarchy level.
|
|
98
|
+
- **Temporal reporting.** Beyond *which* patterns are real, the output says *when*: each
|
|
99
|
+
pattern's occurrence timing (median lag and spread) is reported by default — on the
|
|
100
|
+
dendrogram, in the table, and in the plain-English verdict — for every pattern.
|
|
101
|
+
- **Readable output.** Results-table columns are named for what they are (`monte_carlo_p`,
|
|
102
|
+
`fdr_q`, `survives_fwer_holm`, `bout_support`, `duration_median_ms`), and each pattern
|
|
103
|
+
carries a plain-English `interpretation`. `explain()` documents every setting and column;
|
|
104
|
+
the full reference is in [`PARAMETERS.md`](PARAMETERS.md).
|
|
105
|
+
- **One reference, everywhere.** Settings and columns live in one registry that generates the
|
|
106
|
+
code's `explain()`, `PARAMETERS.md`, the Colab form labels, and the tutorial's tables — so
|
|
107
|
+
the software, the paper and the notebook can't drift apart.
|
|
108
|
+
- **Clean figures** — dendrograms, temporal signatures, forest plots — that share one
|
|
109
|
+
customisable style (`tpattern.viz.STYLE`).
|
|
110
|
+
|
|
57
111
|
## Why it exists
|
|
58
112
|
|
|
59
113
|
T-pattern analysis finds behaviourally meaningful structure that event counts and
|
|
@@ -62,7 +116,7 @@ software, the reference implementation. `tpattern` is an open, complementary
|
|
|
62
116
|
implementation that reproduces THEME's detection and adds a reproducible chance
|
|
63
117
|
model and clear reporting:
|
|
64
118
|
|
|
65
|
-
- **reproduces** THEME's core detection (critical intervals,
|
|
119
|
+
- **reproduces** THEME's core detection (critical intervals, NX/T baseline,
|
|
66
120
|
completeness competition) — validated to the pattern for published datasets;
|
|
67
121
|
- **adds a chance model** — every pattern is tested against a surrogate null, with
|
|
68
122
|
p-values **corrected for repeated testing** (false-discovery rate and
|
|
@@ -109,12 +163,18 @@ print(recommend(observations))
|
|
|
109
163
|
result = calibrate(observations, Config(), null="profile", B=200, q_target=0.05)
|
|
110
164
|
survivors = result.kept("fdr") # patterns that survive false-discovery control
|
|
111
165
|
|
|
112
|
-
# 4. Report: a tidy table (
|
|
113
|
-
#
|
|
114
|
-
|
|
166
|
+
# 4. Report: a tidy results table (monte_carlo_p, fdr_q, survives_fwer_holm, the observed
|
|
167
|
+
# timing, and a plain-English `interpretation` per pattern), pattern dendrograms, the
|
|
168
|
+
# temporal-signature and occurrence plots, the event-type roles/connections tables, and a
|
|
169
|
+
# paste-ready Methods paragraph stating every setting and what it does.
|
|
170
|
+
report(result, "report_out", observations=observations, title="My analysis")
|
|
115
171
|
print(methods_text(Config(), observations=observations, calibration=result))
|
|
116
172
|
```
|
|
117
173
|
|
|
174
|
+
Prefer not to write the pipeline yourself? `python -c "import tpattern;
|
|
175
|
+
tpattern.new_analysis()"` writes a commented [`my_analysis.py`](examples/my_analysis.py)
|
|
176
|
+
you edit and run (see [Getting started](#getting-started-in-a-minute)).
|
|
177
|
+
|
|
118
178
|
Two runnable examples:
|
|
119
179
|
|
|
120
180
|
```bash
|
|
@@ -128,7 +188,8 @@ Config()).detect()` returns them directly.
|
|
|
128
188
|
|
|
129
189
|
**Comparing two groups** (winner vs loser, goal vs non-goal, condition A vs B)?
|
|
130
190
|
`group_contrast(observations, group_of)` tests each pattern's occurrence between
|
|
131
|
-
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot
|
|
191
|
+
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot` — or run
|
|
192
|
+
`new_analysis(template="two_groups")` for a ready-made script.
|
|
132
193
|
|
|
133
194
|
## The workflow
|
|
134
195
|
|
|
@@ -179,7 +240,10 @@ survive the surrogate null. All seeded and deterministic. See
|
|
|
179
240
|
- [`SCHEMA.md`](SCHEMA.md) — the input data structure (what your export must contain).
|
|
180
241
|
- [`CODING_GUIDE.md`](CODING_GUIDE.md) — how to set up a coding window to produce it.
|
|
181
242
|
- [`data/`](data/) — the derived World Cup sequences, in the canonical format.
|
|
182
|
-
- [`examples/`](examples/) — a runnable quickstart
|
|
243
|
+
- [`examples/`](examples/) — a runnable quickstart, the reproduction script, and the
|
|
244
|
+
`my_analysis.py` / `my_group_comparison.py` / `my_null_choice.py` starter templates.
|
|
245
|
+
- [`PARAMETERS.md`](PARAMETERS.md) — every setting and output column, with range, recommended
|
|
246
|
+
value, and what higher/lower does (generated from the code, so it never drifts).
|
|
183
247
|
|
|
184
248
|
## Tests
|
|
185
249
|
|
|
@@ -19,6 +19,60 @@ analysis companion to the [OpenTag.Studio](https://opentag.studio) tagging tool.
|
|
|
19
19
|
Bring your own data (one row per event — see [`SCHEMA.md`](SCHEMA.md)) or try the
|
|
20
20
|
included [`data/`](data/) example.
|
|
21
21
|
|
|
22
|
+
## Getting started in a minute
|
|
23
|
+
|
|
24
|
+
Install, generate a starter script, point it at your data, run it:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install tpattern
|
|
28
|
+
python -c "import tpattern; tpattern.new_analysis()" # writes my_analysis.py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Open `my_analysis.py`, change the two lines marked `# <-- EDIT` (your file and its time
|
|
32
|
+
unit), and run `python my_analysis.py`. It loads your events, **checks the file and tells
|
|
33
|
+
you in plain language about any problem**, recommends the analysis settings with reasons,
|
|
34
|
+
calibrates every pattern against a chance model, and writes a folder of results with a
|
|
35
|
+
plain-English verdict for each pattern.
|
|
36
|
+
|
|
37
|
+
Doing something more specific? Generate a template for it:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from tpattern import new_analysis
|
|
41
|
+
new_analysis() # one group: detect, calibrate, report
|
|
42
|
+
new_analysis(template="two_groups") # compare two groups (odds ratios + forest plot)
|
|
43
|
+
new_analysis(template="null_choice") # core vs shell: which null a pattern survives
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Not sure what a setting means? Ask the code — every setting and output column is documented:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from tpattern import explain
|
|
50
|
+
print(explain("min_lag")) # what it does, its range, the recommended value, higher vs lower
|
|
51
|
+
print(explain("fdr_q")) # what an output column means
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What you get
|
|
55
|
+
|
|
56
|
+
- **Forgiving data import.** `read_table` checks your file as it reads it and reports, in
|
|
57
|
+
plain language, any problem, how much of the file it affects, and how to fix it — instead
|
|
58
|
+
of silently dropping rows. Each check has a stable code (Appendix A of the tutorial; also
|
|
59
|
+
[`PARAMETERS.md`](PARAMETERS.md)).
|
|
60
|
+
- **A proper chance model.** Every pattern is tested against a surrogate null, with p-values
|
|
61
|
+
corrected for repeated testing (Benjamini–Hochberg FDR for screening, Holm family-wise for
|
|
62
|
+
confirmatory claims), stratified by hierarchy level.
|
|
63
|
+
- **Temporal reporting.** Beyond *which* patterns are real, the output says *when*: each
|
|
64
|
+
pattern's occurrence timing (median lag and spread) is reported by default — on the
|
|
65
|
+
dendrogram, in the table, and in the plain-English verdict — for every pattern.
|
|
66
|
+
- **Readable output.** Results-table columns are named for what they are (`monte_carlo_p`,
|
|
67
|
+
`fdr_q`, `survives_fwer_holm`, `bout_support`, `duration_median_ms`), and each pattern
|
|
68
|
+
carries a plain-English `interpretation`. `explain()` documents every setting and column;
|
|
69
|
+
the full reference is in [`PARAMETERS.md`](PARAMETERS.md).
|
|
70
|
+
- **One reference, everywhere.** Settings and columns live in one registry that generates the
|
|
71
|
+
code's `explain()`, `PARAMETERS.md`, the Colab form labels, and the tutorial's tables — so
|
|
72
|
+
the software, the paper and the notebook can't drift apart.
|
|
73
|
+
- **Clean figures** — dendrograms, temporal signatures, forest plots — that share one
|
|
74
|
+
customisable style (`tpattern.viz.STYLE`).
|
|
75
|
+
|
|
22
76
|
## Why it exists
|
|
23
77
|
|
|
24
78
|
T-pattern analysis finds behaviourally meaningful structure that event counts and
|
|
@@ -27,7 +81,7 @@ software, the reference implementation. `tpattern` is an open, complementary
|
|
|
27
81
|
implementation that reproduces THEME's detection and adds a reproducible chance
|
|
28
82
|
model and clear reporting:
|
|
29
83
|
|
|
30
|
-
- **reproduces** THEME's core detection (critical intervals,
|
|
84
|
+
- **reproduces** THEME's core detection (critical intervals, NX/T baseline,
|
|
31
85
|
completeness competition) — validated to the pattern for published datasets;
|
|
32
86
|
- **adds a chance model** — every pattern is tested against a surrogate null, with
|
|
33
87
|
p-values **corrected for repeated testing** (false-discovery rate and
|
|
@@ -74,12 +128,18 @@ print(recommend(observations))
|
|
|
74
128
|
result = calibrate(observations, Config(), null="profile", B=200, q_target=0.05)
|
|
75
129
|
survivors = result.kept("fdr") # patterns that survive false-discovery control
|
|
76
130
|
|
|
77
|
-
# 4. Report: a tidy table (
|
|
78
|
-
#
|
|
79
|
-
|
|
131
|
+
# 4. Report: a tidy results table (monte_carlo_p, fdr_q, survives_fwer_holm, the observed
|
|
132
|
+
# timing, and a plain-English `interpretation` per pattern), pattern dendrograms, the
|
|
133
|
+
# temporal-signature and occurrence plots, the event-type roles/connections tables, and a
|
|
134
|
+
# paste-ready Methods paragraph stating every setting and what it does.
|
|
135
|
+
report(result, "report_out", observations=observations, title="My analysis")
|
|
80
136
|
print(methods_text(Config(), observations=observations, calibration=result))
|
|
81
137
|
```
|
|
82
138
|
|
|
139
|
+
Prefer not to write the pipeline yourself? `python -c "import tpattern;
|
|
140
|
+
tpattern.new_analysis()"` writes a commented [`my_analysis.py`](examples/my_analysis.py)
|
|
141
|
+
you edit and run (see [Getting started](#getting-started-in-a-minute)).
|
|
142
|
+
|
|
83
143
|
Two runnable examples:
|
|
84
144
|
|
|
85
145
|
```bash
|
|
@@ -93,7 +153,8 @@ Config()).detect()` returns them directly.
|
|
|
93
153
|
|
|
94
154
|
**Comparing two groups** (winner vs loser, goal vs non-goal, condition A vs B)?
|
|
95
155
|
`group_contrast(observations, group_of)` tests each pattern's occurrence between
|
|
96
|
-
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot
|
|
156
|
+
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot` — or run
|
|
157
|
+
`new_analysis(template="two_groups")` for a ready-made script.
|
|
97
158
|
|
|
98
159
|
## The workflow
|
|
99
160
|
|
|
@@ -144,7 +205,10 @@ survive the surrogate null. All seeded and deterministic. See
|
|
|
144
205
|
- [`SCHEMA.md`](SCHEMA.md) — the input data structure (what your export must contain).
|
|
145
206
|
- [`CODING_GUIDE.md`](CODING_GUIDE.md) — how to set up a coding window to produce it.
|
|
146
207
|
- [`data/`](data/) — the derived World Cup sequences, in the canonical format.
|
|
147
|
-
- [`examples/`](examples/) — a runnable quickstart
|
|
208
|
+
- [`examples/`](examples/) — a runnable quickstart, the reproduction script, and the
|
|
209
|
+
`my_analysis.py` / `my_group_comparison.py` / `my_null_choice.py` starter templates.
|
|
210
|
+
- [`PARAMETERS.md`](PARAMETERS.md) — every setting and output column, with range, recommended
|
|
211
|
+
value, and what higher/lower does (generated from the code, so it never drifts).
|
|
148
212
|
|
|
149
213
|
## Tests
|
|
150
214
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tpattern"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.13"
|
|
8
8
|
description = "Open, auditable temporal & sequential pattern analysis (T-patterns) for sport and behaviour"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""The copy-and-adapt analysis template generator and its variants."""
|
|
2
|
+
import ast, pytest
|
|
3
|
+
from tpattern import new_analysis
|
|
4
|
+
from tpattern.starter import TEMPLATES
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_all_templates_are_valid_python():
|
|
8
|
+
assert set(TEMPLATES) == {"basic", "two_groups", "null_choice"}
|
|
9
|
+
for name, src in TEMPLATES.items():
|
|
10
|
+
ast.parse(src) # each generated script is valid Python
|
|
11
|
+
assert "# <-- EDIT" in src and "read_table" in src
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_new_analysis_default_and_named(tmp_path):
|
|
15
|
+
p = new_analysis(tmp_path / "a.py")
|
|
16
|
+
assert "recommend" in open(p).read() and "calibrate" in open(p).read()
|
|
17
|
+
g = new_analysis(tmp_path / "g.py", template="two_groups")
|
|
18
|
+
assert "group_contrast" in open(g).read() and "forest_plot" in open(g).read()
|
|
19
|
+
n = new_analysis(tmp_path / "n.py", template="null_choice")
|
|
20
|
+
assert "compare_nulls" in open(n).read()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_overwrite_guard_and_bad_name(tmp_path):
|
|
24
|
+
f = tmp_path / "a.py"
|
|
25
|
+
new_analysis(f)
|
|
26
|
+
with pytest.raises(FileExistsError):
|
|
27
|
+
new_analysis(f)
|
|
28
|
+
new_analysis(f, overwrite=True)
|
|
29
|
+
with pytest.raises(ValueError):
|
|
30
|
+
new_analysis(tmp_path / "x.py", template="nope")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_basic_template_uses_readable_columns():
|
|
34
|
+
assert "monte_carlo_p" in TEMPLATES["basic"] and "survives_fwer_holm" in TEMPLATES["basic"]
|
|
@@ -6,7 +6,7 @@ behavior: T-patterns and their detection. Behavior Research Methods,
|
|
|
6
6
|
Instruments, & Computers, 32(1), 93-110.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "0.1.
|
|
9
|
+
__version__ = "0.1.13" # defined before submodule imports (methods.py reads it)
|
|
10
10
|
|
|
11
11
|
from .io import (Observation, read_observation, read_sample, read_table,
|
|
12
12
|
read_sofcoder, DataFormatError, check_reference, CHECKS)
|
|
@@ -26,6 +26,7 @@ from .guided import run_analysis, launch
|
|
|
26
26
|
from .contrast import group_contrast, contrast_items
|
|
27
27
|
from .reference import (explain, explain_settings, explain_columns,
|
|
28
28
|
parameters_markdown, SETTINGS, COLUMNS)
|
|
29
|
+
from .starter import new_analysis, TEMPLATE
|
|
29
30
|
|
|
30
31
|
__all__ = [
|
|
31
32
|
"Observation", "read_observation", "read_sample", "read_table", "read_sofcoder",
|
|
@@ -43,5 +44,5 @@ __all__ = [
|
|
|
43
44
|
"run_analysis", "launch",
|
|
44
45
|
"group_contrast", "contrast_items",
|
|
45
46
|
"explain", "explain_settings", "explain_columns", "parameters_markdown",
|
|
46
|
-
"SETTINGS", "COLUMNS",
|
|
47
|
+
"SETTINGS", "COLUMNS", "new_analysis",
|
|
47
48
|
]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Starting points students can copy and adapt.
|
|
3
|
+
=============================================
|
|
4
|
+
|
|
5
|
+
The tutorial teaches the method on the worked example; this gives a learner a clean
|
|
6
|
+
template for their *own* data. `new_analysis()` writes a heavily commented starter
|
|
7
|
+
script, with every line to change marked and pointers to `explain()` for each setting.
|
|
8
|
+
Three flavours cover the common designs:
|
|
9
|
+
|
|
10
|
+
new_analysis() # one group: detect, calibrate, report
|
|
11
|
+
new_analysis(template="two_groups") # compare two groups (odds ratios + forest plot)
|
|
12
|
+
new_analysis(template="null_choice") # core vs shell: which null a pattern survives
|
|
13
|
+
|
|
14
|
+
It is the "now do it on your own research" bridge.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
_BASIC = '''\
|
|
22
|
+
"""
|
|
23
|
+
My T-pattern analysis. (tpattern.new_analysis — copy and adapt.)
|
|
24
|
+
|
|
25
|
+
Point this at your own event table and run it. Every line to change is marked # <-- EDIT.
|
|
26
|
+
For any setting, see what it does, its range and the recommended value with:
|
|
27
|
+
from tpattern import explain; print(explain("min_lag"))
|
|
28
|
+
Your data: one row per event, with columns observation, event, start
|
|
29
|
+
(which session/rally/bout the event belongs to, what happened, and when). See SCHEMA.md
|
|
30
|
+
and the tutorial paper for the full walk-through.
|
|
31
|
+
"""
|
|
32
|
+
from tpattern import read_table, recommend, Config, calibrate, report, explain
|
|
33
|
+
|
|
34
|
+
# 1. LOAD -----------------------------------------------------------------------
|
|
35
|
+
DATA_FILE = "my_events.csv" # <-- EDIT: your event table (CSV)
|
|
36
|
+
TIME_UNIT = "ms" # <-- EDIT: "s" or "ms", the unit of your start column
|
|
37
|
+
observations = read_table(DATA_FILE, time_unit=TIME_UNIT, report="all")
|
|
38
|
+
# read_table checks the file as it reads it and prints any problems by code (Appendix A).
|
|
39
|
+
|
|
40
|
+
# 2. INSPECT + RECOMMEND --------------------------------------------------------
|
|
41
|
+
# The advisor inspects your data and recommends the null, the minimum lag and the error
|
|
42
|
+
# control, each with its reason. Read it, then decide (you are not obliged to follow it).
|
|
43
|
+
print(recommend(observations))
|
|
44
|
+
|
|
45
|
+
# 3. CONFIGURE ------------------------------------------------------------------
|
|
46
|
+
# Start from the advisor's recommendation. To understand any setting, print its help:
|
|
47
|
+
# print(explain("min_lag")) print(explain("surrogate_null")) print(explain("frequent_event_exclusion"))
|
|
48
|
+
cfg = Config(
|
|
49
|
+
min_lag=1, # <-- 1 requires a genuine time gap (recommended for frame-coded data); 0 = THEME-style
|
|
50
|
+
freq_exclude=1.5, # <-- set a very common event aside from pattern-building (kept in the counts)
|
|
51
|
+
# everything else is left at sensible defaults; run explain() to see them
|
|
52
|
+
)
|
|
53
|
+
NULL = "profile" # <-- "profile" tests coupling beyond each type's own timing (the usual choice)
|
|
54
|
+
B = 2000 # <-- surrogates: 200 to screen many patterns, 2000+ for a confirmatory claim
|
|
55
|
+
|
|
56
|
+
# 4. CALIBRATE + REPORT ---------------------------------------------------------
|
|
57
|
+
result = calibrate(observations, cfg, null=NULL, B=B, q_target=0.05)
|
|
58
|
+
report(result, "my_output", observations=observations, title="My analysis")
|
|
59
|
+
|
|
60
|
+
# my_output/ now holds the results table (patterns_table.csv, with monte_carlo_p, fdr_q and
|
|
61
|
+
# survives_fwer_holm), the pattern dendrograms, the temporal-signature plot, the event-type
|
|
62
|
+
# roles and connections tables, and a plain-text SUMMARY. Each pattern's "interpretation"
|
|
63
|
+
# column reads it in plain English. To understand a column: print(explain("fdr_q"))
|
|
64
|
+
print("\\nDone. Open the my_output/ folder.")
|
|
65
|
+
'''
|
|
66
|
+
|
|
67
|
+
_TWO_GROUPS = '''\
|
|
68
|
+
"""
|
|
69
|
+
My T-pattern group comparison. (tpattern.new_analysis(template="two_groups"))
|
|
70
|
+
|
|
71
|
+
Do patterns occur differently between two groups (winners vs losers, condition A vs B,
|
|
72
|
+
goals vs non-goals)? For each pattern this tests its occurrence between the groups
|
|
73
|
+
(Fisher's exact test, odds ratio with a 95% confidence interval) and draws a forest plot.
|
|
74
|
+
Lines to change are marked # <-- EDIT. Run explain("min_lag") etc. for any setting.
|
|
75
|
+
"""
|
|
76
|
+
import csv
|
|
77
|
+
from tpattern import read_table, Config, group_contrast, forest_plot
|
|
78
|
+
|
|
79
|
+
# 1. LOAD your events (one row per event: observation, event, start).
|
|
80
|
+
DATA_FILE = "my_events.csv" # <-- EDIT
|
|
81
|
+
TIME_UNIT = "ms" # <-- EDIT: "s" or "ms"
|
|
82
|
+
observations = read_table(DATA_FILE, time_unit=TIME_UNIT, report="all")
|
|
83
|
+
|
|
84
|
+
# 2. GROUP each observation. group_of maps an observation's name to its group label.
|
|
85
|
+
# Easiest: a two-column CSV (observation,group). <-- EDIT the path and column names.
|
|
86
|
+
group_of = {row["observation"]: row["group"]
|
|
87
|
+
for row in csv.DictReader(open("my_groups.csv"))}
|
|
88
|
+
|
|
89
|
+
# 3. CONTRAST: detect patterns and compare their occurrence between the two groups.
|
|
90
|
+
# min_count drops patterns too rare to compare; raise it to be more conservative.
|
|
91
|
+
items = group_contrast(observations, group_of, Config(min_lag=1), min_count=5)
|
|
92
|
+
|
|
93
|
+
# 4. PLOT the odds ratios with 95% CIs (a point whose CI excludes 1 differs between groups).
|
|
94
|
+
forest_plot(items, "group_contrast.png", title="Pattern occurrence: group comparison")
|
|
95
|
+
print("wrote group_contrast.png (odds ratios with 95% confidence intervals)")
|
|
96
|
+
'''
|
|
97
|
+
|
|
98
|
+
_NULL_CHOICE = '''\
|
|
99
|
+
"""
|
|
100
|
+
My null-choice comparison. (tpattern.new_analysis(template="null_choice"))
|
|
101
|
+
|
|
102
|
+
Which patterns are genuine coupling, and which are just shared timing? This calibrates
|
|
103
|
+
your data under two nulls and splits the survivors into the CORE (survives both nulls =
|
|
104
|
+
coupling beyond each event's own timing) and the SHELL (survives the rotation null only =
|
|
105
|
+
co-occurrence driven by shared timing). Lines to change are marked # <-- EDIT.
|
|
106
|
+
"""
|
|
107
|
+
from tpattern import read_table, Config, compare_nulls, null_comparison_plot
|
|
108
|
+
|
|
109
|
+
DATA_FILE = "my_events.csv" # <-- EDIT
|
|
110
|
+
TIME_UNIT = "ms" # <-- EDIT: "s" or "ms"
|
|
111
|
+
observations = read_table(DATA_FILE, time_unit=TIME_UNIT, report="all")
|
|
112
|
+
|
|
113
|
+
# Compare the profile-preserving null (coupling) against the rotation null (any structure).
|
|
114
|
+
# B = 2000 gives a fine Monte-Carlo p; use 200 to explore quickly first.
|
|
115
|
+
cmp = compare_nulls(observations, Config(min_lag=1), nulls=("profile", "rotation"), B=2000)
|
|
116
|
+
print(cmp) # lists the core and the shell, with each pattern's q under each null
|
|
117
|
+
|
|
118
|
+
null_comparison_plot(cmp, "null_comparison.png")
|
|
119
|
+
print("\\nwrote null_comparison.png (core = survives both nulls; shell = rotation only)")
|
|
120
|
+
'''
|
|
121
|
+
|
|
122
|
+
TEMPLATES = {"basic": _BASIC, "two_groups": _TWO_GROUPS, "null_choice": _NULL_CHOICE}
|
|
123
|
+
_DEFAULT_NAME = {"basic": "my_analysis.py", "two_groups": "my_group_comparison.py",
|
|
124
|
+
"null_choice": "my_null_choice.py"}
|
|
125
|
+
|
|
126
|
+
# kept for backward compatibility (0.1.12 exported TEMPLATE)
|
|
127
|
+
TEMPLATE = _BASIC
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def new_analysis(path: str | Path | None = None, *, template: str = "basic",
|
|
131
|
+
overwrite: bool = False) -> str:
|
|
132
|
+
"""Write a copy-and-adapt analysis template to `path`. Returns the path written.
|
|
133
|
+
|
|
134
|
+
`template` is one of ``"basic"`` (one group), ``"two_groups"`` (compare two groups),
|
|
135
|
+
or ``"null_choice"`` (core vs shell). If `path` is omitted a sensible filename is used.
|
|
136
|
+
|
|
137
|
+
>>> from tpattern import new_analysis
|
|
138
|
+
>>> new_analysis() # writes ./my_analysis.py
|
|
139
|
+
>>> new_analysis(template="two_groups") # writes ./my_group_comparison.py
|
|
140
|
+
"""
|
|
141
|
+
if template not in TEMPLATES:
|
|
142
|
+
raise ValueError(f"template must be one of {sorted(TEMPLATES)}, got {template!r}")
|
|
143
|
+
p = Path(path) if path is not None else Path(_DEFAULT_NAME[template])
|
|
144
|
+
if p.exists() and not overwrite:
|
|
145
|
+
raise FileExistsError(f"{p} already exists; pass overwrite=True to replace it.")
|
|
146
|
+
p.write_text(TEMPLATES[template])
|
|
147
|
+
return str(p)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tpattern
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Open, auditable temporal & sequential pattern analysis (T-patterns) for sport and behaviour
|
|
5
5
|
Author-email: Andrew Callaway <acallaway@bournemouth.ac.uk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -54,6 +54,60 @@ analysis companion to the [OpenTag.Studio](https://opentag.studio) tagging tool.
|
|
|
54
54
|
Bring your own data (one row per event — see [`SCHEMA.md`](SCHEMA.md)) or try the
|
|
55
55
|
included [`data/`](data/) example.
|
|
56
56
|
|
|
57
|
+
## Getting started in a minute
|
|
58
|
+
|
|
59
|
+
Install, generate a starter script, point it at your data, run it:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install tpattern
|
|
63
|
+
python -c "import tpattern; tpattern.new_analysis()" # writes my_analysis.py
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Open `my_analysis.py`, change the two lines marked `# <-- EDIT` (your file and its time
|
|
67
|
+
unit), and run `python my_analysis.py`. It loads your events, **checks the file and tells
|
|
68
|
+
you in plain language about any problem**, recommends the analysis settings with reasons,
|
|
69
|
+
calibrates every pattern against a chance model, and writes a folder of results with a
|
|
70
|
+
plain-English verdict for each pattern.
|
|
71
|
+
|
|
72
|
+
Doing something more specific? Generate a template for it:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from tpattern import new_analysis
|
|
76
|
+
new_analysis() # one group: detect, calibrate, report
|
|
77
|
+
new_analysis(template="two_groups") # compare two groups (odds ratios + forest plot)
|
|
78
|
+
new_analysis(template="null_choice") # core vs shell: which null a pattern survives
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Not sure what a setting means? Ask the code — every setting and output column is documented:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from tpattern import explain
|
|
85
|
+
print(explain("min_lag")) # what it does, its range, the recommended value, higher vs lower
|
|
86
|
+
print(explain("fdr_q")) # what an output column means
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## What you get
|
|
90
|
+
|
|
91
|
+
- **Forgiving data import.** `read_table` checks your file as it reads it and reports, in
|
|
92
|
+
plain language, any problem, how much of the file it affects, and how to fix it — instead
|
|
93
|
+
of silently dropping rows. Each check has a stable code (Appendix A of the tutorial; also
|
|
94
|
+
[`PARAMETERS.md`](PARAMETERS.md)).
|
|
95
|
+
- **A proper chance model.** Every pattern is tested against a surrogate null, with p-values
|
|
96
|
+
corrected for repeated testing (Benjamini–Hochberg FDR for screening, Holm family-wise for
|
|
97
|
+
confirmatory claims), stratified by hierarchy level.
|
|
98
|
+
- **Temporal reporting.** Beyond *which* patterns are real, the output says *when*: each
|
|
99
|
+
pattern's occurrence timing (median lag and spread) is reported by default — on the
|
|
100
|
+
dendrogram, in the table, and in the plain-English verdict — for every pattern.
|
|
101
|
+
- **Readable output.** Results-table columns are named for what they are (`monte_carlo_p`,
|
|
102
|
+
`fdr_q`, `survives_fwer_holm`, `bout_support`, `duration_median_ms`), and each pattern
|
|
103
|
+
carries a plain-English `interpretation`. `explain()` documents every setting and column;
|
|
104
|
+
the full reference is in [`PARAMETERS.md`](PARAMETERS.md).
|
|
105
|
+
- **One reference, everywhere.** Settings and columns live in one registry that generates the
|
|
106
|
+
code's `explain()`, `PARAMETERS.md`, the Colab form labels, and the tutorial's tables — so
|
|
107
|
+
the software, the paper and the notebook can't drift apart.
|
|
108
|
+
- **Clean figures** — dendrograms, temporal signatures, forest plots — that share one
|
|
109
|
+
customisable style (`tpattern.viz.STYLE`).
|
|
110
|
+
|
|
57
111
|
## Why it exists
|
|
58
112
|
|
|
59
113
|
T-pattern analysis finds behaviourally meaningful structure that event counts and
|
|
@@ -62,7 +116,7 @@ software, the reference implementation. `tpattern` is an open, complementary
|
|
|
62
116
|
implementation that reproduces THEME's detection and adds a reproducible chance
|
|
63
117
|
model and clear reporting:
|
|
64
118
|
|
|
65
|
-
- **reproduces** THEME's core detection (critical intervals,
|
|
119
|
+
- **reproduces** THEME's core detection (critical intervals, NX/T baseline,
|
|
66
120
|
completeness competition) — validated to the pattern for published datasets;
|
|
67
121
|
- **adds a chance model** — every pattern is tested against a surrogate null, with
|
|
68
122
|
p-values **corrected for repeated testing** (false-discovery rate and
|
|
@@ -109,12 +163,18 @@ print(recommend(observations))
|
|
|
109
163
|
result = calibrate(observations, Config(), null="profile", B=200, q_target=0.05)
|
|
110
164
|
survivors = result.kept("fdr") # patterns that survive false-discovery control
|
|
111
165
|
|
|
112
|
-
# 4. Report: a tidy table (
|
|
113
|
-
#
|
|
114
|
-
|
|
166
|
+
# 4. Report: a tidy results table (monte_carlo_p, fdr_q, survives_fwer_holm, the observed
|
|
167
|
+
# timing, and a plain-English `interpretation` per pattern), pattern dendrograms, the
|
|
168
|
+
# temporal-signature and occurrence plots, the event-type roles/connections tables, and a
|
|
169
|
+
# paste-ready Methods paragraph stating every setting and what it does.
|
|
170
|
+
report(result, "report_out", observations=observations, title="My analysis")
|
|
115
171
|
print(methods_text(Config(), observations=observations, calibration=result))
|
|
116
172
|
```
|
|
117
173
|
|
|
174
|
+
Prefer not to write the pipeline yourself? `python -c "import tpattern;
|
|
175
|
+
tpattern.new_analysis()"` writes a commented [`my_analysis.py`](examples/my_analysis.py)
|
|
176
|
+
you edit and run (see [Getting started](#getting-started-in-a-minute)).
|
|
177
|
+
|
|
118
178
|
Two runnable examples:
|
|
119
179
|
|
|
120
180
|
```bash
|
|
@@ -128,7 +188,8 @@ Config()).detect()` returns them directly.
|
|
|
128
188
|
|
|
129
189
|
**Comparing two groups** (winner vs loser, goal vs non-goal, condition A vs B)?
|
|
130
190
|
`group_contrast(observations, group_of)` tests each pattern's occurrence between
|
|
131
|
-
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot
|
|
191
|
+
the groups (Fisher's exact test, odds ratio + 95% CI) and feeds `forest_plot` — or run
|
|
192
|
+
`new_analysis(template="two_groups")` for a ready-made script.
|
|
132
193
|
|
|
133
194
|
## The workflow
|
|
134
195
|
|
|
@@ -179,7 +240,10 @@ survive the surrogate null. All seeded and deterministic. See
|
|
|
179
240
|
- [`SCHEMA.md`](SCHEMA.md) — the input data structure (what your export must contain).
|
|
180
241
|
- [`CODING_GUIDE.md`](CODING_GUIDE.md) — how to set up a coding window to produce it.
|
|
181
242
|
- [`data/`](data/) — the derived World Cup sequences, in the canonical format.
|
|
182
|
-
- [`examples/`](examples/) — a runnable quickstart
|
|
243
|
+
- [`examples/`](examples/) — a runnable quickstart, the reproduction script, and the
|
|
244
|
+
`my_analysis.py` / `my_group_comparison.py` / `my_null_choice.py` starter templates.
|
|
245
|
+
- [`PARAMETERS.md`](PARAMETERS.md) — every setting and output column, with range, recommended
|
|
246
|
+
value, and what higher/lower does (generated from the code, so it never drifts).
|
|
183
247
|
|
|
184
248
|
## Tests
|
|
185
249
|
|
|
@@ -12,6 +12,7 @@ tests/test_reference.py
|
|
|
12
12
|
tests/test_report_dendrogram.py
|
|
13
13
|
tests/test_reproduction.py
|
|
14
14
|
tests/test_sofcoder.py
|
|
15
|
+
tests/test_starter.py
|
|
15
16
|
tests/test_support_reporting.py
|
|
16
17
|
tests/test_synthetic.py
|
|
17
18
|
tpattern/__init__.py
|
|
@@ -28,6 +29,7 @@ tpattern/randomise.py
|
|
|
28
29
|
tpattern/reference.py
|
|
29
30
|
tpattern/report.py
|
|
30
31
|
tpattern/significance.py
|
|
32
|
+
tpattern/starter.py
|
|
31
33
|
tpattern/synthetic.py
|
|
32
34
|
tpattern/viz.py
|
|
33
35
|
tpattern.egg-info/PKG-INFO
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|