processbehavior 0.2.0__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.
Files changed (56) hide show
  1. processbehavior/__init__.py +133 -0
  2. processbehavior/analysis.py +2944 -0
  3. processbehavior/analysis_dataset.py +361 -0
  4. processbehavior/analysis_result.py +1276 -0
  5. processbehavior/calibration.py +70 -0
  6. processbehavior/capability.py +720 -0
  7. processbehavior/data_preparation.py +792 -0
  8. processbehavior/datasets/__init__.py +37 -0
  9. processbehavior/datasets/data/coffee_shop_demo_long.csv +385 -0
  10. processbehavior/datasets/loaders.py +66 -0
  11. processbehavior/datasets/synthetic.py +1711 -0
  12. processbehavior/derivations.py +602 -0
  13. processbehavior/effects_calculator.py +603 -0
  14. processbehavior/exceptions.py +216 -0
  15. processbehavior/exporters/__init__.py +5 -0
  16. processbehavior/exporters/excel_exporter.py +708 -0
  17. processbehavior/focused_result.py +329 -0
  18. processbehavior/formulation_spec.py +144 -0
  19. processbehavior/loss_function.py +517 -0
  20. processbehavior/maximum_information.py +285 -0
  21. processbehavior/plotting/__init__.py +46 -0
  22. processbehavior/plotting/_math.py +121 -0
  23. processbehavior/plotting/capability_chart.py +858 -0
  24. processbehavior/plotting/contracts.py +176 -0
  25. processbehavior/plotting/control_chart.py +206 -0
  26. processbehavior/plotting/effects_charts.py +640 -0
  27. processbehavior/plotting/lane_boundaries.py +78 -0
  28. processbehavior/plotting/limits.py +157 -0
  29. processbehavior/plotting/maximum_information_chart.py +395 -0
  30. processbehavior/plotting/plotter.py +1469 -0
  31. processbehavior/plotting/renderers.py +660 -0
  32. processbehavior/plotting/report.py +211 -0
  33. processbehavior/plotting/residuals.py +282 -0
  34. processbehavior/plotting/run_rules_viz.py +150 -0
  35. processbehavior/plotting/stats_box.py +183 -0
  36. processbehavior/plotting/themes.py +544 -0
  37. processbehavior/plotting/x_axis_layout.py +474 -0
  38. processbehavior/plotting/zones.py +127 -0
  39. processbehavior/process_behavior.py +1290 -0
  40. processbehavior/py.typed +0 -0
  41. processbehavior/residual_calculator.py +572 -0
  42. processbehavior/result_signals.py +149 -0
  43. processbehavior/result_tabular.py +296 -0
  44. processbehavior/sds_detector.py +1432 -0
  45. processbehavior/signals/__init__.py +14 -0
  46. processbehavior/signals/config.py +435 -0
  47. processbehavior/signals/detector.py +328 -0
  48. processbehavior/signals/detectors.py +368 -0
  49. processbehavior/signals/result.py +232 -0
  50. processbehavior/spc_constants.py +692 -0
  51. processbehavior/study.py +2733 -0
  52. processbehavior/types.py +154 -0
  53. processbehavior-0.2.0.dist-info/METADATA +279 -0
  54. processbehavior-0.2.0.dist-info/RECORD +56 -0
  55. processbehavior-0.2.0.dist-info/WHEEL +4 -0
  56. processbehavior-0.2.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,183 @@
