priorityx 0.2.1__tar.gz → 0.2.3__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. {priorityx-0.2.1 → priorityx-0.2.3}/PKG-INFO +2 -1
  2. {priorityx-0.2.1 → priorityx-0.2.3}/README.md +1 -0
  3. {priorityx-0.2.1 → priorityx-0.2.3}/docs/api-reference.md +14 -7
  4. {priorityx-0.2.1 → priorityx-0.2.3}/docs/quickstart.md +6 -1
  5. {priorityx-0.2.1 → priorityx-0.2.3}/examples/README.md +3 -4
  6. {priorityx-0.2.1 → priorityx-0.2.3}/examples/bugs/bugs_monitoring.py +8 -8
  7. {priorityx-0.2.1 → priorityx-0.2.3}/examples/incidents/incident_monitoring.py +10 -12
  8. {priorityx-0.2.1 → priorityx-0.2.3}/examples/violations/violations_monitoring.py +10 -19
  9. {priorityx-0.2.1 → priorityx-0.2.3}/pyproject.toml +3 -1
  10. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/__init__.py +1 -1
  11. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/core/glmm.py +17 -17
  12. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/tracking/drivers.py +273 -212
  13. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/tracking/transitions.py +28 -1
  14. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/utils/helpers.py +3 -3
  15. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/viz/matrix.py +22 -7
  16. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/viz/timeline.py +21 -6
  17. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/viz/trajectory.py +27 -12
  18. priorityx-0.2.3/tests/test_drivers.py +243 -0
  19. {priorityx-0.2.1 → priorityx-0.2.3}/uv.lock +577 -345
  20. priorityx-0.2.1/src/priorityx/viz/__init__.py +0 -0
  21. priorityx-0.2.1/tests/test_drivers.py +0 -118
  22. {priorityx-0.2.1 → priorityx-0.2.3}/.github/workflows/release.yml +0 -0
  23. {priorityx-0.2.1 → priorityx-0.2.3}/.github/workflows/test.yml +0 -0
  24. {priorityx-0.2.1 → priorityx-0.2.3}/.gitignore +0 -0
  25. {priorityx-0.2.1 → priorityx-0.2.3}/LICENSE +0 -0
  26. {priorityx-0.2.1 → priorityx-0.2.3}/Makefile +0 -0
  27. {priorityx-0.2.1 → priorityx-0.2.3}/docs/methodology.md +0 -0
  28. {priorityx-0.2.1 → priorityx-0.2.3}/docs/priority_classification.md +0 -0
  29. {priorityx-0.2.1 → priorityx-0.2.3}/scripts/version.py +0 -0
  30. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/core/__init__.py +0 -0
  31. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/core/filters.py +0 -0
  32. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/core/quadrants.py +0 -0
  33. {priorityx-0.2.1/src/priorityx/drivers → priorityx-0.2.3/src/priorityx/tracking}/__init__.py +0 -0
  34. {priorityx-0.2.1 → priorityx-0.2.3}/src/priorityx/tracking/movement.py +0 -0
  35. {priorityx-0.2.1/src/priorityx/tracking → priorityx-0.2.3/src/priorityx/utils}/__init__.py +0 -0
  36. {priorityx-0.2.1/src/priorityx/utils → priorityx-0.2.3/src/priorityx/viz}/__init__.py +0 -0
  37. {priorityx-0.2.1 → priorityx-0.2.3}/tests/test_glmm.py +0 -0
  38. {priorityx-0.2.1 → priorityx-0.2.3}/tests/test_quadrants.py +0 -0
  39. {priorityx-0.2.1 → priorityx-0.2.3}/tests/test_transitions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: priorityx
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Entity prioritization and escalation detection using GLMM statistical models
5
5
  Project-URL: Homepage, https://github.com/okkymabruri/priorityx
6
6
  Project-URL: Repository, https://github.com/okkymabruri/priorityx
@@ -74,6 +74,7 @@ plot_priority_matrix(results, entity_name="Service", save_plot=True)
74
74
  - GLMM-based priority matrix (Q1–Q4) with entity-level intercept/slope insights
75
75
  - Priority-based transition timeline (Crisis / Investigate / Monitor / Low) with spike markers (`*X`, `*Y`, `*XY`)
76
76
  - Cumulative movement tracking and trajectory visualizations
77
+ - Transition driver analysis that surfaces top subcategories causing quadrant shifts with spike summaries
77
78
  - Deterministic seeding option for reproducible GLMM runs (set `PRIORITYX_GLMM_SEED`)
