pccpy 0.4.3__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.
- pccpy-0.4.3.dist-info/METADATA +319 -0
- pccpy-0.4.3.dist-info/RECORD +22 -0
- pccpy-0.4.3.dist-info/WHEEL +5 -0
- pccpy-0.4.3.dist-info/licenses/LICENSE +21 -0
- pccpy-0.4.3.dist-info/top_level.txt +1 -0
- spyc/__init__.py +89 -0
- spyc/_constants.py +101 -0
- spyc/_data.py +106 -0
- spyc/_sigma.py +88 -0
- spyc/capability.py +395 -0
- spyc/charts/__init__.py +24 -0
- spyc/charts/_engine.py +102 -0
- spyc/charts/advanced.py +497 -0
- spyc/charts/attributes.py +122 -0
- spyc/charts/timeweighted.py +108 -0
- spyc/charts/variables.py +148 -0
- spyc/multivariate.py +542 -0
- spyc/normality.py +78 -0
- spyc/plotting.py +227 -0
- spyc/quality_tools.py +60 -0
- spyc/results.py +181 -0
- spyc/rules.py +169 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pccpy
|
|
3
|
+
Version: 0.4.3
|
|
4
|
+
Summary: Control Estadístico de Procesos (SPC) en Python: cartas de control, capacidad y pruebas al estilo Minitab
|
|
5
|
+
Author-email: LeoSanta15 <angelsanta1@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/LeoSanta15/pccpy
|
|
8
|
+
Project-URL: Issues, https://github.com/LeoSanta15/pccpy/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/LeoSanta15/pccpy/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: spc,control estadístico de procesos,cartas de control,capacidad,minitab,six sigma
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.22
|
|
24
|
+
Requires-Dist: scipy>=1.9
|
|
25
|
+
Requires-Dist: pandas>=1.4
|
|
26
|
+
Requires-Dist: matplotlib>=3.5
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: sphinx>=7; extra == "docs"
|
|
34
|
+
Requires-Dist: sphinx-rtd-theme>=2; extra == "docs"
|
|
35
|
+
Requires-Dist: myst-parser>=2; extra == "docs"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# spyc
|
|
39
|
+
|
|
40
|
+
**Control Estadístico de Procesos (SPC) en Python, al estilo Minitab.**
|
|
41
|
+
|
|
42
|
+
Cartas de control, pruebas de causas especiales, análisis de capacidad, prueba de
|
|
43
|
+
normalidad y Pareto, con salidas y convenciones (LCS/LCI, LEI/LES, Cp/Cpk/Pp/Ppk,
|
|
44
|
+
Z.Bench, PPM, Anderson-Darling) pensadas para quien viene de Minitab.
|
|
45
|
+
|
|
46
|
+
Todo en español: nombres de columnas, mensajes, descripciones de pruebas y gráficos.
|
|
47
|
+
|
|
48
|
+
## Instalación
|
|
49
|
+
|
|
50
|
+
Desde GitHub:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install git+https://github.com/TU_USUARIO/spyc.git
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Desarrollo local:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/TU_USUARIO/spyc.git
|
|
60
|
+
cd spyc
|
|
61
|
+
pip install -e ".[dev]"
|
|
62
|
+
pytest --cov=spyc # pruebas + cobertura (97% al momento de escribir esto)
|
|
63
|
+
ruff check src/ # estilo y errores comunes
|
|
64
|
+
mypy src/spyc # tipos
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Ver [`CHANGELOG.md`](CHANGELOG.md) para el historial de versiones.
|
|
68
|
+
|
|
69
|
+
## Documentación
|
|
70
|
+
|
|
71
|
+
Hay documentación construida con Sphinx en `docs/`, con una página por cada grupo
|
|
72
|
+
de cartas (generadas a partir de los docstrings, así que siempre coinciden con el
|
|
73
|
+
código instalado). Para generarla y abrirla en tu máquina:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install -e ".[docs]"
|
|
77
|
+
sphinx-build -b html docs/source docs/build
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Abre `docs/build/index.html`. Una vez el repositorio esté en GitHub, puedes conectarlo
|
|
81
|
+
en [readthedocs.org](https://readthedocs.org) para tenerla publicada automáticamente
|
|
82
|
+
en cada push; el archivo `.readthedocs.yaml` ya está listo para eso.
|
|
83
|
+
|
|
84
|
+
Requiere Python ≥ 3.9 (numpy, scipy, pandas, matplotlib).
|
|
85
|
+
|
|
86
|
+
## Inicio rápido
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import numpy as np
|
|
90
|
+
import spyc
|
|
91
|
+
|
|
92
|
+
rng = np.random.default_rng(1)
|
|
93
|
+
x = rng.normal(100, 2, 60)
|
|
94
|
+
x[40:] += 3 # el proceso se desplaza
|
|
95
|
+
|
|
96
|
+
carta = spyc.imr_chart(x, tests=(1, 2, 3, 4, 5, 6, 7, 8))
|
|
97
|
+
print(carta.summary()) # resumen tipo sesión de Minitab
|
|
98
|
+
carta.violations() # DataFrame: panel, punto, prueba, descripción
|
|
99
|
+
carta.to_frame() # todos los valores, límites y pruebas fallidas
|
|
100
|
+
carta.plot() # figura de matplotlib
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Cartas de control
|
|
104
|
+
|
|
105
|
+
| Minitab | Función | Notas |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| I-MR | `imr_chart(x)` | `sigma_method`: `'mr'`, `'median_mr'`, `'mssd'`; `span` configurable |
|
|
108
|
+
| Xbar-R | `xbar_r_chart(datos)` | `sigma_method`: `'rbar'`, `'pooled'` |
|
|
109
|
+
| Xbar-S | `xbar_s_chart(datos)` | `sigma_method`: `'sbar'`, `'pooled'` |
|
|
110
|
+
| P | `p_chart(defectuosos, n)` | límites variables con `n` variable |
|
|
111
|
+
| NP | `np_chart(defectuosos, n)` | |
|
|
112
|
+
| C | `c_chart(defectos)` | |
|
|
113
|
+
| U | `u_chart(defectos, n)` | |
|
|
114
|
+
| Laney P' / U' | `laney_p_chart`, `laney_u_chart` | corrige sobredispersión |
|
|
115
|
+
| EWMA | `ewma_chart(datos, weight=0.2, k=3)` | límites exactos (se ensanchan al inicio) |
|
|
116
|
+
| CUSUM | `cusum_chart(datos, h=4, k=0.5)` | tabular, sumas superior e inferior |
|
|
117
|
+
| Media móvil | `ma_chart(datos, length=3)` | límites más anchos en los primeros `length-1` puntos |
|
|
118
|
+
| Z-MR | `zmr_chart(x, partes)` | corridas cortas; ver más abajo |
|
|
119
|
+
| I-MR-R/S (entre/dentro) | `imr_rs_chart(datos, within='r')` | variación entre y dentro de subgrupos |
|
|
120
|
+
| G | `g_chart(entre_eventos)` | eventos raros, geométrica |
|
|
121
|
+
| T | `t_chart(tiempos)` | eventos raros, Weibull o exponencial |
|
|
122
|
+
| T² de Hotelling | `t2_chart(matriz)` | Fase I / Fase II, con diagnóstico por variable |
|
|
123
|
+
| Varianza generalizada | `generalized_variance_chart(matriz, subgroup_size=n)` | dispersión multivariada, `\|S\|` |
|
|
124
|
+
| MEWMA | `mewma_chart(matriz)` | límite por ARL (por defecto 200) |
|
|
125
|
+
|
|
126
|
+
Los datos de variables aceptan una matriz 2D (filas = subgrupos), un vector con
|
|
127
|
+
`subgroup_size=5`, o un vector con `subgroup=identificadores` (subgrupos de tamaño desigual).
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
datos = rng.normal(50, 1, size=(25, 5))
|
|
131
|
+
spyc.xbar_r_chart(datos, tests=(1, 2, 3, 4)).summary()
|
|
132
|
+
|
|
133
|
+
vector = datos.ravel() # mismos datos en un solo vector
|
|
134
|
+
ids = np.repeat(np.arange(25), 5) # identificador de subgrupo de cada dato
|
|
135
|
+
spyc.xbar_r_chart(vector, subgroup_size=5)
|
|
136
|
+
spyc.xbar_s_chart(vector, subgroup=ids)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Parámetros históricos y etapas** (equivalentes a *Opciones de estimación* y *Etapas* de Minitab):
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
spyc.imr_chart(x, mu=100, sigma=2) # parámetros conocidos
|
|
143
|
+
spyc.imr_chart(x, stages=["antes"] * 40 + ["después"] * 20) # límites por etapa
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Atributos:**
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
defectuosos = rng.binomial(200, 0.05, 30)
|
|
150
|
+
spyc.p_chart(defectuosos, n=200)
|
|
151
|
+
spyc.laney_p_chart(defectuosos, n=200)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Cartas avanzadas
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
# Z-MR: partes con medias y variaciones distintas en una sola carta (corridas cortas)
|
|
158
|
+
partes = ["A"] * 10 + ["B"] * 10 + ["A"] * 10
|
|
159
|
+
medidas = np.r_[rng.normal(50, 1, 10), rng.normal(80, 2, 10), rng.normal(50, 1, 10)]
|
|
160
|
+
spyc.zmr_chart(medidas, partes, sigma_method="by_part") # 'constant', 'relative', 'by_part', 'by_run'
|
|
161
|
+
|
|
162
|
+
# I-MR-R/S: la variación entre subgrupos no genera falsas alarmas como en Xbar-R
|
|
163
|
+
sub = rng.normal(20, 1, (25, 5)) + rng.normal(0, 1.5, (25, 1))
|
|
164
|
+
c = spyc.imr_rs_chart(sub, within="s")
|
|
165
|
+
c.params[0] # sigma_dentro, sigma_entre, sigma_entre_dentro
|
|
166
|
+
|
|
167
|
+
# Eventos raros
|
|
168
|
+
spyc.g_chart(rng.geometric(0.02, 40) - 1) # casos entre eventos
|
|
169
|
+
spyc.t_chart(rng.weibull(1.5, 40) * 30) # tiempo entre eventos
|
|
170
|
+
|
|
171
|
+
# Media móvil
|
|
172
|
+
spyc.ma_chart(x, length=5)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`zmr_chart` estandariza cada observación con la media de su parte y una sigma estimada
|
|
176
|
+
con el rango móvil *dentro de cada corrida* (bloque consecutivo de la misma parte);
|
|
177
|
+
`mu={parte: valor}` y `sigma=` aceptan valores históricos o nominales.
|
|
178
|
+
|
|
179
|
+
## Cartas multivariadas
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
Sigma = [[1, .6, .3], [.6, 1, .2], [.3, .2, 1]]
|
|
183
|
+
historico = rng.multivariate_normal([0, 0, 0], Sigma, 100)
|
|
184
|
+
nuevos = rng.multivariate_normal([0, 0, 0], Sigma, 40)
|
|
185
|
+
nuevos[25:, 2] += 4 # la variable 3 se desplaza
|
|
186
|
+
|
|
187
|
+
# Fase I: parámetros estimados de los mismos datos
|
|
188
|
+
spyc.t2_chart(historico).summary()
|
|
189
|
+
|
|
190
|
+
# Fase II: parámetros históricos (n_hist = observaciones con que se estimaron)
|
|
191
|
+
c = spyc.t2_chart(nuevos, mu=historico.mean(axis=0), cov=np.cov(historico, rowvar=False),
|
|
192
|
+
n_hist=100)
|
|
193
|
+
c.violations()
|
|
194
|
+
c.contributions(31) # ¿qué variable explica la señal?
|
|
195
|
+
|
|
196
|
+
# Subgrupos: matriz N x p con subgroup_size, o array 3-D (subgrupos x n x p)
|
|
197
|
+
spyc.t2_chart(nuevos[:40], subgroup_size=4)
|
|
198
|
+
spyc.generalized_variance_chart(nuevos[:40], subgroup_size=8)
|
|
199
|
+
|
|
200
|
+
# MEWMA: cambios pequeños y sostenidos
|
|
201
|
+
spyc.mewma_chart(nuevos, mu=historico.mean(axis=0), cov=np.cov(historico, rowvar=False))
|
|
202
|
+
spyc.mewma_limit(p=2, weight=0.1, arl=200) # límite H para un ARL dado (≈ 8.64)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Los datos pueden ser un `DataFrame` (los nombres de columna se usan en `contributions`).
|
|
206
|
+
El límite de T² usa por defecto α = 0.00135 (cola de 3 sigmas, como Minitab). Sin `mu`/`cov`
|
|
207
|
+
se usan límites de **Fase I** (Beta para individuales, F para subgrupos); con `mu`/`cov` y
|
|
208
|
+
`n_hist`, de **Fase II**; con `mu`/`cov` sin `n_hist` los parámetros se toman como conocidos (χ²).
|
|
209
|
+
|
|
210
|
+
### Etapas y Box-Cox
|
|
211
|
+
|
|
212
|
+
Las 4 cartas multivariadas (T², varianza generalizada, MEWMA y MCUSUM) aceptan `stages`
|
|
213
|
+
y `boxcox`, igual que las cartas univariadas:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
etapas = [1] * 40 + [2] * 40 # p. ej. antes/después de un ajuste
|
|
217
|
+
|
|
218
|
+
# Sin mu/cov: la media y la covarianza se vuelven a estimar dentro de cada etapa
|
|
219
|
+
c = spyc.t2_chart(nuevos_con_dos_etapas, stages=etapas)
|
|
220
|
+
c.stage_mean[1], c.stage_mean[2] # medias distintas por etapa
|
|
221
|
+
c.contributions(45) # usa la media/cov de la etapa del punto 45
|
|
222
|
+
|
|
223
|
+
# Con mu/cov históricos: los mismos parámetros en todas las etapas
|
|
224
|
+
spyc.t2_chart(nuevos_con_dos_etapas, mu=mu, cov=cov, n_hist=100, stages=etapas)
|
|
225
|
+
|
|
226
|
+
# MEWMA y MCUSUM: el acumulador reinicia al empezar cada etapa
|
|
227
|
+
spyc.mewma_chart(nuevos_con_dos_etapas, stages=etapas)
|
|
228
|
+
spyc.mcusum_chart(nuevos_con_dos_etapas, stages=etapas)
|
|
229
|
+
|
|
230
|
+
# Box-Cox: una lambda por variable (no se puede combinar con mu/cov históricos)
|
|
231
|
+
c = spyc.t2_chart(datos_positivos, boxcox=True)
|
|
232
|
+
c.params[0]["lambda_boxcox"] # {'X1': ..., 'X2': ..., 'X3': ...}
|
|
233
|
+
spyc.generalized_variance_chart(datos_positivos, subgroup_size=6, boxcox=True)
|
|
234
|
+
spyc.mewma_chart(datos_positivos, boxcox=True)
|
|
235
|
+
spyc.mcusum_chart(datos_positivos, boxcox=True)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Pruebas de causas especiales
|
|
239
|
+
|
|
240
|
+
Las 8 pruebas de Minitab (1 a 8) con sus parámetros por defecto
|
|
241
|
+
(`K` = 3, 9, 6, 14, 2, 4, 15, 8). Se piden con `tests=(...)` y se ajustan con `test_params`:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
spyc.imr_chart(x, tests=(1, 2, 5), test_params={2: 7}) # prueba 2 con 7 puntos
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Cada tipo de carta aplica el subconjunto que corresponde (completo en I, Xbar y Z;
|
|
248
|
+
básicas 1-4 en MR, R, S, G, T y cartas de atributos; solo la 1 en EWMA, CUSUM, MA y las
|
|
249
|
+
multivariadas). En G y T la prueba 1 usa los percentiles de su distribución.
|
|
250
|
+
Las funciones individuales están en `spyc.rules`.
|
|
251
|
+
|
|
252
|
+
## Capacidad del proceso
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
datos = rng.normal(10, 0.1, 100)
|
|
256
|
+
res = spyc.capability_analysis(datos, lsl=9.7, usl=10.3, subgroup_size=5)
|
|
257
|
+
print(res.summary()) # Cp, CPL, CPU, Cpk, Pp, PPL, PPU, Ppk, Cpm, Z.Bench, PPM, IC
|
|
258
|
+
res.to_frame()
|
|
259
|
+
res.plot()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Como en Minitab, **Cp/Cpk usan la desviación estándar dentro de subgrupos** y
|
|
263
|
+
**Pp/Ppk la desviación general**; por eso Cpk ≠ Ppk cuando el proceso se desplaza o hay variación entre subgrupos.
|
|
264
|
+
|
|
265
|
+
Datos no normales:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
spyc.capability_nonnormal(x, lsl=1, usl=20, distribution="weibull")
|
|
269
|
+
spyc.capability_boxcox(x, lsl=1, usl=20)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Distribuciones: `normal`, `lognormal`, `weibull`, `gamma`, `exponential`,
|
|
273
|
+
`loglogistic`, `logistic`, `largest_extreme`, `smallest_extreme` (método de percentiles).
|
|
274
|
+
|
|
275
|
+
`spyc.capability_sixpack(datos, lsl, usl, subgroup_size=5)` genera el **Capability Sixpack**.
|
|
276
|
+
|
|
277
|
+
## Normalidad, Pareto y constantes
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
spyc.normality_test(x) # Anderson-Darling (por defecto), 'shapiro', 'dagostino'
|
|
281
|
+
spyc.probability_plot(x)
|
|
282
|
+
|
|
283
|
+
tabla = spyc.pareto(["rayón", "abolladura", "rayón", "otro"])
|
|
284
|
+
spyc.plot_pareto(tabla)
|
|
285
|
+
|
|
286
|
+
spyc.control_chart_constants(5) # d2, d3, c4, c5, A2, A3, D3, D4, B3, B4
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Las constantes se calculan por integración numérica para **cualquier** `n ≥ 2` (no solo tablas hasta 25).
|
|
290
|
+
|
|
291
|
+
## Validación
|
|
292
|
+
|
|
293
|
+
pruebas automatizadas. Las referencias son independientes de spyc:
|
|
294
|
+
|
|
295
|
+
- Constantes d2, d3, c4 frente a las tablas publicadas (Montgomery).
|
|
296
|
+
- Límites I-MR, Xbar-R y Xbar-S frente al cálculo manual con A2, D3, D4, A3, B3, B4.
|
|
297
|
+
- Anderson-Darling frente a `statsmodels.stats.diagnostic.normal_ad` (coincide a 9 decimales).
|
|
298
|
+
- EWMA y CUSUM frente a la recursión manual.
|
|
299
|
+
- Las 8 pruebas de causas especiales frente a casos construidos a mano, incluidos los casos límite.
|
|
300
|
+
- T²: valores frente a `scipy.spatial.distance.mahalanobis`; los límites de Fase I y II
|
|
301
|
+
(Beta y F) por simulación Monte Carlo (tasa de falsa alarma y valor esperado).
|
|
302
|
+
- Varianza generalizada: constantes b1 y b2 por simulación de matrices de covarianza.
|
|
303
|
+
- MEWMA: el límite para p=2, λ=0.1, ARL=200 da 8.63 (publicado: 8.64, Prabhu y Runger 1997)
|
|
304
|
+
y el ARL se comprueba por simulación.
|
|
305
|
+
- G y T: cuantiles frente a `scipy.stats`, y máxima verosimilitud Weibull por su ecuación de score.
|
|
306
|
+
- Constantes de Z-MR (1.128 y 3.686) frente a las que documenta Minitab.
|
|
307
|
+
|
|
308
|
+
> **Importante:** spyc no se ha comparado corrida a corrida contra el software Minitab
|
|
309
|
+
> (no hay licencia disponible en el desarrollo). Sigue las fórmulas y convenciones que
|
|
310
|
+
> Minitab documenta. Si encuentras una diferencia, abre un issue (ver `CONTRIBUTING.md`).
|
|
311
|
+
|
|
312
|
+
## Fuera de alcance (por ahora)
|
|
313
|
+
|
|
314
|
+
Cartas multivariadas (T² de Hotelling), cartas de corridas cortas, Z-MR, I-MR-R/S
|
|
315
|
+
(entre/dentro), transformación de Johnson, MSA/Gage R&R.
|
|
316
|
+
|
|
317
|
+
## Licencia
|
|
318
|
+
|
|
319
|
+
MIT.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
pccpy-0.4.3.dist-info/licenses/LICENSE,sha256=nHoS7zkyH9dODTc-wkgXuXUTdSSqPNTrFz6gBO0WgVk,1072
|
|
2
|
+
spyc/__init__.py,sha256=H9V4zs5zsVbHyooRXcRvN4u-j8fb-8zNsFmEWxcBnm4,1900
|
|
3
|
+
spyc/_constants.py,sha256=jsOTg062VXtxUn3K5SjZ2fhrOFbc4zOmMQFFIqWnN_A,2729
|
|
4
|
+
spyc/_data.py,sha256=jjIjGZn_fYvYH2dWyMzavkoSEVpS7zU6gUWYTWRJaYg,4011
|
|
5
|
+
spyc/_sigma.py,sha256=7cUFK2Z48RzYFXR82llm7ERS7Xv1AP4Pbe-5jaLmrtM,3445
|
|
6
|
+
spyc/capability.py,sha256=-3aVL1V-43agzfVUYWwAVR91K7hlMGzaRca4Am4-wuo,15944
|
|
7
|
+
spyc/multivariate.py,sha256=pZfkc6HVnbxF83vRaMOZjbu8U1PuFNTew1DBUD5vDjs,24031
|
|
8
|
+
spyc/normality.py,sha256=QCHFeLqTn10xsvX2IRPoS9e-qWIlRxqIRtjSYq5k92Y,2712
|
|
9
|
+
spyc/plotting.py,sha256=ljaorhxaTh55bMla0i3i1J5jYzkq2ZOYFsBI4crzQXI,10037
|
|
10
|
+
spyc/quality_tools.py,sha256=FTNkkXrwzgynMvW_xY_qU-Q53jS9IyRyBGp39Iv9Q_E,2397
|
|
11
|
+
spyc/results.py,sha256=tR9FVakg2-oZ6QGUTGou41p5DwOKExxWGuQYKhUppZU,7580
|
|
12
|
+
spyc/rules.py,sha256=rlGlzlVh_gHJYBvFoTEpTcgB2qMzMMn0D8oBpyi6aeI,6086
|
|
13
|
+
spyc/charts/__init__.py,sha256=U0u3Dw3i-AZZ7fGgzQLmCXp9HqjD6-vN6JNa-hQRjMc,600
|
|
14
|
+
spyc/charts/_engine.py,sha256=tvsQUuuedGAvqxgxeiAV8cAM8ebR0AEkKdiWg2BLv_w,3618
|
|
15
|
+
spyc/charts/advanced.py,sha256=y0Fxu4WE78BwM65tEzh47FEE9D1WNb9WCDzqEoqot-I,21558
|
|
16
|
+
spyc/charts/attributes.py,sha256=JvJQe3iLJSTWCqgJP0yKNKtsQpLXwyACi8hMvDY3D9s,5387
|
|
17
|
+
spyc/charts/timeweighted.py,sha256=M3r4O5QKjRVXuIgqLzqnUok5R9k5VpTAocmzRPeKWLM,4205
|
|
18
|
+
spyc/charts/variables.py,sha256=ORuEfaipD7t8rJfX1C8GoAO1GbBY2U0M7qYGNYxTnFo,5599
|
|
19
|
+
pccpy-0.4.3.dist-info/METADATA,sha256=9gWY6z7syZ1HqWw703ziSSXlStmh1iP3OcTVKuRlWEU,13363
|
|
20
|
+
pccpy-0.4.3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
21
|
+
pccpy-0.4.3.dist-info/top_level.txt,sha256=LTiaF0UW38bvJzBsmdnooNa5uCV1C_tb6vkbv0p3T_k,5
|
|
22
|
+
pccpy-0.4.3.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Autores de spyc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
spyc
|
spyc/__init__.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""spyc - Control Estadístico de Procesos (SPC) en Python, al estilo Minitab."""
|
|
2
|
+
from ._constants import c4, c5, control_chart_constants, d2, d3
|
|
3
|
+
from .capability import (
|
|
4
|
+
CapabilityResult,
|
|
5
|
+
NonNormalCapabilityResult,
|
|
6
|
+
capability_analysis,
|
|
7
|
+
capability_boxcox,
|
|
8
|
+
capability_nonnormal,
|
|
9
|
+
)
|
|
10
|
+
from .charts import (
|
|
11
|
+
c_chart,
|
|
12
|
+
cusum_chart,
|
|
13
|
+
ewma_chart,
|
|
14
|
+
g_chart,
|
|
15
|
+
imr_chart,
|
|
16
|
+
imr_rs_chart,
|
|
17
|
+
laney_p_chart,
|
|
18
|
+
laney_u_chart,
|
|
19
|
+
ma_chart,
|
|
20
|
+
np_chart,
|
|
21
|
+
p_chart,
|
|
22
|
+
t_chart,
|
|
23
|
+
u_chart,
|
|
24
|
+
xbar_r_chart,
|
|
25
|
+
xbar_s_chart,
|
|
26
|
+
zmr_chart,
|
|
27
|
+
zone_chart,
|
|
28
|
+
)
|
|
29
|
+
from .multivariate import (
|
|
30
|
+
generalized_variance_chart,
|
|
31
|
+
mcusum_chart,
|
|
32
|
+
mcusum_limit,
|
|
33
|
+
mewma_chart,
|
|
34
|
+
mewma_limit,
|
|
35
|
+
t2_chart,
|
|
36
|
+
)
|
|
37
|
+
from .normality import NormalityResult, normality_test
|
|
38
|
+
from .plotting import capability_sixpack, plot_capability, plot_control_chart, probability_plot
|
|
39
|
+
from .quality_tools import pareto, plot_pareto
|
|
40
|
+
from .results import ControlChart, MultivariateChart, Panel
|
|
41
|
+
|
|
42
|
+
__version__ = "0.4.3"
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"CapabilityResult",
|
|
46
|
+
"ControlChart",
|
|
47
|
+
"MultivariateChart",
|
|
48
|
+
"NonNormalCapabilityResult",
|
|
49
|
+
"NormalityResult",
|
|
50
|
+
"Panel",
|
|
51
|
+
"c4",
|
|
52
|
+
"c5",
|
|
53
|
+
"c_chart",
|
|
54
|
+
"capability_analysis",
|
|
55
|
+
"capability_boxcox",
|
|
56
|
+
"capability_nonnormal",
|
|
57
|
+
"capability_sixpack",
|
|
58
|
+
"control_chart_constants",
|
|
59
|
+
"cusum_chart",
|
|
60
|
+
"d2",
|
|
61
|
+
"d3",
|
|
62
|
+
"ewma_chart",
|
|
63
|
+
"g_chart",
|
|
64
|
+
"generalized_variance_chart",
|
|
65
|
+
"imr_chart",
|
|
66
|
+
"imr_rs_chart",
|
|
67
|
+
"laney_p_chart",
|
|
68
|
+
"laney_u_chart",
|
|
69
|
+
"ma_chart",
|
|
70
|
+
"mcusum_chart",
|
|
71
|
+
"mcusum_limit",
|
|
72
|
+
"mewma_chart",
|
|
73
|
+
"mewma_limit",
|
|
74
|
+
"normality_test",
|
|
75
|
+
"np_chart",
|
|
76
|
+
"p_chart",
|
|
77
|
+
"pareto",
|
|
78
|
+
"plot_capability",
|
|
79
|
+
"plot_control_chart",
|
|
80
|
+
"plot_pareto",
|
|
81
|
+
"probability_plot",
|
|
82
|
+
"t2_chart",
|
|
83
|
+
"t_chart",
|
|
84
|
+
"u_chart",
|
|
85
|
+
"xbar_r_chart",
|
|
86
|
+
"xbar_s_chart",
|
|
87
|
+
"zmr_chart",
|
|
88
|
+
"zone_chart",
|
|
89
|
+
]
|
spyc/_constants.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Constantes de control estadístico de procesos (d2, d3, c4, c5, A2, D3, D4, ...).
|
|
2
|
+
|
|
3
|
+
Se calculan numéricamente para *cualquier* tamaño de subgrupo n >= 2, en lugar
|
|
4
|
+
de depender de una tabla finita. Los valores coinciden con las tablas
|
|
5
|
+
publicadas (p. ej. Montgomery, Apéndice VI) a los decimales publicados.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
from functools import cache
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy import integrate
|
|
14
|
+
from scipy.special import gammaln
|
|
15
|
+
|
|
16
|
+
_SQRT2PI = math.sqrt(2.0 * math.pi)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _phi(t: float) -> float:
|
|
20
|
+
return math.exp(-0.5 * t * t) / _SQRT2PI
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _Phi(t: float) -> float:
|
|
24
|
+
return 0.5 * math.erfc(-t / math.sqrt(2.0))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _check_n(n: int) -> int:
|
|
28
|
+
n = int(n)
|
|
29
|
+
if n < 2:
|
|
30
|
+
raise ValueError(f"El tamaño de subgrupo debe ser >= 2 (recibido: {n}).")
|
|
31
|
+
return n
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@cache
|
|
35
|
+
def d2(n: int) -> float:
|
|
36
|
+
"""Media del rango relativo W = R/sigma para n observaciones normales."""
|
|
37
|
+
n = _check_n(n)
|
|
38
|
+
|
|
39
|
+
def f(x: float) -> float:
|
|
40
|
+
p = _Phi(x)
|
|
41
|
+
return 1.0 - p**n - (1.0 - p) ** n
|
|
42
|
+
|
|
43
|
+
val, _ = integrate.quad(f, -np.inf, np.inf, epsabs=1e-13, epsrel=1e-13, limit=200)
|
|
44
|
+
return float(val)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@cache
|
|
48
|
+
def d3(n: int) -> float:
|
|
49
|
+
"""Desviación estándar del rango relativo W = R/sigma."""
|
|
50
|
+
n = _check_n(n)
|
|
51
|
+
lim = 9.0 + math.log(n)
|
|
52
|
+
|
|
53
|
+
def joint(y: float, x: float) -> float:
|
|
54
|
+
# densidad conjunta (mínimo x, máximo y) por (y - x)^2
|
|
55
|
+
return (
|
|
56
|
+
(y - x) ** 2
|
|
57
|
+
* n
|
|
58
|
+
* (n - 1)
|
|
59
|
+
* _phi(x)
|
|
60
|
+
* _phi(y)
|
|
61
|
+
* (_Phi(y) - _Phi(x)) ** (n - 2)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
ex2, _ = integrate.dblquad(
|
|
65
|
+
joint, -lim, lim, lambda x: x, lambda x: lim, epsabs=1e-11, epsrel=1e-11
|
|
66
|
+
)
|
|
67
|
+
return math.sqrt(max(ex2 - d2(n) ** 2, 0.0))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@cache
|
|
71
|
+
def c4(n: int) -> float:
|
|
72
|
+
"""Factor de corrección de sesgo de la desviación estándar muestral."""
|
|
73
|
+
n = _check_n(n)
|
|
74
|
+
return math.sqrt(2.0 / (n - 1)) * math.exp(gammaln(n / 2.0) - gammaln((n - 1) / 2.0))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@cache
|
|
78
|
+
def c5(n: int) -> float:
|
|
79
|
+
"""Desviación estándar de s / sigma: sqrt(1 - c4^2)."""
|
|
80
|
+
return math.sqrt(1.0 - c4(n) ** 2)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def control_chart_constants(n: int) -> dict[str, float]:
|
|
84
|
+
"""Devuelve todas las constantes para un tamaño de subgrupo ``n``.
|
|
85
|
+
|
|
86
|
+
Claves: d2, d3, c4, c5, A2, A3, D3, D4, B3, B4.
|
|
87
|
+
"""
|
|
88
|
+
n = _check_n(n)
|
|
89
|
+
_d2, _d3, _c4, _c5 = d2(n), d3(n), c4(n), c5(n)
|
|
90
|
+
return {
|
|
91
|
+
"d2": _d2,
|
|
92
|
+
"d3": _d3,
|
|
93
|
+
"c4": _c4,
|
|
94
|
+
"c5": _c5,
|
|
95
|
+
"A2": 3.0 / (_d2 * math.sqrt(n)),
|
|
96
|
+
"A3": 3.0 / (_c4 * math.sqrt(n)),
|
|
97
|
+
"D3": max(0.0, 1.0 - 3.0 * _d3 / _d2),
|
|
98
|
+
"D4": 1.0 + 3.0 * _d3 / _d2,
|
|
99
|
+
"B3": max(0.0, 1.0 - 3.0 * _c5 / _c4),
|
|
100
|
+
"B4": 1.0 + 3.0 * _c5 / _c4,
|
|
101
|
+
}
|
spyc/_data.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Conversión y validación de datos de entrada."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def as_1d(x, name: str = "x", allow_nan: bool = False) -> np.ndarray:
|
|
9
|
+
"""Convierte ``x`` (lista, array, Series) a un array float 1-D."""
|
|
10
|
+
arr = np.asarray(x, dtype=float)
|
|
11
|
+
if arr.ndim == 0:
|
|
12
|
+
arr = arr.reshape(1)
|
|
13
|
+
if arr.ndim != 1:
|
|
14
|
+
raise ValueError(f"'{name}' debe ser unidimensional (recibido: {arr.ndim}-D).")
|
|
15
|
+
if arr.size == 0:
|
|
16
|
+
raise ValueError(f"'{name}' está vacío.")
|
|
17
|
+
if not allow_nan and not np.all(np.isfinite(arr)):
|
|
18
|
+
raise ValueError(f"'{name}' contiene valores faltantes o infinitos.")
|
|
19
|
+
return arr
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def to_subgroups(
|
|
23
|
+
data,
|
|
24
|
+
subgroup_size: int | None = None,
|
|
25
|
+
subgroup=None,
|
|
26
|
+
) -> np.ndarray:
|
|
27
|
+
"""Devuelve una matriz (k subgrupos x m columnas) rellena con NaN.
|
|
28
|
+
|
|
29
|
+
Formatos aceptados (los mismos que ofrece Minitab):
|
|
30
|
+
|
|
31
|
+
* ``data`` 2-D (array o DataFrame): cada **fila** es un subgrupo.
|
|
32
|
+
* ``data`` 1-D + ``subgroup_size``: observaciones consecutivas se agrupan de
|
|
33
|
+
a ``subgroup_size``.
|
|
34
|
+
* ``data`` 1-D + ``subgroup`` (identificadores, misma longitud): cada valor
|
|
35
|
+
distinto de ``subgroup`` define un subgrupo, en orden de aparición. Permite
|
|
36
|
+
subgrupos de tamaño desigual.
|
|
37
|
+
"""
|
|
38
|
+
arr = np.asarray(data, dtype=float)
|
|
39
|
+
if arr.ndim == 2:
|
|
40
|
+
if subgroup_size is not None or subgroup is not None:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"Con datos 2-D no se debe indicar 'subgroup_size' ni 'subgroup'."
|
|
43
|
+
)
|
|
44
|
+
mat = arr
|
|
45
|
+
elif arr.ndim == 1:
|
|
46
|
+
if subgroup is not None:
|
|
47
|
+
ids = np.asarray(subgroup)
|
|
48
|
+
if ids.shape != arr.shape:
|
|
49
|
+
raise ValueError("'subgroup' debe tener la misma longitud que los datos.")
|
|
50
|
+
groups = [g.to_numpy(dtype=float) for _, g in pd.Series(arr).groupby(ids, sort=False)]
|
|
51
|
+
m = max(len(g) for g in groups)
|
|
52
|
+
mat = np.full((len(groups), m), np.nan)
|
|
53
|
+
for i, g in enumerate(groups):
|
|
54
|
+
mat[i, : len(g)] = g
|
|
55
|
+
elif subgroup_size is not None:
|
|
56
|
+
m = int(subgroup_size)
|
|
57
|
+
if m < 1:
|
|
58
|
+
raise ValueError("'subgroup_size' debe ser >= 1.")
|
|
59
|
+
if arr.size % m != 0:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"{arr.size} observaciones no se pueden dividir en subgrupos de {m}."
|
|
62
|
+
)
|
|
63
|
+
mat = arr.reshape(-1, m)
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"Datos 1-D: indique 'subgroup_size' o 'subgroup', o pase una matriz 2-D."
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError("Los datos deben ser 1-D o 2-D.")
|
|
70
|
+
|
|
71
|
+
if mat.shape[0] == 0 or mat.shape[1] == 0:
|
|
72
|
+
raise ValueError("No hay datos.")
|
|
73
|
+
if np.any(np.isinf(mat)):
|
|
74
|
+
raise ValueError("Los datos contienen valores infinitos.")
|
|
75
|
+
if np.any(np.all(np.isnan(mat), axis=1)):
|
|
76
|
+
raise ValueError("Hay subgrupos sin ninguna observación válida.")
|
|
77
|
+
return mat
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def stage_slices(n_points: int, stages) -> list:
|
|
81
|
+
"""Lista de (etiqueta, índices) con las etapas en orden de aparición.
|
|
82
|
+
|
|
83
|
+
Cada etapa debe ser un bloque contiguo (como exige Minitab).
|
|
84
|
+
"""
|
|
85
|
+
if stages is None:
|
|
86
|
+
return [(1, np.arange(n_points))]
|
|
87
|
+
st = np.asarray(stages)
|
|
88
|
+
if st.shape != (n_points,):
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"'stages' debe tener una etiqueta por punto graficado ({n_points}); "
|
|
91
|
+
f"recibido: {st.shape}."
|
|
92
|
+
)
|
|
93
|
+
out, seen = [], set()
|
|
94
|
+
start = 0
|
|
95
|
+
for i in range(1, n_points + 1):
|
|
96
|
+
if i == n_points or st[i] != st[start]:
|
|
97
|
+
label = st[start].item() if hasattr(st[start], "item") else st[start]
|
|
98
|
+
if label in seen:
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"La etapa {label!r} aparece en bloques separados; "
|
|
101
|
+
"cada etapa debe ser contigua."
|
|
102
|
+
)
|
|
103
|
+
seen.add(label)
|
|
104
|
+
out.append((label, np.arange(start, i)))
|
|
105
|
+
start = i
|
|
106
|
+
return out
|