tpattern 0.1.11__tar.gz → 0.1.12__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.
Files changed (39) hide show
  1. {tpattern-0.1.11/tpattern.egg-info → tpattern-0.1.12}/PKG-INFO +1 -1
  2. {tpattern-0.1.11 → tpattern-0.1.12}/pyproject.toml +1 -1
  3. tpattern-0.1.12/tests/test_reference.py +54 -0
  4. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/__init__.py +5 -1
  5. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/guided.py +2 -2
  6. tpattern-0.1.12/tpattern/reference.py +214 -0
  7. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/report.py +11 -7
  8. {tpattern-0.1.11 → tpattern-0.1.12/tpattern.egg-info}/PKG-INFO +1 -1
  9. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern.egg-info/SOURCES.txt +2 -0
  10. {tpattern-0.1.11 → tpattern-0.1.12}/LICENSE +0 -0
  11. {tpattern-0.1.11 → tpattern-0.1.12}/README.md +0 -0
  12. {tpattern-0.1.11 → tpattern-0.1.12}/setup.cfg +0 -0
  13. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_calibrated_pins.py +0 -0
  14. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_contrast.py +0 -0
  15. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_dedup.py +0 -0
  16. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_guided.py +0 -0
  17. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_null_compare.py +0 -0
  18. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_outputs.py +0 -0
  19. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_read_table_validation.py +0 -0
  20. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_report_dendrogram.py +0 -0
  21. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_reproduction.py +0 -0
  22. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_sofcoder.py +0 -0
  23. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_support_reporting.py +0 -0
  24. {tpattern-0.1.11 → tpattern-0.1.12}/tests/test_synthetic.py +0 -0
  25. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/advisor.py +0 -0
  26. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/ci.py +0 -0
  27. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/contrast.py +0 -0
  28. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/detect.py +0 -0
  29. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/diagnostics.py +0 -0
  30. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/io.py +0 -0
  31. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/methods.py +0 -0
  32. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/pattern.py +0 -0
  33. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/randomise.py +0 -0
  34. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/significance.py +0 -0
  35. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/synthetic.py +0 -0
  36. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern/viz.py +0 -0
  37. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern.egg-info/dependency_links.txt +0 -0
  38. {tpattern-0.1.11 → tpattern-0.1.12}/tpattern.egg-info/requires.txt +0 -0
  39. {tpattern-0.1.11 → tpattern-0.1.12}/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.11
3
+ Version: 0.1.12
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tpattern"
7
- version = "0.1.11"
7
+ version = "0.1.12"
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,54 @@
1
+ """The reference registry: coverage, explain(), and the renamed output columns."""
2
+ from tpattern import (SETTINGS, COLUMNS, explain, explain_settings, explain_columns,
3
+ parameters_markdown, read_table, Config, calibrate,
4
+ patterns_table, Observation)
5
+
6
+
7
+ def test_every_setting_is_complete():
8
+ for s in SETTINGS:
9
+ for field in ("key", "form", "group", "does", "range", "recommend", "effect"):
10
+ assert s.get(field), f"{s.get('form')} missing {field}"
11
+ assert s["group"] in ("A", "B", "C")
12
+
13
+
14
+ def test_explain_by_key_and_form_name():
15
+ a = explain("min_lag")
16
+ b = explain("minimum_lag_frames")
17
+ assert a == b and "recommended" in a and "range" in a
18
+
19
+
20
+ def test_explain_column_and_unknown():
21
+ assert "Benjamini" in explain("fdr_q")
22
+ assert "No setting or column" in explain("does_not_exist")
23
+
24
+
25
+ def test_parameters_markdown_covers_all():
26
+ md = parameters_markdown()
27
+ for s in SETTINGS:
28
+ assert s["form"] in md
29
+ for c in COLUMNS:
30
+ assert c["key"] in md
31
+
32
+
33
+ def _sample():
34
+ obs = []
35
+ for i in range(6):
36
+ ev = [(0, "A"), (300, "B")] + ([(600, "C")] if i < 3 else [])
37
+ obs.append(Observation(name=f"o{i}", start=0, end=1000, events=ev))
38
+ return obs
39
+
40
+
41
+ def test_output_columns_renamed():
42
+ res = calibrate(_sample(), Config(min_lag=1), null="profile", B=50, q_target=.05, seed=1)
43
+ row = patterns_table(res)[0]
44
+ assert "monte_carlo_p" in row and "survives_fwer_holm" in row
45
+ assert "p_emp" not in row and "fwer_keep" not in row
46
+ # every reported column has a registry entry (the tutorial table generates from it)
47
+ keys = {c["key"] for c in COLUMNS}
48
+ for col in ("monte_carlo_p", "fdr_q", "survives_fwer_holm", "N", "level", "bout_support"):
49
+ assert col in keys
50
+
51
+
52
+ def test_sort_tolerates_old_name():
53
+ res = calibrate(_sample(), Config(min_lag=1), null="profile", B=50, q_target=.05, seed=1)
54
+ patterns_table(res, sort="p_emp") # must not raise
@@ -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.11" # defined before submodule imports (methods.py reads it)
9
+ __version__ = "0.1.12" # 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)
@@ -24,6 +24,8 @@ from .advisor import recommend
24
24
  from .methods import methods_text
