farc-hd 0.1.0__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 (31) hide show
  1. farc_hd-0.1.0/PKG-INFO +15 -0
  2. farc_hd-0.1.0/farc_hd/FARCHD/Apriori.py +198 -0
  3. farc_hd-0.1.0/farc_hd/FARCHD/BullWarmUp.py +32 -0
  4. farc_hd-0.1.0/farc_hd/FARCHD/DataBase.py +396 -0
  5. farc_hd-0.1.0/farc_hd/FARCHD/Fuzzy.py +172 -0
  6. farc_hd-0.1.0/farc_hd/FARCHD/Individual.py +428 -0
  7. farc_hd-0.1.0/farc_hd/FARCHD/Item.py +118 -0
  8. farc_hd-0.1.0/farc_hd/FARCHD/Itemset.py +217 -0
  9. farc_hd-0.1.0/farc_hd/FARCHD/Population.py +271 -0
  10. farc_hd-0.1.0/farc_hd/FARCHD/Rule.py +297 -0
  11. farc_hd-0.1.0/farc_hd/FARCHD/RuleBase.py +948 -0
  12. farc_hd-0.1.0/farc_hd/FARCHD/Utilidades.py +99 -0
  13. farc_hd-0.1.0/farc_hd/FARCHD/WarmUpParameters.py +32 -0
  14. farc_hd-0.1.0/farc_hd/FARCHD/__init__.py +0 -0
  15. farc_hd-0.1.0/farc_hd/FARCHD/myDataSetV2.py +543 -0
  16. farc_hd-0.1.0/farc_hd/FARCHD/parseParameters.py +149 -0
  17. farc_hd-0.1.0/farc_hd/FARCHD/utils.py +257 -0
  18. farc_hd-0.1.0/farc_hd/FarcHDClassifier.py +278 -0
  19. farc_hd-0.1.0/farc_hd/__init__.py +0 -0
  20. farc_hd-0.1.0/farc_hd/org/core/Fichero.py +68 -0
  21. farc_hd-0.1.0/farc_hd/org/core/Files.py +65 -0
  22. farc_hd-0.1.0/farc_hd/org/core/MTwister.py +173 -0
  23. farc_hd-0.1.0/farc_hd/org/core/Randomize.py +160 -0
  24. farc_hd-0.1.0/farc_hd/org/core/__init__.py +0 -0
  25. farc_hd-0.1.0/farc_hd.egg-info/PKG-INFO +15 -0
  26. farc_hd-0.1.0/farc_hd.egg-info/SOURCES.txt +29 -0
  27. farc_hd-0.1.0/farc_hd.egg-info/dependency_links.txt +1 -0
  28. farc_hd-0.1.0/farc_hd.egg-info/requires.txt +3 -0
  29. farc_hd-0.1.0/farc_hd.egg-info/top_level.txt +1 -0
  30. farc_hd-0.1.0/pyproject.toml +25 -0
  31. farc_hd-0.1.0/setup.cfg +4 -0
