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.
- farc_hd-0.1.0/PKG-INFO +15 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Apriori.py +198 -0
- farc_hd-0.1.0/farc_hd/FARCHD/BullWarmUp.py +32 -0
- farc_hd-0.1.0/farc_hd/FARCHD/DataBase.py +396 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Fuzzy.py +172 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Individual.py +428 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Item.py +118 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Itemset.py +217 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Population.py +271 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Rule.py +297 -0
- farc_hd-0.1.0/farc_hd/FARCHD/RuleBase.py +948 -0
- farc_hd-0.1.0/farc_hd/FARCHD/Utilidades.py +99 -0
- farc_hd-0.1.0/farc_hd/FARCHD/WarmUpParameters.py +32 -0
- farc_hd-0.1.0/farc_hd/FARCHD/__init__.py +0 -0
- farc_hd-0.1.0/farc_hd/FARCHD/myDataSetV2.py +543 -0
- farc_hd-0.1.0/farc_hd/FARCHD/parseParameters.py +149 -0
- farc_hd-0.1.0/farc_hd/FARCHD/utils.py +257 -0
- farc_hd-0.1.0/farc_hd/FarcHDClassifier.py +278 -0
- farc_hd-0.1.0/farc_hd/__init__.py +0 -0
- farc_hd-0.1.0/farc_hd/org/core/Fichero.py +68 -0
- farc_hd-0.1.0/farc_hd/org/core/Files.py +65 -0
- farc_hd-0.1.0/farc_hd/org/core/MTwister.py +173 -0
- farc_hd-0.1.0/farc_hd/org/core/Randomize.py +160 -0
- farc_hd-0.1.0/farc_hd/org/core/__init__.py +0 -0
- farc_hd-0.1.0/farc_hd.egg-info/PKG-INFO +15 -0
- farc_hd-0.1.0/farc_hd.egg-info/SOURCES.txt +29 -0
- farc_hd-0.1.0/farc_hd.egg-info/dependency_links.txt +1 -0
- farc_hd-0.1.0/farc_hd.egg-info/requires.txt +3 -0
- farc_hd-0.1.0/farc_hd.egg-info/top_level.txt +1 -0
- farc_hd-0.1.0/pyproject.toml +25 -0
- 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
|