25
25
  from .guided import run_analysis, launch
26
26
  from .contrast import group_contrast, contrast_items
27
+ from .reference import (explain, explain_settings, explain_columns,
28
+ parameters_markdown, SETTINGS, COLUMNS)
27
29
 
28
30
  __all__ = [
29
31
  "Observation", "read_observation", "read_sample", "read_table", "read_sofcoder",
@@ -40,4 +42,6 @@ __all__ = [
40
42
  "recommend", "methods_text",
41
43
  "run_analysis", "launch",
42
44
  "group_contrast", "contrast_items",
45
+ "explain", "explain_settings", "explain_columns", "parameters_markdown",
46
+ "SETTINGS", "COLUMNS",
43
47
  ]
@@ -104,8 +104,8 @@ def _write_excel(result, q_target, path):
104
104
  rows.append({
105
105
  "pattern": str(c.pattern), "N": c.N, "level": c.level,
106
106
  "critical_interval": f"[{c.ci[0]},{c.ci[1]}]" if c.ci else "",
107
- "p_empirical": round(c.p_emp, 4), "q_FDR": round(c.fdr_q, 4),
108
- "survives_FDR": int(c.fdr_q <= q_target), "survives_FWER": int(c.fwer_keep),
107
+ "monte_carlo_p": round(c.p_emp, 4), "fdr_q": round(c.fdr_q, 4),
108
+ "survives_fdr": int(c.fdr_q <= q_target), "survives_fwer_holm": int(c.fwer_keep),
109
109
  })
110
110
  try:
111
111
  import pandas as pd
@@ -0,0 +1,214 @@
1
+ """
2
+ One reference for every setting and every output column.
3
+ =========================================================
4
+
5
+ The problem this solves: a user meets these names in three places — the Python
6
+ `Config`, the Colab settings form, and the results table — and needs, for each, the
7
+ same four things: what it *is*, what range it takes, what value is *recommended*, and
8
+ what *higher or lower* does to the analysis. Rather than scatter that across
9
+ docstrings, a paper table and the advisor, it lives here once. Everything else is
10
+ generated from it: :func:`explain`, the ``PARAMETERS.md`` reference, the tutorial's
11
+ settings and output-column tables, and the plain-language field labels in the Colab
12
+ form. So the code, the paper and the notebook cannot drift.
13
+
14
+ Each setting record has:
15
+ key the ``Config`` (or read_table/calibrate) argument name
16
+ form the friendly name shown in the Colab form and the paper
17
+ group A = the data can decide it (the advisor recommends);
18
+ B = your policy (the data cannot decide it);
19
+ C = a structural input fixed before analysis
20
+ does what it does to the detection, in one line
21
+ range the values it accepts
22
+ recommend the recommended value, and when
23
+ effect what higher / lower (or on / off) means
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ # --- settings ---------------------------------------------------------------
29
+ SETTINGS: list[dict] = [
30
+ # A. the data can decide these (the advisor recommends them)
31
+ dict(key="null", form="surrogate_null", group="A",
32
+ does="Which chance model each detected pattern is tested against.",
33
+ range="profile, rotation or shuffle.",
34
+ recommend="profile when event types have their own timing (the advisor decides this from the conditional-uniformity check).",
35
+ effect="profile tests coupling beyond each type's own timing; rotation and shuffle test whether any temporal structure is present at all."),
36
+ dict(key="min_lag", form="minimum_lag_frames", group="A",
37
+ does="The smallest genuine time gap required between two linked events.",
38
+ range="a whole number of time units, 0 or more.",
39
+ recommend="1 when many events share a timestamp (the advisor recommends it when more than ~10% of neighbouring events are co-timed); 0 to match THEME.",
40
+ effect="higher treats more co-timed pairs as co-occurrence, not sequence; 0 lets co-timed events count as ordered (THEME-compatible, but sub-frame order is undefined)."),
41
+ dict(key="freq_exclude", form="frequent_event_exclusion", group="A",
42
+ does="Sets aside an over-common event type from building patterns (it is still counted in the baseline).",
43
+ range="a positive number (mean occurrences per observation, under the default rule); off if empty.",
44
+ recommend="the advisor flags a type averaging more than ~1.5 occurrences per observation.",
45
+ effect="lower excludes more (rarer) types; off keeps every type eligible and can flood the results with 'this common event, then anything'."),
46
+ dict(key="min_occurrence", form="minimum_occurrence", group="A",
47
+ does="How many times a pattern must recur before it is tested.",
48
+ range="a whole number, 2 or more (3 is the usual floor).",
49
+ recommend="3, the floor at which the surrogate test can be calibrated.",
50
+ effect="higher keeps only more frequent patterns; below 3 the chance test cannot resolve."),
51
+ dict(key="error_control", form="error_control", group="A",
52
+ does="Which multiple-testing correction is treated as primary.",
53
+ range="fdr or fwer.",
54
+ recommend="fdr to screen many patterns; fwer for a single confirmatory claim.",
55
+ effect="fdr controls the expected share of false discoveries; fwer controls any false positive at all (stricter, and needs a large B)."),
56
+ dict(key="B", form="surrogates_B", group="A",
57
+ does="How many surrogate datasets are drawn for the Monte-Carlo p-value.",
58
+ range="a whole number, typically hundreds to thousands.",
59
+ recommend="200 to screen; 2000 or more for a confirmatory or family-wise claim (the smallest possible p is 1/(B+1)).",
60
+ effect="higher gives a finer p-value and more confidence, at more compute."),
61
+ # B. your policy (the data cannot decide these)
62
+ dict(key="alpha", form="detection_alpha", group="B",
63
+ does="The critical-interval acceptance threshold: a screening gate deciding which candidates are detected, not the basis of inference.",
64
+ range="between 0 and 1, small (e.g. .005 to .05).",
65
+ recommend=".005, a convention in sport T-pattern work; the surrogate calibration, not this threshold, is what a claim rests on.",
66
+ effect="a looser value lets more candidates through to calibration, but does not change which patterns survive it."),
67
+ dict(key="q_target", form="false_discovery_q", group="B",
68
+ does="The false-discovery-rate threshold a pattern's q-value must clear.",
69
+ range="between 0 and 1 (e.g. .05).",
70
+ recommend=".05.",
71
+ effect="lower is stricter (fewer patterns reported)."),
72
+ dict(key="alpha_fwer", form="family_wise_alpha", group="B",
73
+ does="The family-wise error rate used for confirmatory claims (Holm).",
74
+ range="between 0 and 1 (e.g. .005).",
75
+ recommend=".005; a family-wise claim needs B in the thousands to resolve.",
76
+ effect="lower is stricter; too small a B cannot reach it."),
77
+ dict(key="min_samples_frac", form="bout_support_percent", group="B",
78
+ does="The minimum fraction of observations a pattern must appear in to be kept.",
79
+ range="0 to 100 percent; 0 is off.",
80
+ recommend="off. Report prevalence rather than filtering on it (Section 4.1 shows why a threshold cuts along the wrong axis).",
81
+ effect="raising it deletes rare-but-genuine patterns, including well-evidenced ones specific to a subgroup."),
82
+ dict(key="lumping_factor", form="lumping_factor", group="B",
83
+ does="If one event nearly always follows another, treat the pair as a single unit so the combinations do not explode.",
84
+ range="between 0 and 1; off when 0.",
85
+ recommend="off. The THEME manual illustrates it with 0.9 (a near-deterministic link).",
86
+ effect="a higher threshold lumps fewer pairs; used mainly to tame very dense data."),
87
+ dict(key="completeness", form="completeness_competition", group="B",
88
+ does="Drops a pattern whose every occurrence sits inside a longer detected pattern.",
89
+ range="on or off.",
90
+ recommend="on.",
91
+ effect="off keeps redundant sub-patterns and inflates the counts."),
92
+ dict(key="collapse_equivalent", form="collapse_equivalent_patterns", group="B",
93
+ does="Merges patterns with an identical set of occurrences (the two directions of a co-timed pair, or different bracketings of one chain) to one representative.",
94
+ range="on or off.",
95
+ recommend="on.",
96
+ effect="off double-counts perfectly-dependent duplicates and enlarges the multiple-comparison family."),
97
+ dict(key="include_univariate", form="include_univariate_patterns", group="B",
98
+ does="Keeps single-event-type entries (Level 0) in the output.",
99
+ range="on or off.",
100
+ recommend="on.",
101
+ effect="off drops the univariate baseline rows."),
102
+ dict(key="exclude_events", form="force_exclude_events", group="B",
103
+ does="An explicit list of event types to bar from building patterns, overriding the frequency rule.",
104
+ range="comma-separated type names, or empty.",
105
+ recommend="empty, so the frequency rule applies.",
106
+ effect="listing a type removes it from pattern-building whatever its frequency (used to reproduce a specific analysis)."),
107
+ # C. structural inputs (fixed before analysis, change every result)
108
+ dict(key="time_unit", form="time_unit", group="C",
109
+ does="The unit of the time columns in your file.",
110
+ range="s or ms.",
111
+ recommend="whatever your data uses; times are held internally in milliseconds.",
112
+ effect="sets the unit intervals are reported in; the wrong choice mis-scales every time."),
113
+ dict(key="obs_window", form="use_observation_window_columns", group="C",
114
+ does="Reads each observation's window from obs_start / obs_end columns instead of its first and last event.",
115
+ range="on or off.",
116
+ recommend="on when your observations have real bounds (a possession, rally or bout usually does).",
117
+ effect="off takes the window from the events, which shifts every NX/T baseline probability."),
118
+ dict(key="max_level", form="maximum_depth", group="C",
119
+ does="A cap on how deep the pattern hierarchy can go.",
120
+ range="a whole number, 1 or more.",
121
+ recommend="8; it is rarely reached.",
122
+ effect="lower yields fewer and simpler patterns and can prevent overload on dense data."),
123
+ dict(key="collapse_duplicates", form="same_instant_duplicates", group="C",
124
+ does="Collapses two records of the same event type at an identical timestamp to one point.",
125
+ range="on or off.",
126
+ recommend="on for finely-timed data (a double-entry is one point in time); off only on coarsely-timed data where two same-type events might genuinely share a coded time.",
127
+ effect="off keeps both records, inflating that type's baseline rate."),
128
+ dict(key="max_edges", form="candidate_edge_cap", group="C",
129
+ does="A cap on candidate window boundaries per pair (an approximate search for very dense data).",
130
+ range="a whole number, 0 or more; 0 means exact.",
131
+ recommend="0 (exact). Set it only on very dense data where the exact search does not finish.",
132
+ effect="lower is faster but makes the window search approximate rather than exhaustive."),
133
+ ]
134
+
135
+ # --- output columns ---------------------------------------------------------
136
+ COLUMNS: list[dict] = [
137
+ dict(key="pattern", does="The pattern as text (the signature is its canonical bracketed form)."),
138
+ dict(key="N", does="Number of occurrences of the pattern across all observations."),
139
+ dict(key="bouts", does="Number of distinct observations the pattern occurs in."),
140
+ dict(key="bout_support", does="bouts as a fraction of all observations (the pattern's prevalence)."),
141
+ dict(key="length", does="Number of event types in the pattern."),
142
+ dict(key="level", does="Hierarchy depth: 0 is a single event type, 1 a pair, higher levels longer chains."),
143
+ dict(key="loop", does="1 if the pattern repeats an event type, otherwise 0."),
144
+ dict(key="critical_interval", does="The accepted time window [d1, d2] within which the later event follows the earlier."),
145
+ dict(key="duration_median_ms", does="The observed timing of the occurrences: the median gap (with duration_iqr_ms giving the middle half), in milliseconds."),
146
+ dict(key="monte_carlo_p", does="The Monte-Carlo empirical p-value: the fraction of surrogate datasets in which the pattern is as strong or stronger."),
147
+ dict(key="fdr_q", does="The Benjamini-Hochberg false-discovery q-value (the screening decision)."),
148
+ dict(key="survives_fwer_holm", does="The Holm family-wise decision: 1 if the pattern survives family-wise control, 0 if not."),
149
+ ]
150
+
151
+ _GROUP = {"A": "The data can decide it (the advisor recommends)",
152
+ "B": "Your policy (the data cannot decide it)",
153
+ "C": "Structural input (fixed before analysis)"}
154
+
155
+
156
+ def explain(name: str | None = None) -> str:
157
+ """Plain-language reference for a setting or output column, or for everything.
158
+
159
+ ``explain("min_lag")`` (or its form name ``"minimum_lag_frames"``) describes one
160
+ setting; ``explain("fdr_q")`` an output column; ``explain()`` prints them all.
161
+ """
162
+ if name is None:
163
+ return explain_settings() + "\n\n" + explain_columns()
164
+ for s in SETTINGS:
165
+ if name in (s["key"], s["form"]):
166
+ return (f"{s['form']} (Config: {s['key']}) [{_GROUP[s['group']]}]\n"
167
+ f" what it does : {s['does']}\n"
168
+ f" range : {s['range']}\n"
169
+ f" recommended : {s['recommend']}\n"
170
+ f" higher/lower : {s['effect']}")
171
+ for c in COLUMNS:
172
+ if name == c["key"]:
173
+ return f"{c['key']} — {c['does']}"
174
+ return f"No setting or column named {name!r}. Try explain() to list them all."
175
+
176
+
177
+ def explain_settings() -> str:
178
+ out = ["SETTINGS"]
179
+ for g in ("A", "B", "C"):
180
+ out.append(f"\n[{g}] {_GROUP[g]}")
181
+ for s in SETTINGS:
182
+ if s["group"] == g:
183
+ out.append(f" {s['form']:28s} {s['does']}")
184
+ out.append(f" {'':28s} range: {s['range']} recommended: {s['recommend']}")
185
+ return "\n".join(out)
186
+
187
+
188
+ def explain_columns() -> str:
189
+ return "OUTPUT COLUMNS\n" + "\n".join(f" {c['key']:22s} {c['does']}" for c in COLUMNS)
190
+
191
+
192
+ def parameters_markdown() -> str:
193
+ """The full reference as Markdown (used to write PARAMETERS.md)."""
194
+ L = ["# tpattern parameters and outputs",
195
+ "",
196
+ "Every setting and output column, with what it does, its range, the recommended "
197
+ "value, and what higher or lower means. Generated from `tpattern/reference.py`, "
198
+ "which is also the source for the tutorial's tables, the Colab form labels and "
199
+ "`tpattern.explain()`.", ""]
200
+ for g in ("A", "B", "C"):
201
+ L += [f"## {_GROUP[g]}", "",
202
+ "| Setting (form name / `Config`) | What it does | Range | Recommended | Higher / lower |",
203
+ "|---|---|---|---|---|"]
204
+ for s in SETTINGS:
205
+ if s["group"] == g:
206
+ L.append(f"| **{s['form']}** / `{s['key']}` | {s['does']} | {s['range']} "
207
+ f"| {s['recommend']} | {s['effect']} |")
208
+ L.append("")
209
+ L += ["## Output columns", "",
210
+ "| Column | What it is |", "|---|---|"]
211
+ for c in COLUMNS:
212
+ L.append(f"| `{c['key']}` | {c['does']} |")
213
+ L.append("")
214
+ return "\n".join(L)
@@ -102,8 +102,8 @@ def patterns_table(source, outfile: str | None = None, ci_unit: str = "ms",
102
102
  """Build a results table from a list of `Pattern` or a `CalibrationResult`.
103
103
 
104
104
  Returns a list of row dicts. If `outfile` is given, also writes CSV.
105
- When given a CalibrationResult, includes p_emp, fdr_q, fwer_keep and sorts by
106
- p_emp; otherwise sorts by N (descending).
105
+ When given a CalibrationResult, includes monte_carlo_p, fdr_q, survives_fwer_holm and sorts by
106
+ monte_carlo_p; otherwise sorts by N (descending).
107
107
 
108
108
  Every row carries `bouts` (distinct observations the pattern spans) and
109
109
  `bout_support` (that as a fraction of the sample). Prevalence is reported rather
@@ -141,19 +141,23 @@ def patterns_table(source, outfile: str | None = None, ci_unit: str = "ms",
141
141
  "duration_range_ms": f"[{dur['min']},{dur['max']}]" if dur else None,
142
142
  }
143
143
  if calibrated:
144
- row["p_emp"] = round(it.p_emp, 4)
144
+ # readable column names (see tpattern/reference.py); the internal
145
+ # Calibrated attributes (p_emp, fwer_keep) keep their names.
146
+ row["monte_carlo_p"] = round(it.p_emp, 4)
145
147
  row["fdr_q"] = round(it.fdr_q, 4)
146
- row["fwer_keep"] = int(it.fwer_keep)
148
+ row["survives_fwer_holm"] = int(it.fwer_keep)
147
149
  row["analytic_strength"] = round(it.strength, 2) # -log10(analytic p); comparison only
148
150
  row["interpretation"] = interpret(p, it.N, it.fdr_q, ci_unit=ci_unit, dur=dur)
149
151
  rows.append(row)
150
152
 
151
153
  key = sort
152
154
  if sort == "auto":
153
- key = "p_emp" if calibrated else "N"
155
+ key = "monte_carlo_p" if calibrated else "N"
156
+ if key in ("p_emp", "fwer_keep"): # tolerate the old names
157
+ key = {"p_emp": "monte_carlo_p", "fwer_keep": "survives_fwer_holm"}[key]
154
158
  if key == "N":
155
159
  rows.sort(key=lambda r: (-r["N"], r["level"]))
156
- elif key in ("p_emp", "fdr_q") and calibrated:
160
+ elif key in ("monte_carlo_p", "fdr_q") and calibrated:
157
161
  rows.sort(key=lambda r: (r[key], -r["N"]))
158
162
 
159
163
  if outfile:
@@ -400,6 +404,6 @@ def report(source, outdir: str, *, ci_unit: str = "ms", title: str = "T-pattern
400
404
  for r in rows[:15]:
401
405
  line = f" N={r['N']:>3} L{r['level']} {r['pattern']}"
402
406
  if calibrated:
403
- line += f" p_emp={r['p_emp']} q={r['fdr_q']}"
407
+ line += f" monte_carlo_p={r['monte_carlo_p']} q={r['fdr_q']}"
404
408
  fh.write(line + "\n")
405
409
  return written
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tpattern
3
- Version: 0.1.11
3
+ Version: 0.1.12
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
@@ -8,6 +8,7 @@ tests/test_guided.py
8
8
  tests/test_null_compare.py
9
9
  tests/test_outputs.py
10
10
  tests/test_read_table_validation.py
11
+ tests/test_reference.py
11
12
  tests/test_report_dendrogram.py
12
13
  tests/test_reproduction.py
13
14
  tests/test_sofcoder.py
@@ -24,6 +25,7 @@ tpattern/io.py
24
25
  tpattern/methods.py
25
26
  tpattern/pattern.py
26
27
  tpattern/randomise.py
28
+ tpattern/reference.py
27
29
  tpattern/report.py
28
30
  tpattern/significance.py
29
31
  tpattern/synthetic.py
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