farc_hd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.1
2
+ Name: farc-hd
3
+ Version: 0.1.0
4
+ Summary: Fuzzy Association Rule-based Classification Model for High-Dimensional problems
5
+ Author-email: Iñaki Mellado <inaki.mellado@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: numpy>=1.20.0
14
+ Requires-Dist: scikit-learn>=1.0.0
15
+ Requires-Dist: numba>=0.55.0
@@ -0,0 +1,198 @@
1
+ # ==============================================================================
2
+ # FARC-HD (Fuzzy Association Rule-based Classification Model for High-Dimensional)
3
+ # ==============================================================================
4
+ # Original Algorithm Design & Java Implementation:
5
+ # @author Jesus Alcalá-Fdez (University of Granada) - 09/02/2010
6
+ # @copyright KEEL Copyright (c) 2008-2010
7
+ #
8
+ # Python Translation, Scikit-learn API Integration & Optimization:
9
+ # @author Iñaki Mellado Ilundain
10
+ # @author JOSE ANTONIO SANZ DELGADO
11
+ #
12
+ # Description:
13
+ # This file is part of the Python port of the FARC-HD algorithm,
14
+ # originally developed for the KEEL software tool. It has been redesigned
15
+ # to be fully compatible with the scikit-learn ecosystem and optimized
16
+ # using Numba for high-performance JIT compilation.
17
+ # ==============================================================================
18
+
19
+ from farc_hd.FARCHD.RuleBase import RuleBase
20
+ from farc_hd.FARCHD.Itemset import Itemset
21
+ from farc_hd.FARCHD.Item import Item
22
+
23
+ class Apriori:
24
+ # -------------------------------------------------------------------------
25
+ # NOMBRE: generateRB
26
+ # DESCRIPCIÓN:
27
+ # calcula los soportes mínimos por clase.
28
+ # Variables de entrada:
29
+ # - ruleBase: Objeto base donde se almacenarán las reglas finales.
30
+ # - dataBase: Base de datos con la definición de etiquetas y variables.
31
+ # - train: Conjunto de datos de entrenamiento.
32
+ # - minsup: Valor de soporte mínimo relativo.
33
+ # - minconf: Valor de confianza mínima para la generación de reglas.
34
+ # - depth: Profundidad máxima (número de ítems) de los itemsets a generar.
35
+ # -------------------------------------------------------------------------
36
+ def __init__(self, ruleBase=None, dataBase=None, train=None, minsup=None, minconf=None, depth=None):
37
+ self.train = train
38
+ self.dataBase = dataBase
39
+ self.ruleBase = ruleBase
40
+ self.minconf = minconf
41
+ self.depth = depth
42
+ self.nClasses = self.train.getnClasses()
43
+ self.nVariables = self.train.getnInputs()
44
+
45
+ self.L2 = []
46
+ self.minSupps = [self.train.frecuentClass(i) * minsup for i in range(self.nClasses)]
47
+
48
+ # -------------------------------------------------------------------------
49
+ # NOMBRE: generateRB
50
+ # DESCRIPCIÓN:
51
+ # Itera sobre cada clase para generar itemsets frecuentes, reglas y reducir el conjunto final de reglas.
52
+ # -------------------------------------------------------------------------
53
+ def generateRB(self):
54
+ self.ruleStage1 = 0
55
+ self.ruleBaseClase = RuleBase(self.dataBase, self.train, self.ruleBase.getK(), self.ruleBase.getTypeInference())
56
+
57
+ for i in range(self.nClasses):
58
+ self.minsup = self.minSupps[i]
59
+ self.generateL2(i)
60
+ self.generateLarge(self.L2, i)
61
+ self.ruleBaseClase.reduceRules(i)
62
+
63
+ self.ruleBase.add(self.ruleBaseClase)
64
+ self.ruleBaseClase.clear()
65
+
66
+ self.ruleBase.almacenaPesos()
67
+
68
+ # -------------------------------------------------------------------------
69
+ # NOMBRE: generateL2
70
+ # DESCRIPCIÓN:
71
+ # Filtra y genera itemsets de nivel 1 (un solo ítem) que superan el
72
+ # umbral de soporte mínimo para una clase específica.
73
+ # ENTRADA:
74
+ # - clas [int]: Índice de la clase objetivo.
75
+ # -------------------------------------------------------------------------
76
+ def generateL2(self, clas):
77
+ self.L2 = []
78
+ itemset = Itemset(clas)
79
+ absValue = 0
80
+ for i in range(self.nVariables):
81
+ if self.dataBase.numLabels(i) > 1:
82
+ for j in range(self.dataBase.numLabels(i)):
83
+ item = Item(i, j, absValue)
84
+ itemset.add(item)
85
+ itemset.calculateSupports(self.dataBase)
86
+
87
+ absValue += 1
88
+ if itemset.getSupportClass() >= self.minsup:
89
+ self.L2.append(itemset.clone())
90
+ itemset.remove(0)
91
+
92
+ self.generateRules(self.L2, clas)
93
+
94
+ # -------------------------------------------------------------------------
95
+ # NOMBRE: hasUncoverClass
96
+ # DESCRIPCIÓN:
97
+ # Verifica el número de ejemplos de una clase que no han sido
98
+ # cubiertos por los itemsets actuales.
99
+ # ENTRADA:
100
+ # - clas [int]: Índice de la clase a verificar.
101
+ # SALIDA:
102
+ # - uncover [int]: Cantidad de ejemplos sin cobertura.
103
+ # -------------------------------------------------------------------------
104
+ def hasUncoverClass(self, clas):
105
+ uncover = 0
106
+ for j in range(self.train.size()):
107
+ if self.train.getOutputAsInteger(j) == clas:
108
+ stop = False
109
+ for itemset in self.L2:
110
+ degree = itemset.degree(self.dataBase, self.train.getExample(j))
111
+ if degree > 0.0:
112
+ stop = True
113
+ break
114
+ if not stop:
115
+ uncover += 1
116
+ return uncover
117
+
118
+ # -------------------------------------------------------------------------
119
+ # NOMBRE: generateLarge
120
+ # DESCRIPCIÓN:
121
+ # Proceso recursivo que expande itemsets (k -> k+1) combinando
122
+ # elementos combinables que mantienen el soporte mínimo.
123
+ # ENTRADA:
124
+ # - Lk [list]: Lista de itemsets del nivel actual.
125
+ # - clas [int]: Índice de la clase analizada.
126
+ # SALIDA:
127
+ # - None
128
+ # -------------------------------------------------------------------------
129
+ def generateLarge(self, Lk, clas):
130
+ size = len(Lk)
131
+ if size > 1:
132
+ if Lk[0].size() < self.nVariables and Lk[0].size() < self.depth:
133
+ Lnew = []
134
+ for i in range(size - 1):
135
+ itemseti = Lk[i]
136
+ for j in range(i + 1, size):
137
+ itemsetj = Lk[j]
138
+
139
+ if self.isCombinable(itemseti, itemsetj):
140
+ newItemset = itemseti.clone()
141
+ newItemset.add((itemsetj.get(itemsetj.size()-1)).clone())
142
+ newItemset.calculateSupports(self.dataBase) ## Cambio
143
+ if newItemset.getSupportClass() >= self.minsup:
144
+ Lnew.append(newItemset)
145
+
146
+ self.generateRules(Lnew, clas)
147
+ self.generateLarge(Lnew, clas)
148
+ Lnew.clear()
149
+
150
+ # -------------------------------------------------------------------------
151
+ # NOMBRE: isCombinable
152
+ # DESCRIPCIÓN:
153
+ # Lógica de poda para asegurar que solo se combinen itemsets siguiendo una
154
+ # secuencia indexada ascendente de variables
155
+ # ENTRADA:
156
+ # - itemseti: Primer conjunto de ítems.
157
+ # - itemsetj: Segundo conjunto de ítems.
158
+ # SALIDA:
159
+ # - [bool]: True si son combinables, False en caso contrario.
160
+ # -------------------------------------------------------------------------
161
+ def isCombinable(self, itemseti, itemsetj):
162
+ itemi = itemseti.get(itemseti.size()-1)
163
+ itemj = itemsetj.get(itemseti.size()-1)
164
+ return itemi.getVariable() < itemj.getVariable()
165
+
166
+ # -------------------------------------------------------------------------
167
+ # NOMBRE: getRulesStage1
168
+ # DESCRIPCIÓN:
169
+ # Retorna el conteo acumulado de reglas encontradas en la primera etapa.
170
+ # SALIDA:
171
+ # - [int]: Total de reglas generadas.
172
+ # -------------------------------------------------------------------------
173
+ def getRulesStage1(self):
174
+ return self.ruleStage1
175
+
176
+ # -------------------------------------------------------------------------
177
+ # NOMBRE: generateRules
178
+ # DESCRIPCIÓN:
179
+ # Calcula la confianza de los itemsets y decide cuáles se integran
180
+ # como reglas y cuáles se eliminan del proceso de expansión.
181
+ # ENTRADA:
182
+ # - Lk [list]: Lista de itemsets candidatos.
183
+ # - clas [int]: Índice de la clase objetivo.
184
+ # -------------------------------------------------------------------------
185
+ def generateRules(self, Lk, clas):
186
+ i = len(Lk) - 1
187
+ while i >= 0:
188
+ itemset = Lk[i]
189
+ if itemset.getSupport() > 0.0:
190
+ confidence = itemset.getSupportClass() / itemset.getSupport()
191
+ else:
192
+ confidence = 0.0
193
+ if confidence > 0.4:
194
+ self.ruleBaseClase.add(itemset)
195
+ self.ruleStage1 += 1
196
+ if confidence > self.minconf:
197
+ Lk.pop(i)
198
+ i -= 1
@@ -0,0 +1,32 @@
1
+ # ==============================================================================
2
+ # FARC-HD (Fuzzy Association Rule-based Classification Model for High-Dimensional)
3
+ # ==============================================================================
4
+ # Original Algorithm Design & Java Implementation:
5
+ # @author Jesus Alcalá-Fdez (University of Granada) - 09/02/2010
6
+ # @copyright KEEL Copyright (c) 2008-2010
7
+ #
8
+ # Python Translation, Scikit-learn API Integration & Optimization:
9
+ # @author Iñaki Mellado Ilundain
10
+ # @author JOSE ANTONIO SANZ DELGADO
11
+ #
12
+ # Description:
13
+ # This file is part of the Python port of the FARC-HD algorithm,
14
+ # originally developed for the KEEL software tool. It has been redesigned
15
+ # to be fully compatible with the scikit-learn ecosystem and optimized
16
+ # using Numba for high-performance JIT compilation.
17
+ # ==============================================================================
18
+
19
+ from farc_hd.FARCHD.Bull import Bull
20
+ # DESCRIPCIon:
21
+ # Clase secundaria para precalentamiento de numba
22
+ class BullWarmUp(Bull):
23
+ """
24
+ Versión 'Muda' de Bull.
25
+ Ejecuta toda la lógica matemática (evolución, reglas, inferencia)
26
+ pero ANULA todas las funciones de escritura en disco (I/O).
27
+ Esto evita que el calentamiento pierda tiempo creando archivos basura.
28
+ """
29
+ def writeEvo(self): pass
30
+ def writeRules(self): pass
31
+ def writeTime(self): pass
32
+ def doOutput(self, dataset, filename): pass
@@ -0,0 +1,396 @@
1
+ # ==============================================================================
2
+ # FARC-HD (Fuzzy Association Rule-based Classification Model for High-Dimensional)
3
+ # ==============================================================================
4
+ # Original Algorithm Design & Java Implementation:
5
+ # @author Jesus Alcalá-Fdez (University of Granada) - 09/02/2010
6
+ # @copyright KEEL Copyright (c) 2008-2010
7
+ #
8
+ # Python Translation, Scikit-learn API Integration & Optimization:
9
+ # @author Iñaki Mellado Ilundain
10
+ # @author JOSE ANTONIO SANZ DELGADO
11
+ #
12
+ # Description:
13
+ # This file is part of the Python port of the FARC-HD algorithm,
14
+ # originally developed for the KEEL software tool. It has been redesigned
15
+ # to be fully compatible with the scikit-learn ecosystem and optimized
16
+ # using Numba for high-performance JIT compilation.
17
+ # ==============================================================================
18
+
19
+ import numpy as np
20
+ from farc_hd.FARCHD.Fuzzy import Fuzzy, fuzzificacion_total_numba
21
+ from farc_hd.org.core.Files import Files
22
+ from numba import njit
23
+
24
+
25
+ # -------------------------------------------------------------------------
26
+ # NOMBRE: decode_numba
27
+ # DESCRIPCIÓN:
28
+ # Realiza el desplazamiento de las funciones de pertenencia triangulares
29
+ # basándose en un cromosoma (gen).
30
+ # ENTRADA:
31
+ # - gene [np.array]: Vector de valores que dictan el desplazamiento.
32
+ # - n_variables [int]: Número total de variables de entrada.
33
+ # - nLabels [np.array]: Cantidad de etiquetas por variable.
34
+ # - varReal [np.array]: Máscara booleana para identificar variables continuas.
35
+ # - ini_x0, ini_x1, ini_x3: Matrices con puntos base del triángulo.
36
+ # - output_x0, output_x1, output_x3: Matrices donde se guardan los puntos modificados.
37
+ # -------------------------------------------------------------------------
38
+ @njit(fastmath=True)
39
+ def decode_numba(gene, n_variables, nLabels, varReal, ini_x0, ini_x1, ini_x3, output_x0, output_x1, output_x3):
40
+ pos = 0
41
+ for i in range(n_variables):
42
+ # Omitimos variables que no son reales/continuas
43
+ if not varReal[i]:
44
+ continue
45
+
46
+ count_labels = nLabels[i]
47
+ for j in range(count_labels):
48
+ val_gene = gene[pos]
49
+ nuevaPos = val_gene - 0.5
50
+ x1_curr = ini_x1[i, j]
51
+
52
+ # CASO 1: Es la primera etiqueta (solo tiene vecinos a la derecha)
53
+ if j == 0:
54
+ x1_next = ini_x1[i, j+1]
55
+ displacement = nuevaPos * (x1_next - x1_curr)
56
+
57
+ # CASO 2: Es la última etiqueta (solo tiene vecinos a la izquierda)
58
+ elif j == (count_labels - 1):
59
+ x1_prev = ini_x1[i, j-1]
60
+ displacement = nuevaPos * (x1_curr - x1_prev)
61
+
62
+ # CASO 3: Etiquetas intermedias
63
+ else:
64
+ x1_next = ini_x1[i, j+1]
65
+ x1_prev = ini_x1[i, j-1]
66
+ # Si se mueve a la derecha, el rango máximo es hacia el centro del vecino derecho
67
+ if val_gene >= 0.5:
68
+ displacement = nuevaPos * (x1_next - x1_curr)
69
+ # Si se mueve a la izquierda, el rango máximo es hacia el centro del vecino izquierdo
70
+ else:
71
+ displacement = nuevaPos * (x1_curr - x1_prev)
72
+
73
+ # Aplicamos el desplazamiento calculado a los tres puntos del triángulo (A, B, C)
74
+ output_x0[i, j] = ini_x0[i, j] + displacement
75
+ output_x1[i, j] = ini_x1[i, j] + displacement
76
+ output_x3[i, j] = ini_x3[i, j] + displacement
77
+
78
+ # Avanzamos a la siguiente posición del cromosoma
79
+ pos += 1
80
+
81
+ class DataBase:
82
+ # -------------------------------------------------------------------------
83
+ # NOMBRE: __init__
84
+ # DESCRIPCIÓN:
85
+ # Crea la partición difusa inicial (grid uniforme) y prepara los arrays
86
+ # Numpy para el procesamiento acelerado.
87
+ # ENTRADA:
88
+ # - nLabels [int]: Número por defecto de etiquetas difusas por variable.
89
+ # - train [myDataSet]: Conjunto de entrenamiento para calcular rangos.
90
+ # -------------------------------------------------------------------------
91
+ def __init__(self, nLabels, train):
92
+ self.todasLasClases = train.getOutputsAsIntegers()
93
+ self.nClases = train.getnClasses()
94
+ self.nEtiquetas = nLabels
95
+ ranks = train.returnRanks()
96
+ self.n_variables = train.getnInputs()
97
+ self.names = np.copy(train.names())
98
+ self.nLabels = np.zeros(self.n_variables, dtype=np.int32)
99
+ self.varReal = np.array([False]*self.n_variables)
100
+ self.dataBase = [None] * self.n_variables
101
+ self.dataBaseIni = [None] * self.n_variables
102
+ self.labelTotales = 0
103
+
104
+ # Definición de Etiquetas y Estructura Fuzzy
105
+ for i in range (self.n_variables):
106
+ rank = np.abs(ranks[i][1] - ranks[i][0])
107
+
108
+ # Determinamos el número de etiquetas según el tipo de variable
109
+ if train.isNominal(i):
110
+ self.nLabels[i] = int(rank)+1
111
+ elif (train.isInteger(i)) and ((rank+1) <= nLabels):
112
+ self.nLabels[i] = int(rank)+1
113
+ else:
114
+ # Si es continua, usamos el parámetro nLabels y marcamos como sintonizable (varReal)
115
+ self.nLabels[i] = nLabels
116
+ self.varReal[i] = True
117
+
118
+ self.dataBase[i] = [None]*self.nLabels[i]
119
+ self.dataBaseIni[i] = [None]*self.nLabels[i]
120
+ mark = rank / (self.nLabels[i] - 1.0)
121
+
122
+ for j in range(self.nLabels[i]):
123
+ self.dataBase[i][j] = Fuzzy()
124
+ self.dataBaseIni[i][j] = Fuzzy()
125
+
126
+ # Definición de los 3 puntos del triángulo (x0:izq, x1:centro, x3:der)
127
+ value = ranks[i][0] + (mark * (j-1))
128
+ x0_val = self.setValue(value, ranks[i][0], ranks[i][1])
129
+
130
+ value = ranks[i][0] + (mark * j)
131
+ x1_val = self.setValue(value, ranks[i][0], ranks[i][1])
132
+
133
+ value = ranks[i][0] + (mark * (j+1))
134
+ x3_val = self.setValue(value, ranks[i][0], ranks[i][1])
135
+
136
+ # Guardamos en la base de datos actual e inicial
137
+ self.dataBaseIni[i][j].set_x0(x0_val)
138
+ self.dataBaseIni[i][j].set_x1(x1_val)
139
+ self.dataBaseIni[i][j].set_x3(x3_val)
140
+ self.dataBaseIni[i][j].set_y(1.0)
141
+
142
+ self.dataBase[i][j].set_x0(x0_val)
143
+ self.dataBase[i][j].set_x1(x1_val)
144
+ self.dataBase[i][j].set_x3(x3_val)
145
+ self.dataBase[i][j].set_y(1.0)
146
+
147
+ nombre = "L_" + str(j) + "(" + str(self.nLabels[i]) + ")"
148
+ self.dataBase[i][j].set_name(nombre)
149
+ self.dataBaseIni[i][j].set_name(nombre)
150
+
151
+ self.labelTotales += 1
152
+
153
+ print(f"Label totales = {self.labelTotales}")
154
+ print(f"self.nLabels = {self.nLabels}")
155
+
156
+ # Preparación de Matrices para Optimización (Numba)
157
+ # Creamos matrices densas para evitar el acceso lento a listas de objetos Fuzzy
158
+ max_labels = np.max(self.nLabels)
159
+
160
+ # Matrices para valores iniciales (constantes durante la evolución)
161
+ self.ini_x0 = np.zeros((self.n_variables, max_labels), dtype=np.float64)
162
+ self.ini_x1 = np.zeros((self.n_variables, max_labels), dtype=np.float64)
163
+ self.ini_x3 = np.zeros((self.n_variables, max_labels), dtype=np.float64)
164
+
165
+ # Matrices actualizables (se actualizan en cada iteración de decode_numba)
166
+ self.x0_arr = np.zeros((self.n_variables, max_labels), dtype=np.float64)
167
+ self.x1_arr = np.zeros((self.n_variables, max_labels), dtype=np.float64)
168
+ self.x3_arr = np.zeros((self.n_variables, max_labels), dtype=np.float64)
169
+
170
+ # Volcamos los datos de los objetos Fuzzy a las matrices de NumPy
171
+ for i in range(self.n_variables):
172
+ for j in range(self.nLabels[i]):
173
+ self.ini_x0[i, j] = self.dataBaseIni[i][j].get_x0()
174
+ self.ini_x1[i, j] = self.dataBaseIni[i][j].get_x1()
175
+ self.ini_x3[i, j] = self.dataBaseIni[i][j].get_x3()
176
+
177
+ self.x0_arr[i, j] = self.dataBase[i][j].get_x0()
178
+ self.x1_arr[i, j] = self.dataBase[i][j].get_x1()
179
+ self.x3_arr[i, j] = self.dataBase[i][j].get_x3()
180
+
181
+ # -------------------------------------------------------------------------
182
+ # NOMBRE: getnClases
183
+ # DESCRIPCIÓN:
184
+ # Retorna el numero de clases
185
+ # SALIDA:
186
+ # - self.nClases[int]: numero de clases del dataset.
187
+ # -------------------------------------------------------------------------
188
+ def getnClases(self):
189
+ return self.nClases
190
+
191
+ # -------------------------------------------------------------------------
192
+ # NOMBRE: setValue
193
+ # DESCRIPCIÓN:
194
+ # Ajusta valores con una tolerancia de 1E-4 para evitar errores de
195
+ # precisión numérica en los límites de los rangos.
196
+ # ENTRADA:
197
+ # - val, minimo, tope [float]: Valor a evaluar y límites.
198
+ # SALIDA:
199
+ # - val [float]: Valor ajustado o el original.
200
+ # -------------------------------------------------------------------------
201
+ def setValue(self, val, minimo, tope):
202
+ if (val > (minimo - 1E-4)) and (val < (minimo + 1E-4)):
203
+ return minimo
204
+ if (val > (tope - 1E-4)) and (val < (tope + 1E-4)):
205
+ return tope
206
+ return val
207
+
208
+ # -------------------------------------------------------------------------
209
+ # NOMBRE: decode
210
+ # DESCRIPCIÓN:
211
+ # Actualiza las matrices numéricas de los triángulos difusos
212
+ # ENTRADA:
213
+ # - gene [np.array]: Genotipo del individuo a decodificar.
214
+ # -------------------------------------------------------------------------
215
+ def decode(self, gene):
216
+ # Solo actualizamos las matrices de números (x0_arr, x1_arr, etc.)
217
+ # Esto es lo que usa Numba para evaluar rápido.
218
+ decode_numba(gene, self.n_variables, self.nLabels, self.varReal,
219
+ self.ini_x0, self.ini_x1, self.ini_x3,
220
+ self.x0_arr, self.x1_arr, self.x3_arr)
221
+
222
+ # -------------------------------------------------------------------------
223
+ # NOMBRE: synchronize_objects
224
+ # DESCRIPCIÓN:
225
+ # Sincroniza el "mundo Numpy" con el "mundo de objetos Python". Actualiza
226
+ # cada objeto Fuzzy individual con los valores calculados en los arrays.
227
+ # -------------------------------------------------------------------------
228
+ def synchronize_objects(self):
229
+ # Copia los valores de los arrays optimizados de vuelta a los objetos Fuzzy
230
+ # para que funciones como 'printString' o 'saveFile' funcionen bien.
231
+ for i in range(self.n_variables):
232
+ if not self.varReal[i]:
233
+ continue
234
+ for j in range(self.nLabels[i]):
235
+ # Pasamos los datos del mundo Numpy al mundo de Objetos Python
236
+ self.dataBase[i][j].set_x0(self.x0_arr[i, j])
237
+ self.dataBase[i][j].set_x1(self.x1_arr[i, j])
238
+ self.dataBase[i][j].set_x3(self.x3_arr[i, j])
239
+
240
+ # -------------------------------------------------------------------------
241
+ # NOMBRE: numVariables
242
+ # DESCRIPCIÓN:
243
+ # Retorna el numero de variables
244
+ # SALIDA:
245
+ # - self.n_variables[int]: numero de variables.
246
+ # -------------------------------------------------------------------------
247
+ def numVariables(self):
248
+ return self.n_variables
249
+
250
+ # -------------------------------------------------------------------------
251
+ # NOMBRE: getnLabelsReal
252
+ # DESCRIPCIÓN:
253
+ # Calcula el número total de etiquetas difusas para variables continuas.
254
+ # SALIDA:
255
+ # - count [int]: Suma de etiquetas de variables reales.
256
+ # -------------------------------------------------------------------------
257
+ def getnLabelsReal(self):
258
+ count = 0
259
+ for i in range (self.n_variables):
260
+ if self.varReal[i]:
261
+ count += self.nLabels[i]
262
+ return count
263
+
264
+ # -------------------------------------------------------------------------
265
+ # NOMBRE: numLabels
266
+ # DESCRIPCIÓN:
267
+ # Retorna la cantidad de etiquetas de una variable específica.
268
+ # ENTRADA:
269
+ # - variable [int]: Índice de la variable.
270
+ # SALIDA:
271
+ # - [int]: Número de etiquetas.
272
+ # -------------------------------------------------------------------------
273
+ def numLabels(self, variable):
274
+ return self.nLabels[variable]
275
+
276
+ # -------------------------------------------------------------------------
277
+ # NOMBRE: getnLabels
278
+ # DESCRIPCIÓN:
279
+ # Retorna el array con la cantidad de etiquetas de todas las variables.
280
+ # SALIDA:
281
+ # - self.nLabels[np.array]: Vector de enteros con las etiquetas por variable.
282
+ # -------------------------------------------------------------------------
283
+ def getnLabels(self):
284
+ return self.nLabels
285
+
286
+ # -------------------------------------------------------------------------
287
+ # NOMBRE: matching
288
+ # DESCRIPCIÓN:
289
+ # Retorna el grado de pertenencia precalculado para un ejemplo y etiqueta.
290
+ # ENTRADA:
291
+ # - variableLabel [int]: Índice de la etiqueta en la matriz de fuzzificación.
292
+ # - value [int]: Índice del ejemplo.
293
+ # SALIDA:
294
+ # - [float]: Valor de matching entre 0 y 1.
295
+ # -------------------------------------------------------------------------
296
+ def matching(self, variableLabel, value):
297
+ if (variableLabel < 0):
298
+ return 1
299
+ else:
300
+ return self.trainFuzzificado[value][variableLabel]
301
+
302
+ # -------------------------------------------------------------------------
303
+ # NOMBRE: print_triangle
304
+ # DESCRIPCIÓN:
305
+ # Genera una cadena de texto con los parámetros del triángulo de una etiqueta.
306
+ # ENTRADA:
307
+ # - var [int]: Índice de la variable.
308
+ # - label [int]: Índice de la etiqueta.
309
+ # SALIDA:
310
+ # - cadena [str]: Información formateada del triángulo.
311
+ # -------------------------------------------------------------------------
312
+ def print_triangle(self, var, label):
313
+ cadena = ""
314
+ d = self.dataBase[var][label]
315
+ cadena += d.getName() + ": \t" + str(d.get_x0()) + "\t" + str(d.get_x1()) + "\t" + str(d.get_x3()) + "\n"
316
+ return cadena
317
+
318
+ # -------------------------------------------------------------------------
319
+ # NOMBRE: print
320
+ # DESCRIPCIÓN:
321
+ # Retorna el nombre asignado a una etiqueta específica.
322
+ # ENTRADA:
323
+ # - var [int]: Índice de la variable.
324
+ # - label [int]: Índice de la etiqueta.
325
+ # SALIDA:
326
+ # - [str]: Nombre de la etiqueta.
327
+ # -------------------------------------------------------------------------
328
+ def print(self, var, label):
329
+ return self.dataBase[var][label].getName()
330
+
331
+ # -------------------------------------------------------------------------
332
+ # NOMBRE: printString
333
+ # DESCRIPCIÓN:
334
+ # Genera un resumen completo de la base de datos difusa en formato texto.
335
+ # SALIDA:
336
+ # - string [str]: Resumen detallado de variables y etiquetas.
337
+ # -------------------------------------------------------------------------
338
+ def printString(self):
339
+ string = "@Using Triangular Membership Functions as antecedent fuzzy sets"
340
+ for i in range (self.n_variables):
341
+ string += "\n\n@Number of Labels in Variable " + str((i+1)) + ": " + str(self.nLabels[i])
342
+ string += "\n" + self.names[i] + ":\n"
343
+ for j in range(self.nLabels[i]):
344
+ string += self.dataBase[i][j].getName() + ": (" + str(self.dataBase[i][j].get_x0()) + ", " + str(self.dataBase[i][j].get_x1()) + ", " + str(self.dataBase[i][j].get_x3()) + ")\n"
345
+ return string
346
+
347
+ # -------------------------------------------------------------------------
348
+ # NOMBRE: saveFile
349
+ # DESCRIPCIÓN:
350
+ # Guarda la configuración actual de la base de datos en un archivo físico.
351
+ # ENTRADA:
352
+ # - filename [str]: Ruta del archivo de destino.
353
+ # -------------------------------------------------------------------------
354
+ def saveFile(self, filename):
355
+ stringOut = self.printString()
356
+ Files.writeFile(filename, stringOut)
357
+
358
+ # -------------------------------------------------------------------------
359
+ # NOMBRE: fuzzificacion
360
+ # DESCRIPCIÓN:
361
+ # Calcula los grados de pertenencia de todos los datos de
362
+ # entrada frente a las etiquetas de la base de datos.
363
+ # ENTRADA:
364
+ # - datos [myDataSet]: El conjunto de datos a procesar.
365
+ # -------------------------------------------------------------------------
366
+ def fuzzificacion(self, datos):
367
+ datos_raw = datos.getAllData()
368
+ self.trainFuzzificado = fuzzificacion_total_numba(
369
+ datos_raw,
370
+ self.n_variables,
371
+ self.nLabels,
372
+ self.x0_arr,
373
+ self.x1_arr,
374
+ self.x3_arr,
375
+ self.labelTotales
376
+ )
377
+
378
+ # -------------------------------------------------------------------------
379
+ # NOMBRE: getTrainFuzzy
380
+ # DESCRIPCIÓN:
381
+ # Retorna la matriz de datos ya fuzzificados.
382
+ # SALIDA:
383
+ # - [np.array]: Matriz de grados de pertenencia.
384
+ # -------------------------------------------------------------------------
385
+ def getTrainFuzzy(self) -> np.array:
386
+ return self.trainFuzzificado
387
+
388
+ # -------------------------------------------------------------------------
389
+ # NOMBRE: getClasses
390
+ # DESCRIPCIÓN:
391
+ # Retorna el vector con las clases reales de cada ejemplo del dataset.
392
+ # SALIDA:
393
+ # - [np.array]: Vector de enteros con las clases.
394
+ # -------------------------------------------------------------------------
395
+ def getClasses(self) -> np.array:
396
+ return self.todasLasClases