78
79
 
79
80
  ## Use Cases
@@ -46,6 +46,7 @@ plot_priority_matrix(results, entity_name="Service", save_plot=True)
46
46
  - GLMM-based priority matrix (Q1–Q4) with entity-level intercept/slope insights
47
47
  - Priority-based transition timeline (Crisis / Investigate / Monitor / Low) with spike markers (`*X`, `*Y`, `*XY`)
48
48
  - Cumulative movement tracking and trajectory visualizations
49
+ - Transition driver analysis that surfaces top subcategories causing quadrant shifts with spike summaries
49
50
  - Deterministic seeding option for reproducible GLMM runs (set `PRIORITYX_GLMM_SEED`)
50
51
 
51
52
  ## Use Cases
@@ -100,8 +100,9 @@ analysis = extract_transition_drivers(
100
100
  quarter_to="2024-Q3",
101
101
  entity_col="service",
102
102
  timestamp_col="date",
103
- subcategory_cols=["type", "category"], # Optional
104
- text_col="description" # Optional
103
+ subcategory_cols=["type", "category"], # Optional override; auto-detected if omitted
104
+ top_n_subcategories=5,
105
+ min_subcategory_delta=2,
105
106
  )
106
107
  ```
107
108
 
@@ -115,11 +116,18 @@ Analyzes root causes of a quadrant transition.
115
116
  - `quarter_to`: Ending quarter (e.g., "2024-Q3")
116
117
  - `entity_col`: Entity column name
117
118
  - `timestamp_col`: Timestamp column name
118
- - `subcategory_cols`: Optional list of subcategory columns
119
- - `text_col`: Optional text column for keyword analysis
119
+ - `subcategory_cols`: Optional list of subcategory columns (auto-detected when omitted)
120
+ - `top_n_subcategories`: Limit of subcategory drivers (default: 3)
121
+ - `min_subcategory_delta`: Minimum delta required for subcategory inclusion (default: 1)
120
122
 
121
123
  **Returns:**
122
- - Dictionary with transition overview, magnitude metrics, subcategory drivers, keyword drivers, and priority classification
124
+ - Dictionary with:
125
+ - `transition`: includes risk-level change and `is_concerning` flag
126
+ - `magnitude`: cumulative deltas plus period-specific weekly averages and complaint counts
127
+ - `spike_drivers`: summary notes aligned with spike indicators (`*X`, `*Y`, `*XY`)
128
+ - `subcategory_drivers`: per-column driver lists obeying the provided knobs
129
+ - `priority`: priority tier, trigger reason, spike axis
130
+ - `meta`: diagnostic flags (e.g., whether subcategory columns were auto-detected)
123
131
 
124
132
  ### classify_priority
125
133
 
@@ -222,11 +230,10 @@ Prints formatted summaries of analysis results.
222
230
  ```python
223
231
  from priorityx.tracking.drivers import display_transition_drivers
224
232
 
225
- display_transition_drivers(analysis, show_keywords=True)
233
+ display_transition_drivers(analysis)
226
234
  ```
227
235
 
228
236
  Prints transition driver analysis in human-readable format.
229
237
 
230
238
  **Parameters:**
231
239
  - `analysis`: Output from extract_transition_drivers()
232
- - `show_keywords`: Whether to show keyword analysis (default: False)
@@ -62,9 +62,14 @@ if not transitions.empty:
62
62
  quarter_from=first_transition["from_quarter"],
63
63
  quarter_to=first_transition["transition_quarter"],
64
64
  entity_col="service",
65
- timestamp_col="date"
65
+ timestamp_col="date",
66
+ top_n_subcategories=5,
67
+ min_subcategory_delta=2
66
68
  )
67
69
  display_transition_drivers(analysis)
70
+
71
+ # subcategory columns (e.g., severity/product/topic) are auto-detected
72
+ # when not provided; pass subcategory_cols=[...] to override.
68
73
  ```
69
74
 
70
75
  ## Data Requirements
@@ -16,18 +16,17 @@ pip install git+https://github.com/okkymabruri/priorityx.git
16
16
 
17
17
  ## Running Examples
18
18
 
19
- **IT Incidents** (1,621 incidents, 10 services)
19
+ **IT Incidents** (1,621 incidents, 10 services) — pulls CSV from GitHub raw
20
20
  ```bash
21
21
  python examples/incidents/incident_monitoring.py
22
22
  ```
23
23
 