1
+ """Statistics box rendering for control charts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ import plotly.graph_objects as go
9
+
10
+ if TYPE_CHECKING:
11
+ import pandas as pd
12
+
13
+ from .themes import ChartTheme
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def format_stat_value(value: float, compact: bool = False) -> str:
19
+ """
20
+ Format a statistic value for display.
21
+
22
+ Parameters
23
+ ----------
24
+ value : float
25
+ The statistic value to format
26
+ compact : bool
27
+ If True, use fewer decimal places for compact display
28
+
29
+ Returns
30
+ -------
31
+ str
32
+ Formatted value string
33
+ """
34
+ if compact:
35
+ if abs(value) >= 100:
36
+ return f'{value:.0f}'
37
+ elif abs(value) >= 10:
38
+ return f'{value:.1f}'
39
+ else:
40
+ return f'{value:.2f}'
41
+ else:
42
+ if abs(value) >= 100:
43
+ return f'{value:.2f}'
44
+ elif abs(value) >= 10:
45
+ return f'{value:.3f}'
46
+ else:
47
+ return f'{value:.4f}'
48
+
49
+
50
+ def build_stats_text(stats: dict, data: pd.DataFrame, compact: bool = False) -> str | None:
51
+ """
52
+ Build statistics text for display in a stats box.
53
+
54
+ Parameters
55
+ ----------
56
+ stats : dict
57
+ Chart statistics with 'center', 'upl', 'lpl' keys
58
+ data : DataFrame
59
+ Chart data (for calculating n)
60
+ compact : bool, default False
61
+ If True, use compact format (n=X | CL=Y) for faceted charts.
62
+ If False, use full format with line breaks.
63
+
64
+ Returns
65
+ -------
66
+ str or None
67
+ Formatted stats text, or None if no stats available
68
+ """
69
+ n = len(data)
70
+
71
+ if compact:
72
+ parts = [f'n={n}']
73
+ center = stats.get('center')
74
+ if center is not None:
75
+ parts.append(f'CL={format_stat_value(center, compact=True)}')
76
+ return ' | '.join(parts) if parts else None
77
+ else:
78
+ lines = [f'n = {n}']
79
+
80
+ center = stats.get('center')
81
+ if center is not None:
82
+ lines.append(f'CL = {format_stat_value(center)}')
83
+
84
+ ucl = stats.get('upl')
85
+ if ucl is not None:
86
+ lines.append(f'UPL = {format_stat_value(ucl)}')
87
+
88
+ lcl = stats.get('lpl')
89
+ if lcl is not None:
90
+ lines.append(f'LPL = {format_stat_value(lcl)}')
91
+
92
+ return '<br>'.join(lines) if lines else None
93
+
94
+
95
+ def add_stats_box(
96
+ fig: go.Figure,
97
+ stats: dict,
98
+ data: pd.DataFrame,
99
+ theme: ChartTheme,
100
+ row: int | None = None,
101
+ col: int | None = None,
102
+ nrows: int | None = None,
103
+ ncols: int | None = None,
104
+ ) -> None:
105
+ """
106
+ Add a statistics box annotation to a chart.
107
+
108
+ Unified function for both single charts and faceted subplots.
109
+ When row/col are None, positions at paper (0.02, 0.98) with full text.
110
+ When provided, calculates subplot-relative position with compact text.
111
+
112
+ Parameters
113
+ ----------
114
+ fig : go.Figure
115
+ Plotly figure to add annotation to
116
+ stats : dict
117
+ Chart statistics with 'center', 'upl', 'lpl' keys
118
+ data : DataFrame
119
+ Chart data (for calculating n)
120
+ theme : ChartTheme
121
+ Theme with stats box styling
122
+ row : int, optional
123
+ Row number of subplot (1-indexed). None for single charts.
124
+ col : int, optional
125
+ Column number of subplot (1-indexed). None for single charts.
126
+ nrows : int, optional
127
+ Total number of rows in the facet grid. Required when row/col are set.
128
+ ncols : int, optional
129
+ Total number of columns in the facet grid. Required when row/col are set.
130
+ """
131
+ if row is not None and col is not None:
132
+ if not ncols or not nrows or ncols <= 0 or nrows <= 0:
133
+ logger.debug('Invalid grid dimensions (nrows=%s, ncols=%s); skipping stats box', nrows, ncols)
134
+ return
135
+
136
+ # Faceted: compact text, subplot-relative position, smaller font
137
+ stats_text = build_stats_text(stats, data, compact=True)
138
+ if stats_text is None:
139
+ return
140
+
141
+ col_width = 1.0 / ncols
142
+ row_height = 1.0 / nrows
143
+ x_pos = (col - 1) * col_width + 0.02 * col_width
144
+ y_pos = 1.0 - (row - 1) * row_height - 0.05 * row_height
145
+
146
+ fig.add_annotation(
147
+ text=stats_text,
148
+ xref='paper',
149
+ yref='paper',
150
+ x=x_pos,
151
+ y=y_pos,
152
+ xanchor='left',
153
+ yanchor='top',
154
+ showarrow=False,
155
+ font=dict(size=theme.stats_box_font_size - 1, color=theme.stats_box_font_color, family='monospace'),
156
+ bgcolor=theme.stats_box_bgcolor,
157
+ bordercolor=theme.stats_box_bordercolor,
158
+ borderwidth=theme.stats_box_borderwidth,
159
+ borderpad=3,
160
+ align='left',
161
+ )
162
+ else:
163
+ # Single chart: full text, fixed position, standard font
164
+ stats_text = build_stats_text(stats, data, compact=False)
165
+ if stats_text is None:
166
+ return
167
+
168
+ fig.add_annotation(
169
+ text=stats_text,
170
+ xref='paper',
171
+ yref='paper',
172
+ x=0.02,
173
+ y=0.98,
174
+ xanchor='left',
175
+ yanchor='top',
176
+ showarrow=False,
177
+ font=dict(size=theme.stats_box_font_size, color=theme.stats_box_font_color, family='monospace'),
178
+ bgcolor=theme.stats_box_bgcolor,
179
+ bordercolor=theme.stats_box_bordercolor,
180
+ borderwidth=theme.stats_box_borderwidth,
181
+ borderpad=6,
182
+ align='left',
183
+ )