24
- **Compliance Violations** (495 violations, 10 departments)
24
+ **Compliance Violations** (495 violations, 10 departments) — synthetic dataset
25
25
  ```bash
26
26
  python examples/violations/violations_monitoring.py
27
27
  ```
28
28
 
29
- **Software Bugs** (576 bugs, 10 components)
30
- Pre-generated CSV only. Use as input for your own analysis.
29
+ **Software Bugs** (576 bugs, 10 components) — pulls CSV from GitHub raw
31
30
 
32
31
  ## Output
33
32
 
@@ -1,19 +1,20 @@
1
- # software bug tracking example
2
-
1
+ # %%
2
+ # software bug tracking example (data pulled from GitHub raw)
3
3
  import pandas as pd
4
4
 
5
5
  from priorityx.core.glmm import fit_priority_matrix
6
6
  from priorityx.viz.matrix import plot_priority_matrix
7
7
 
8
- # load bug reports
9
- df = pd.read_csv("examples/bugs/bugs.csv")
8
+ RAW_DATA_URL = "https://raw.githubusercontent.com/okkymabruri/priorityx/main/examples/bugs/bugs.csv"
10
9
 
11
- # parse date column
12
- df["date"] = pd.to_datetime(df["reported_date"])
10
+ # load bug reports from GitHub raw
11
+ df = pd.read_csv(RAW_DATA_URL, parse_dates=["reported_date"])
12
+ df["date"] = df["reported_date"]
13
13
 
14
14
  # fit priority matrix
15
15
  temporal_granularity = "quarterly"
16
16
  entity_name = "Component"
17
+
17
18
  results, stats = fit_priority_matrix(
18
19
  df,
19
20
  entity_col="component",
@@ -33,9 +34,8 @@ plot_priority_matrix(
33
34
  show_quadrant_labels=True,
34
35
  save_plot=True,
35
36
  save_csv=True,
36
- output_dir="examples/bugs/plot",
37
37
  )
38
38
  print()
39
- print("Outputs saved to examples/bugs/plot/ and examples/bugs/results/")
39
+ print("Outputs saved to plot/ and results/ (under the current working directory)")
40
40
 
41
41
  # %%
@@ -1,7 +1,10 @@
1
- # it incident monitoring example
1
+ # %%
2
+ # it incident monitoring example (data pulled from GitHub raw)
2
3
 
3
4
  import pandas as pd
4
5
 
6
+ RAW_DATA_URL = "https://raw.githubusercontent.com/okkymabruri/priorityx/main/examples/incidents/incidents.csv"
7
+
5
8
  from priorityx.core.glmm import fit_priority_matrix
6
9
  from priorityx.tracking.movement import track_cumulative_movement
7
10
  from priorityx.tracking.transitions import extract_transitions
@@ -18,9 +21,9 @@ from priorityx.utils.helpers import (
18
21
  save_dataframe_to_csv,
19
22
  )
20
23
 
21
- # load data
22
- df = pd.read_csv("examples/incidents/incidents.csv")
23
- df["date"] = pd.to_datetime(df["date"])
24
+ # load data from GitHub raw
25
+ df = pd.read_csv(RAW_DATA_URL, parse_dates=["date"])
26
+ print("Loaded incidents from GitHub raw URL")
24
27
 
25
28
  print(f"Loaded {len(df)} incidents for {df['service'].nunique()} services")
26
29
  print(f"Date range: {df['date'].min()} to {df['date'].max()}")
@@ -52,7 +55,6 @@ plot_priority_matrix(
52
55
  show_quadrant_labels=True,
53
56
  save_plot=True,
54
57
  save_csv=True,
55
- output_dir="examples/incidents/plot",
56
58
  )
57
59
 
58
60
  # track movement
@@ -78,7 +80,6 @@ movement_path = save_dataframe_to_csv(
78
80
  artifact="trajectories",
79
81
  entity_name=entity_name,
80
82
  temporal_granularity=temporal_granularity,
81
- output_dir="examples/incidents/results",
82
83
  )
83
84
  print(f"Movement CSV saved: {movement_path}")
84
85
 
@@ -89,8 +90,6 @@ plot_entity_trajectories(
89
90
  max_entities=5,
90
91
  save_plot=True,
91
92
  save_csv=True,
92
- output_dir="examples/incidents/plot",
93
- results_dir="examples/incidents/results",
94
93
  )
95
94
 
96
95
  # detect transitions
@@ -104,7 +103,6 @@ plot_transition_timeline(
104
103
  entity_name=entity_name,
105
104
  save_plot=True,
106
105
  save_csv=True,
107
- output_dir="examples/incidents/plot",
108
106
  movement_df=movement,
109
107
  )
110
108
 
@@ -139,13 +137,13 @@ if len(critical_transitions) > 0:
139
137
  quarter_to=trans["transition_quarter"],
140
138
  entity_col="service",
141
139
  timestamp_col="date",
140
+ top_n_subcategories=5,
141
+ min_subcategory_delta=2,
142
142
  )
143
143
 
144
144
  display_transition_drivers(driver_analysis)
145
145
 
146
146
  print()
147
- print(
148
- "Analysis complete. Check examples/incidents/plot/ and examples/incidents/results/"
149
- )
147
+ print("Analysis complete. Check plot/ and results/ in the current working directory.")
150
148
 
151
149
  # %%
@@ -1,4 +1,4 @@
1
- import os
1
+ # %%
2
2
  import random
3
3
  from datetime import datetime, timedelta
4
4
 
@@ -12,6 +12,7 @@ from priorityx.viz.timeline import plot_transition_timeline
12
12
  from priorityx.utils.helpers import (
13
13
  display_quadrant_summary,
14
14
  display_transition_summary,
15
+ save_dataframe_to_csv,
15
16
  )
16
17
 
17
18
  random.seed(42)
@@ -63,11 +64,6 @@ df = pd.DataFrame(data)
63
64
  temporal_granularity = "quarterly"
64
65
  entity_name = "Department"
65
66
 
66
- PLOT_DIR = "examples/violations/plot"
67
- RESULTS_DIR = "examples/violations/results"
68
- os.makedirs(PLOT_DIR, exist_ok=True)
69
- os.makedirs(RESULTS_DIR, exist_ok=True)
70
-
71
67
  results, stats = fit_priority_matrix(
72
68
  df,
73
69
  entity_col="department",
@@ -86,7 +82,6 @@ plot_priority_matrix(
86
82
  show_quadrant_labels=True,
87
83
  save_plot=True,
88
84
  save_csv=True,
89
- output_dir=PLOT_DIR,
90
85
  )
91
86
 
92
87
  movement, meta = track_cumulative_movement(
@@ -98,17 +93,12 @@ movement, meta = track_cumulative_movement(
98
93
  temporal_granularity=temporal_granularity,
99
94
  )
100
95
 
101
- timestamp = datetime.now().strftime("%Y%m%d")
102
- granularity_suffix = {
103
- "quarterly": "Q",
104
- "yearly": "Y",
105
- "semiannual": "S",
106
- }.get(temporal_granularity, "Q")
107
- movement_path = (
108
- f"{RESULTS_DIR}/trajectories-{entity_name.lower()}-"
109
- f"{granularity_suffix}-{timestamp}.csv"
96
+ movement_path = save_dataframe_to_csv(
97
+ movement,
98
+ artifact="trajectories",
99
+ entity_name=entity_name,
100
+ temporal_granularity=temporal_granularity,
110
101
  )
111
- movement.to_csv(movement_path, index=False)
112
102
  print(f"Movement CSV saved: {movement_path}")
113
103
  transitions = extract_transitions(movement, focus_risk_increasing=True)
114
104
 
@@ -120,9 +110,10 @@ plot_transition_timeline(
120
110
  entity_name=entity_name,
121
111
  save_plot=True,
122
112
  save_csv=True,
123
- output_dir=PLOT_DIR,
124
113
  movement_df=movement,
125
114
  )
126
115
 
127
116
  print()
128
- print("Outputs saved to examples/violations/plot/ and examples/violations/results/")
117
+ print("Outputs saved to plot/ and results/ in the current working directory.")
118
+
119
+ # %%
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "priorityx"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  description = "Entity prioritization and escalation detection using GLMM statistical models"
5
5
  authors = [
6
6
  {name = "Okky Mabruri", email = "okkymbrur@gmail.com"}
@@ -39,6 +39,8 @@ build-backend = "hatchling.build"
39
39
  [dependency-groups]
40
40
  dev = [
41
41
  "jupyter>=1.1.1",
42
+ "jupytext>=1.18.1",
43
+ "mssql-python>=1.0.0",
42
44
  "polars>=1.35.1",
43
45
  "priorityx",
44
46
  "pyarrow>=22.0.0",
@@ -1,6 +1,6 @@
1
1
  """priorityx: Entity prioritization and escalation detection using GLMM statistical models."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.3"
4
4
 
5
5
  # API will be imported
6
6
 
@@ -3,6 +3,7 @@
3
3
  from typing import Dict, Literal, Optional, Tuple
4
4
  from datetime import timedelta
5
5
  import os
6
+ import warnings
6
7
  import numpy as np
7
8
 
8
9
  import pandas as pd
@@ -71,6 +72,7 @@ def fit_priority_matrix(
71
72
  temporal_granularity: Literal["yearly", "quarterly", "semiannual"] = "yearly",
72
73
  vcp_p: float = DEFAULT_VCP_P,
73
74
  fe_p: float = DEFAULT_FE_P,
75
+ verbose: bool = False,
74
76
  ) -> Tuple[pd.DataFrame, Dict]:
75
77
  """
76
78
  Fit Poisson GLMM to classify entities into priority quadrants.
@@ -87,6 +89,7 @@ def fit_priority_matrix(
87
89
  temporal_granularity: Time aggregation level
88
90
  vcp_p: Random effects prior scale
89
91
  fe_p: Fixed effects prior scale
92
+ verbose: When True, print diagnostic logs; otherwise stay quiet
90
93
 
91
94
  Returns:
92
95
  Tuple of (results DataFrame, statistics dict)
@@ -94,6 +97,11 @@ def fit_priority_matrix(
94
97
  # create copy to avoid modifying original
95
98
  df = df.copy()
96
99
 
100
+ if not verbose:
101
+ warnings.filterwarnings(
102
+ "ignore", message="VB fitting did not converge", category=UserWarning
103
+ )
104
+
97
105
  # ensure timestamp column is datetime
98
106
  df[timestamp_col] = pd.to_datetime(df[timestamp_col])
99
107
 
@@ -129,7 +137,7 @@ def fit_priority_matrix(
129
137
  df = df[df[entity_col].isin(valid_entities)]
130
138
  n_after_volume_filter = df[entity_col].nunique()
131
139
  n_filtered_volume = n_before_volume_filter - n_after_volume_filter
132
- if n_filtered_volume > 0:
140
+ if verbose and n_filtered_volume > 0:
133
141
  print(
134
142
  f" Filtered {n_filtered_volume} entities (<{min_total_count} total count)"
135
143
  )
@@ -308,22 +316,14 @@ def fit_priority_matrix(
308
316
  fe_p=fe_p,
309
317
  )
310
318
 
311
- # DIAGNOSTIC LOGGING
312
- print(f"\n[GLMM DEBUG] Formula: {formula}")
313
- print(f"[GLMM DEBUG] N observations: {len(df_prepared)}")
314
- print(f"[GLMM DEBUG] N entities: {df_prepared[entity_col].nunique()}")
315
- print(f"[GLMM DEBUG] Year range: {df_prepared['year'].min()}-{df_prepared['year'].max()}")
316
- print(f"[GLMM DEBUG] Time variable: mean={df_prepared['time'].mean():.6f}, std={df_prepared['time'].std():.6f}")
317
- print(f"[GLMM DEBUG] Entity sample: {df_prepared[entity_col].unique()[:5].tolist()}")
318
- print(f"[GLMM DEBUG] vcp_p={vcp_p}, fe_p={fe_p}")
319
-
320
- # Print first entity's data
321
- first_entity = df_prepared[entity_col].iloc[0]
322
- entity_data = df_prepared[df_prepared[entity_col] == first_entity]
323
- print(f"\n[GLMM DEBUG] First entity '{first_entity}' data:")
324
- debug_cols = [c for c in ["year", "quarter", "semester", entity_col, "count", "time"] if c in entity_data.columns]
325
- print(entity_data[debug_cols].head(10).to_string())
326
-
319
+ if verbose:
320
+ print(f"\n[GLMM DEBUG] Formula: {formula}")
321
+ print(f"[GLMM DEBUG] N observations: {len(df_prepared)}")
322
+ print(f"[GLMM DEBUG] N entities: {df_prepared[entity_col].nunique()}")
323
+ print(f"[GLMM DEBUG] Year range: {df_prepared['year'].min()}-{df_prepared['year'].max()}")
324
+ print(f"[GLMM DEBUG] Time variable: mean={df_prepared['time'].mean():.6f}, std={df_prepared['time'].std():.6f}")
325
+ print(f"[GLMM DEBUG] Entity sample: {df_prepared[entity_col].unique()[:5].tolist()}")
326
+ print(f"[GLMM DEBUG] vcp_p={vcp_p}, fe_p={fe_p}")
327
327
  # use variational bayes (returns posterior mean)
328
328
  # avoids boundary convergence issues vs map
329
329
  _apply_random_seed()