aisp 0.1.31__py3-none-any.whl → 0.1.33__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.
aisp/NSA/__init__.py CHANGED
@@ -2,4 +2,4 @@ from ._negative_selection import BNSA, RNSA
2
2
 
3
3
  __author__ = "João Paulo da Silva Barros"
4
4
  __all__ = ["RNSA", "BNSA"]
5
- __version__ = "0.1.31"
5
+ __version__ = "0.1.33"
aisp/NSA/_base.py CHANGED
@@ -1,8 +1,12 @@
1
+ from abc import abstractmethod
2
+
1
3
  import numpy as np
2
4
  import numpy.typing as npt
3
- from typing import Literal
5
+ from typing import Literal, Optional
4
6
  from scipy.spatial.distance import euclidean, cityblock, minkowski
5
7
 
8
+ from ..utils.metrics import accuracy_score
9
+
6
10
 
7
11
  class Base:
8
12
  """
@@ -93,8 +97,11 @@ class Base:
93
97
  else:
94
98
  return euclidean(u, v)
95
99
 
96
- def _check_and_raise_exceptions_fit(self, X: npt.NDArray = None, y: npt.NDArray = None,
97
- _class_: Literal["RNSA", "BNSA"] = "RNSA",
100
+ @staticmethod
101
+ def _check_and_raise_exceptions_fit(
102
+ X: npt.NDArray = None,
103
+ y: npt.NDArray = None,
104
+ _class_: Literal["RNSA", "BNSA"] = "RNSA",
98
105
  ):
99
106
  """
100
107
  Function responsible for verifying fit function parameters and throwing exceptions if the \
@@ -149,43 +156,6 @@ class Base:
149
156
  "The array X contains values that are not composed only of 0 and 1."
150
157
  )
151
158
 
152
- def _slice_index_list_by_class(self, y: npt.NDArray) -> dict:
153
- """
154
- The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
155
- according to the output class, to loop through the sample array, only in positions where \
156
- the output is the class being trained.
157
-
158
- Parameters:
159
- ---
160
- * y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
161
- ``X`` sample array.
162
-
163
- returns:
164
- ---
165
- * dict: A dictionary with the list of array positions(``y``), with the classes as key.
166
-
167
- ---
168
-
169
- A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a \
170
- classe de saída, para percorrer o array de amostra, apenas nas posições que a saída for \
171
- a classe que está sendo treinada.
172
-
173
- Parameters:
174
- ---
175
- * y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do \
176
- array de amostra ``X``.
177
-
178
- Returns:
179
- ---
180
- * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
181
- """
182
- position_samples = dict()
183
- for _class_ in self.classes:
184
- # Pega as posições das amostras por classes a partir do y.
185
- position_samples[_class_] = list(np.where(y == _class_)[0])
186
-
187
- return position_samples
188
-
189
159
  def _score(self, X: npt.NDArray, y: list) -> float:
190
160
  """
191
161
  Score function calculates forecast accuracy.
@@ -237,4 +207,75 @@ class Base:
237
207
  if len(y) == 0:
238
208
  return 0
239
209
  y_pred = self.predict(X)
240
- return np.sum(y == y_pred) / len(y)
210
+ return accuracy_score(y, y_pred)
211
+
212
+ @abstractmethod
213
+ def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
214
+ """
215
+ Function to train the model using the input data ``X`` and corresponding labels ``y``.
216
+
217
+ This abstract method is implemented by the class that inherits it.
218
+
219
+ Parameters:
220
+ ---
221
+ * X (``npt.NDArray``): Input data used for training the model, previously normalized to the range [0, 1].
222
+ * y (``npt.NDArray``): Corresponding labels or target values for the input data.
223
+ * verbose (``bool``, optional): Flag to enable or disable detailed output during \
224
+ training. Default is ``True``.
225
+
226
+ Returns:
227
+ ---
228
+ * self: Returns the instance of the class that implements this method.
229
+
230
+ ---
231
+
232
+ Função para treinar o modelo usando os dados de entrada ``X`` e os classes correspondentes ``y``.
233
+
234
+ Este método abstrato é implementado pela classe que o herdar.
235
+
236
+ Parâmetros:
237
+ ---
238
+ * X (``npt.NDArray``): Dados de entrada utilizados para o treinamento do modelo, previamente \
239
+ normalizados no intervalo [0, 1].
240
+ * y (``npt.NDArray``): Rótulos ou valores-alvo correspondentes aos dados de entrada.
241
+ * verbose (``bool``, opcional): Flag para ativar ou desativar a saída detalhada durante o \
242
+ treinamento. O padrão é ``True``.
243
+
244
+ Retornos:
245
+ ---
246
+ * self: Retorna a instância da classe que implementa este método.
247
+ """
248
+ pass
249
+
250
+ @abstractmethod
251
+ def predict(self, X) -> Optional[npt.NDArray]:
252
+ """
253
+ Function to generate predictions based on the input data ``X``.
254
+
255
+ This abstract method is implemented by the class that inherits it.
256
+
257
+ Parameters:
258
+ ---
259
+ * X (``npt.NDArray``): Input data for which predictions will be generated.
260
+
261
+ Returns:
262
+ ---
263
+ * Predictions (``Optional[npt.NDArray]``): Predicted values for each input sample, or ``None``
264
+ if the prediction fails.
265
+
266
+ ---
267
+
268
+ Função para gerar previsões com base nos dados de entrada ``X``.
269
+
270
+ Este método abstrato é implementado pela classe que o herdar.
271
+
272
+ Parâmetros:
273
+ ---
274
+ * X (``npt.NDArray``): Dados de entrada para os quais as previsões serão geradas.
275
+
276
+ Retornos:
277
+ ---
278
+ * Previsões (``Optional[npt.NDArray]``): Valores previstos para cada amostra de entrada,
279
+ ou ``None`` se a previsão falhar.
280
+ """
281
+ pass
@@ -3,9 +3,10 @@ import numpy.typing as npt
3
3
  from tqdm import tqdm
4
4
  from typing import Dict, Literal, Optional, Union
5
5
  from collections import namedtuple
6
- from scipy.spatial.distance import hamming
6
+ from scipy.spatial.distance import cdist
7
7
 
8
8
  from ._base import Base
9
+ from ..utils import slice_index_list_by_class
9
10
 
10
11
 
11
12
  class RNSA(Base):
@@ -236,7 +237,7 @@ class RNSA(Base):
236
237
  self.r_s: float = 0
237
238
 
238
239
  if algorithm == "V-detector":
239
- self._Detector = namedtuple("Detector", "position radius")
240
+ self._Detector = namedtuple("Detector", ["position", "radius"])
240
241
  self._algorithm: str = algorithm
241
242
  else:
242
243
  self._Detector = namedtuple("Detector", "position")
@@ -247,12 +248,12 @@ class RNSA(Base):
247
248
  else:
248
249
  self.max_discards: int = 1000
249
250
 
250
- # Obtém as variáveis do kwargs.
251
+ # Retrieves the variables from kwargs.
251
252
  self.p: float = kwargs.get("p", 2)
252
253
  self._cell_bounds: bool = kwargs.get("cell_bounds", False)
253
254
  self.non_self_label: str = kwargs.get("non_self_label", "non-self")
254
255
 
255
- # Inicializa as demais variáveis da classe como None
256
+ # Initializes the other class variables as None.
256
257
  self.detectors: Union[dict, None] = None
257
258
  self.classes: npt.NDArray = None
258
259
 
@@ -286,36 +287,37 @@ class RNSA(Base):
286
287
  ---
287
288
  (``self``): Retorna a própria instância.
288
289
  """
290
+ progress = None
289
291
  super()._check_and_raise_exceptions_fit(X, y)
290
292
 
291
- # Identificando as classes possíveis, dentro do array de saídas ``y``.
293
+ # Identifying the possible classes within the output array `y`.
292
294
  self.classes = np.unique(y)
293
- # Dict que armazenará os detectores com as classes como key.
295
+ # Dictionary that will store detectors with classes as keys.
294
296
  list_detectors_by_class = dict()
295
- # Separa as classes para o treinamento.
297
+ # Separates the classes for training.
296
298
  sample_index = self.__slice_index_list_by_class(y)
297
- # Barra de progresso para a geração de todos os detectores.
299
+ # Progress bar for generating all detectors.
298
300
  if verbose:
299
301
  progress = tqdm(total=int(self.N * (len(self.classes))),
300
- bar_format="{desc} ┇{bar}┇ {n}/{total} detectors", postfix="\n",)
302
+ bar_format="{desc} ┇{bar}┇ {n}/{total} detectors", postfix="\n", )
301
303
  for _class_ in self.classes:
302
- # Inicia o conjunto vazio que conterá os detectores válidos.
304
+ # Initializes the empty set that will contain the valid detectors.
303
305
  valid_detectors_set = []
304
306
  discard_count = 0
305
- # Informando em qual classe o algoritmo está para a barra de progresso.
307
+ # Indicating which class the algorithm is currently processing for the progress bar.
306
308
  if verbose:
307
309
  progress.set_description_str(
308
310
  f"Generating the detectors for the {_class_} class:"
309
311
  )
310
312
  while len(valid_detectors_set) < self.N:
311
- # Gera um vetor candidato a detector aleatoriamente com valores entre 0 e 1.
313
+ # Generates a candidate detector vector randomly with values between 0 and 1.
312
314
  vector_x = np.random.random_sample(size=X.shape[1])
313
- # Verifica a validade do detector para o não-próprio com relação às amostras da classe.
315
+ # Checks the validity of the detector for non-self with respect to the class samples.
314
316
  valid_detector = self.__checks_valid_detector(
315
317
  X=X, vector_x=vector_x, samples_index_class=sample_index[_class_]
316
318
  )
317
319
 
318
- # Se o detector for válido, adicione a lista dos válidos.
320
+ # If the detector is valid, add it to the list of valid detectors.
319
321
  if self._algorithm == "V-detector" and valid_detector is not False:
320
322
  discard_count = 0
321
323
  valid_detectors_set.append(
@@ -338,15 +340,15 @@ class RNSA(Base):
338
340
  "consider reducing its value."
339
341
  )
340
342
 
341
- # Adicionar detectores, com as classes como chave na dict.
343
+ # Add detectors, with classes as keys in the dictionary.
342
344
  list_detectors_by_class[_class_] = valid_detectors_set
343
- # Informar a finalização da geração dos detectores para as classes.
345
+ # Notify completion of detector generation for the classes.
344
346
  if verbose:
345
347
  progress.set_description(
346
348
  f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
347
349
  f'successfully generated\033[0m'
348
350
  )
349
- # Armazena os detectores encontrados no atributo, para os detectores da classe.
351
+ # Saves the found detectors in the attribute for the non-self detectors of the trained model.
350
352
  self.detectors = list_detectors_by_class
351
353
  return self
352
354
 
@@ -382,7 +384,7 @@ class RNSA(Base):
382
384
  contendo as classes previstas para ``X``.
383
385
  * ``None``: Se não existir detectores para a previsão.
384
386
  """
385
- # se não houver detectores retorna None.
387
+ # If there are no detectors, returns None.
386
388
  if self.detectors is None:
387
389
  return None
388
390
  elif not isinstance(X, (np.ndarray, list)):
@@ -394,9 +396,9 @@ class RNSA(Base):
394
396
  )
395
397
  )
396
398
 
397
- # Inicia um array vazio.
399
+ # Initializes an empty array that will store the predictions.
398
400
  C = np.empty(shape=0)
399
- # Para cada linha de amostra em X.
401
+ # For each sample row in X.
400
402
  for line in X:
401
403
  class_found: bool
402
404
  _class_ = self.__compare_sample_to_detectors(line)
@@ -406,11 +408,11 @@ class RNSA(Base):
406
408
  C = np.append(C, [_class_])
407
409
  class_found = True
408
410
 
409
- # Se possuir apenas uma classe e não classificar a amostra define a saída como não-própria.
411
+ # If there is only one class and the sample is not classified, set the output as non-self.
410
412
  if not class_found and len(self.classes) == 1:
411
413
  C = np.append(C, [self.non_self_label])
412
- # Se não identificar a classe com os detectores, coloca a classe com a maior distância
413
- # da média dos seus detectores.
414
+ # If the class is not identified with the detectors, assign the class with the greatest distance
415
+ # from the mean of its detectors.
414
416
  elif not class_found:
415
417
  average_distance: dict = {}
416
418
  for _class_ in self.classes:
@@ -421,12 +423,43 @@ class RNSA(Base):
421
423
  [self.__distance(detector, line)
422
424
  for detector in detectores]
423
425
  )
424
- C = np.append(
425
- C, [max(average_distance, key=average_distance.get)])
426
+ C = np.append(C, [max(average_distance, key=average_distance.get)])
426
427
  return C
427
428
 
429
+ def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
430
+ """
431
+ The function ``__slice_index_list_by_class(...)``, separates the indices of the lines according \
432
+ to the output class, to loop through the sample array, only in positions where the output is \
433
+ the class being trained.
434
+
435
+ Parameters:
436
+ ---
437
+ * y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
438
+ ``X`` sample array.
439
+
440
+ returns:
441
+ ---
442
+ * dict: A dictionary with the list of array positions(``y``), with the classes as key.
443
+
444
+ ---
445
+
446
+ A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
447
+ de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
448
+ está sendo treinada.
449
+
450
+ Parameters:
451
+ ---
452
+ * y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
453
+ de amostra ``X``.
454
+
455
+ Returns:
456
+ ---
457
+ * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
458
+ """
459
+ return slice_index_list_by_class(self.classes, y)
460
+
428
461
  def __checks_valid_detector(self, X: npt.NDArray = None, vector_x: npt.NDArray = None,
429
- samples_index_class: npt.NDArray = None):
462
+ samples_index_class: npt.NDArray = None):
430
463
  """
431
464
  Function to check if the detector has a valid non-proper ``r`` radius for the class.
432
465
 
@@ -457,26 +490,25 @@ class RNSA(Base):
457
490
  * Validade (``bool``): Retorna se o detector é válido ou não.
458
491
 
459
492
  """
460
- # Se um ou mais array de entrada possuir zero dados, retorna falso.
493
+ # If any of the input arrays have zero size, returns false.
461
494
  if (np.size(samples_index_class) == 0 or np.size(X) == 0 or np.size(vector_x) == 0):
462
495
  return False
463
- # se self.k > 1 utiliza os k vizinhos mais próximos (knn), se não verifica o detector sem
464
- # considerar os knn.
496
+ # If self.k > 1, uses the k nearest neighbors (kNN); otherwise, checks the detector
497
+ # without considering kNN.
465
498
  if self.k > 1:
466
- # Iniciar a lista dos knn vazia.
467
499
  knn_list = np.empty(shape=0)
468
500
  for i in samples_index_class:
469
- # Calcula a distância entre os dois vetores e adiciona a lista dos knn, se a
470
- # distância for menor que a maior da lista.
501
+ # Calculates the distance between the two vectors and adds it to the kNN list if the
502
+ # distance is smaller than the largest distance in the list.
471
503
  knn_list = self.__compare_KnearestNeighbors_List(
472
504
  knn_list, self.__distance(X[i], vector_x)
473
505
  )
474
- # Se a média das distâncias na lista dos knn, for menor que o raio, retorna verdadeiro.
506
+ # If the average of the distances in the kNN list is less than the radius, returns true.
475
507
  distance_mean = np.mean(knn_list)
476
508
  if self._algorithm == "V-detector":
477
509
  return self.__detector_is_valid_to_Vdetector(distance_mean, vector_x)
478
510
  elif distance_mean > (self.r + self.r_s):
479
- return True # Detector é valido!
511
+ return True
480
512
  else:
481
513
  distance: Union[float, None] = None
482
514
  for i in samples_index_class:
@@ -487,16 +519,16 @@ class RNSA(Base):
487
519
  elif distance > new_distance:
488
520
  distance = new_distance
489
521
  else:
490
- # Calcula a distância entre os vetores, se menor ou igual ao raio + raio da
491
- # amostra define a validade do detector como falso.
522
+ # Calculates the distance between the vectors; if it is less than or equal to the radius
523
+ # plus the sample's radius, sets the validity of the detector to false.
492
524
  if (self.r + self.r_s) >= self.__distance(X[i], vector_x):
493
525
  return False # Detector não é valido!
494
526
 
495
527
  if self._algorithm == "V-detector":
496
528
  return self.__detector_is_valid_to_Vdetector(distance, vector_x)
497
- return True # Detector é valido!
529
+ return True
498
530
 
499
- return False # Detector não é valido!
531
+ return False # Detector is not valid!
500
532
 
501
533
  def __compare_KnearestNeighbors_List(self, knn: npt.NDArray, distance: float) -> npt.NDArray:
502
534
  """
@@ -528,12 +560,12 @@ class RNSA(Base):
528
560
  ---
529
561
  npt.NDArray: Lista de vizinhos mais próximos atualizada e ordenada.
530
562
  """
531
- # Se a quantidade de distâncias em knn, for menor que k, adiciona a distância.
563
+ # If the number of distances in kNN is less than k, adds the distance.
532
564
  if len(knn) < self.k:
533
565
  knn = np.append(knn, distance)
534
566
  knn.sort()
535
567
  else:
536
- # Se não, adicione a distância, se a nova distancia for menor que a maior distância da lista.
568
+ # Otherwise, add the distance if the new distance is smaller than the largest distance in the list.
537
569
  if knn[self.k - 1] > distance:
538
570
  knn[self.k - 1] = distance
539
571
  knn.sort()
@@ -577,16 +609,14 @@ class RNSA(Base):
577
609
  a nenhuma classe.
578
610
  """
579
611
 
580
- # Lista para armazenar as classes e a distância média entre os detectores e a amostra.
612
+ # List to store the classes and the average distance between the detectors and the sample.
581
613
  possible_classes = []
582
614
  for _class_ in self.classes:
583
- # Variável para identificar, se a classe foi encontrada com os detectores.
615
+ # Variable to indicate if the class was found with the detectors.
584
616
  class_found: bool = True
585
- sum_distance = 0 # Variável para fazer o somatório das distâncias.
617
+ sum_distance = 0
586
618
  for detector in self.detectors[_class_]:
587
- # Calcula a distância entre a amostra e os detectores.
588
619
  distance = self.__distance(detector.position, line)
589
- # Soma as distâncias para calcular a média.
590
620
  sum_distance += distance
591
621
  if self._algorithm == "V-detector":
592
622
  if distance <= detector.radius:
@@ -596,17 +626,16 @@ class RNSA(Base):
596
626
  class_found = False
597
627
  break
598
628
 
599
- # Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
600
- # possível previsão.
629
+ # If the sample passes through all the detectors of a class, adds the class as a possible prediction.
601
630
  if class_found:
602
631
  possible_classes.append([_class_, sum_distance / self.N])
603
- # Se classificar como pertencentes a apenas uma classe, retorna a classe.
632
+ # If classified as belonging to only one class, returns the class.
604
633
  if len(possible_classes) == 1:
605
634
  return possible_classes[0][0]
606
- # Se, pertencer a mais de uma classe, retorna a classe com a distância média mais distante.
635
+ # If belonging to more than one class, returns the class with the greatest average distance.
607
636
  elif len(possible_classes) > 1:
608
637
  return max(possible_classes, key=lambda x: x[1])[0]
609
- else: # Se não, retorna None
638
+ else:
610
639
  return None
611
640
 
612
641
  def __distance(self, u: npt.NDArray, v: npt.NDArray):
@@ -674,47 +703,15 @@ class RNSA(Base):
674
703
  """
675
704
  new_detector_r = float(distance - self.r_s)
676
705
  if self.r >= new_detector_r:
677
- return False # Detector não é valido!
706
+ return False
678
707
  else:
679
- # se _cell_bounds igual a True, considera o detector esta dentro do limite do plano.
708
+ # If _cell_bounds is True, considers the detector to be within the plane bounds.
680
709
  if self._cell_bounds:
681
710
  for p in vector_x:
682
711
  if (p - new_detector_r) < 0 or (p + new_detector_r) > 1:
683
712
  return False
684
713
  return True, new_detector_r
685
714
 
686
- def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
687
- """
688
- The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
689
- according to the output class, to loop through the sample array, only in positions where \
690
- the output is the class being trained.
691
-
692
- Parameters:
693
- ---
694
- * y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
695
- ``X`` sample array.
696
-
697
- returns:
698
- ---
699
- * dict: A dictionary with the list of array positions(``y``), with the classes as key.
700
-
701
- ---
702
-
703
- A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
704
- de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
705
- está sendo treinada.
706
-
707
- Parameters:
708
- ---
709
- * y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
710
- de amostra ``X``.
711
-
712
- Returns:
713
- ---
714
- * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
715
- """
716
- return super()._slice_index_list_by_class(y)
717
-
718
715
  def score(self, X: npt.NDArray, y: list) -> float:
719
716
  """
720
717
  Score function calculates forecast accuracy.
@@ -932,40 +929,41 @@ class BNSA(Base):
932
929
  """
933
930
  super()._check_and_raise_exceptions_fit(X, y, "BNSA")
934
931
 
935
- # Converte todo o array X para boolean
932
+ # Converts the entire array X to boolean
936
933
  if X.dtype != bool:
937
934
  X = X.astype(bool)
938
935
 
939
- # Identificando as classes possíveis, dentro do array de saídas ``y``.
936
+ # Identifying the possible classes within the output array `y`.
940
937
  self.classes = np.unique(y)
941
- # Dict que armazenará os detectores com as classes como key.
938
+ # Dictionary that will store detectors with classes as keys.
942
939
  list_detectors_by_class = dict()
943
- # Separa as classes para o treinamento.
940
+ # Separates the classes for training.
944
941
  sample_index: dict = self.__slice_index_list_by_class(y)
945
- # Barra de progresso para a geração de todos os detectores.
942
+ # Progress bar for generating all detectors.
946
943
  if verbose:
947
944
  progress = tqdm(total=int(self.N * (len(self.classes))),
948
945
  bar_format='{desc} ┇{bar}┇ {n}/{total} detectors', postfix='\n')
949
946
 
950
947
  for _class_ in self.classes:
951
- # Inicia o conjunto vazio que conterá os detectores válidos.
948
+ # Initializes the empty set that will contain the valid detectors.
952
949
  valid_detectors_set: list = []
953
950
  discard_count: int = 0
954
- # Informando em qual classe o algoritmo está para a barra de progresso.
951
+ # Updating the progress bar with the current class the algorithm is processing.
955
952
  if verbose:
956
953
  progress.set_description_str(
957
954
  f"Generating the detectors for the {_class_} class:")
958
955
  while len(valid_detectors_set) < self.N:
959
956
 
960
957
  is_valid_detector: bool = True
961
- # Gera um vetor candidato a detector aleatoriamente com valores 0 e 1.
958
+ # Generates a candidate detector vector randomly with values 0 and 1.
962
959
  vector_x = np.random.choice([False, True], size=X.shape[1])
963
- for i in sample_index[_class_]:
964
- # Verifica a validade do detector para o não-próprio com relação às amostras da classe.
965
- if hamming(X[i], vector_x) <= self.aff_thresh:
966
- is_valid_detector = False
967
- break
968
- # Se o detector for válido, adicione a lista dos válidos.
960
+ # Calculates the distance between the candidate and the class samples.
961
+ distances = cdist(np.expand_dims(vector_x, axis=0),
962
+ X[sample_index[_class_]], metric='hamming')
963
+ # Checks if any of the distances is below or equal to the threshold.
964
+ is_valid_detector = not np.any(distances <= self.aff_thresh)
965
+
966
+ # If the detector is valid, add it to the list of valid detectors.
969
967
  if is_valid_detector:
970
968
  discard_count = 0
971
969
  valid_detectors_set.append(vector_x)
@@ -981,15 +979,15 @@ class BNSA(Base):
981
979
  "radius and consider reducing its value."
982
980
  )
983
981
 
984
- # Adicionar detectores, com as classes como chave na dict.
982
+ # Add detectors to the dictionary with classes as keys.
985
983
  list_detectors_by_class[_class_] = valid_detectors_set
986
984
 
987
- # Informar a finalização da geração dos detectores para as classes.
985
+ # Notify the completion of detector generation for the classes.
988
986
  if verbose:
989
987
  progress.set_description(
990
988
  f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
991
989
  f'successfully generated\033[0m')
992
- # Armazena os detectores encontrados no atributo, para os detectores da classe.
990
+ # Saves the found detectors in the attribute for the class detectors.
993
991
  self.detectors = list_detectors_by_class
994
992
  return self
995
993
 
@@ -1025,7 +1023,7 @@ class BNSA(Base):
1025
1023
  contendo as classes previstas para ``X``.
1026
1024
  * ``None``: Se não existir detectores para a previsão.
1027
1025
  """
1028
- # se não houver detectores retorna None.
1026
+ # If there are no detectors, returns None.
1029
1027
  if self.detectors is None:
1030
1028
  return None
1031
1029
  elif not isinstance(X, (np.ndarray, list)):
@@ -1036,40 +1034,42 @@ class BNSA(Base):
1036
1034
  len(self.detectors[self.classes[0]][0])
1037
1035
  )
1038
1036
  )
1039
- # Verifica se a matriz X contém apenas amostras binárias. Caso contrário, lança uma exceção.
1037
+ # Checks if matrix X contains only binary samples. Otherwise, raises an exception.
1040
1038
  if not np.isin(X, [0, 1]).all():
1041
1039
  raise ValueError(
1042
1040
  "The array X contains values that are not composed only of 0 and 1."
1043
1041
  )
1044
1042
 
1045
- # Converte todo o array X para boolean
1043
+ # Converts the entire array X to boolean.
1046
1044
  if X.dtype != bool:
1047
1045
  X = X.astype(bool)
1048
1046
 
1049
- # Inicia um array vazio.
1047
+ # Initializes an empty array that will store the predictions.
1050
1048
  C = np.empty(shape=0)
1051
- # Para cada linha de amostra em X.
1049
+ # For each sample row in X.
1052
1050
  for line in X:
1053
1051
  class_found: bool = True
1054
- # Lista para armazenar as possíveis classes às quais a amostra se adequou ao self na
1055
- # comparação com os detectores non-self.
1052
+ # List to store the possible classes to which the sample matches with self
1053
+ # when compared to the non-self detectors.
1056
1054
  possible_classes: list = []
1057
1055
  for _class_ in self.classes:
1058
- # Lista para armazenar as taxas de similaridade entre a amostra e os detectores.
1059
1056
  similarity_sum: float = 0
1060
- for detector in self.detectors[_class_]:
1061
- similarity = hamming(line, detector)
1062
- if similarity <= self.aff_thresh:
1063
- class_found = False
1064
- break
1065
- else:
1066
- similarity_sum += similarity
1067
- # Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
1068
- # possível previsão e sua media de similaridade.
1057
+ # Calculates the Hamming distance between the row and all detectors.
1058
+ distances = cdist(np.expand_dims(line, axis=0),
1059
+ self.detectors[_class_], metric='hamming')
1060
+
1061
+ # Check if any distance is below or equal to the threshold.
1062
+ if np.any(distances <= self.aff_thresh):
1063
+ class_found = False
1064
+ else:
1065
+ similarity_sum = np.sum(distances)
1066
+
1067
+ # If the sample passes through all detectors of a class, adds the class as a possible prediction
1068
+ # and its average similarity.
1069
1069
  if class_found:
1070
1070
  possible_classes.append([_class_, similarity_sum / self.N])
1071
1071
 
1072
- # Se, pertencer a uma ou mais classes, adiciona a classe com a distância média mais distante.
1072
+ # If belonging to one or more classes, adds the class with the greatest average distance.
1073
1073
  if len(possible_classes) > 0:
1074
1074
  C = np.append(
1075
1075
  C, [max(possible_classes, key=lambda x: x[1])[0]])
@@ -1077,28 +1077,27 @@ class BNSA(Base):
1077
1077
  else:
1078
1078
  class_found = False
1079
1079
 
1080
- # Se possuir apenas uma classe e não classificar a amostra define a saída como não-própria.
1080
+ # If there is only one class and the sample is not classified, sets the output as non-self.
1081
1081
  if not class_found and len(self.classes) == 1:
1082
1082
  C = np.append(C, ["non-self"])
1083
- # Se a classe não puder ser identificada pelos detectores
1083
+ # If the class cannot be identified by the detectors
1084
1084
  elif not class_found:
1085
1085
  class_differences: dict = {}
1086
1086
  for _class_ in self.classes:
1087
- # Atribua-a o rotulo a classe com à maior distância em relação ao detector mais próximo.
1087
+ # Assign the label to the class with the greatest distance from the nearest detector.
1088
1088
  if self.no_label_sample_selection == 'nearest_difference':
1089
- differences: list = []
1090
- for detector in self.detectors[_class_]:
1091
- differences.append(hamming(line, detector))
1092
- class_differences[_class_] = min(differences)
1093
- # Ou com base na maior distância com relação à média da distancias dos detectores
1089
+ difference_min: float = cdist(np.expand_dims(line, axis=0),
1090
+ self.detectors[_class_], metric='hamming'
1091
+ ).min()
1092
+ class_differences[_class_] = difference_min
1093
+ # Or based on the greatest distance from the average distances of the detectors.
1094
1094
  else:
1095
- difference_sum: float = 0
1096
- for detector in self.detectors[_class_]:
1097
- difference_sum += hamming(line, detector)
1095
+ difference_sum: float = cdist(np.expand_dims(line, axis=0),
1096
+ self.detectors[_class_], metric='hamming'
1097
+ ).sum()
1098
1098
  class_differences[_class_] = difference_sum / self.N
1099
1099
 
1100
- C = np.append(
1101
- C, [max(class_differences, key=class_differences.get)])
1100
+ C = np.append(C, [max(class_differences, key=class_differences.get)])
1102
1101
 
1103
1102
  return C
1104
1103
 
@@ -1132,7 +1131,7 @@ class BNSA(Base):
1132
1131
  ---
1133
1132
  * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
1134
1133
  """
1135
- return super()._slice_index_list_by_class(y)
1134
+ return slice_index_list_by_class(self.classes, y)
1136
1135
 
1137
1136
  def score(self, X: npt.NDArray, y: list) -> float:
1138
1137
  """
aisp/utils/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from ._multiclass import slice_index_list_by_class
2
+
3
+ __author__ = "João Paulo da Silva Barros"
4
+ __all__ = ["slice_index_list_by_class"]
5
+ __version__ = "0.1.33"
@@ -0,0 +1,43 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import numpy.typing as npt
4
+
5
+
6
+ def slice_index_list_by_class(classes: Union[npt.NDArray, list], y: npt.NDArray) -> dict:
7
+ """
8
+ The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
9
+ according to the output class, to loop through the sample array, only in positions where \
10
+ the output is the class being trained.
11
+
12
+ Parameters:
13
+ ---
14
+ * classes (``list or npt.NDArray``): list with unique classes.
15
+ * y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
16
+ ``X`` sample array.
17
+
18
+ returns:
19
+ ---
20
+ * dict: A dictionary with the list of array positions(``y``), with the classes as key.
21
+
22
+ ---
23
+
24
+ A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a \
25
+ classe de saída, para percorrer o array de amostra, apenas nas posições que a saída for \
26
+ a classe que está sendo treinada.
27
+
28
+ Parameters:
29
+ ---
30
+ * classes (``list or npt.NDArray``): lista com classes únicas.
31
+ * y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do \
32
+ array de amostra ``X``.
33
+
34
+ Returns:
35
+ ---
36
+ * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
37
+ """
38
+ position_samples = dict()
39
+ for _class_ in classes:
40
+ # Gets the sample positions by class from y.
41
+ position_samples[_class_] = list(np.nonzero(y == _class_)[0])
42
+
43
+ return position_samples
aisp/utils/metrics.py ADDED
@@ -0,0 +1,61 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import numpy.typing as npt
4
+
5
+
6
+ def accuracy_score(
7
+ y_true: Union[npt.NDArray, list],
8
+ y_pred: Union[npt.NDArray, list]
9
+ ) -> float:
10
+ """
11
+ Function to calculate precision accuracy based on lists of true labels and
12
+ predicted labels.
13
+
14
+ Parameters:
15
+ ---
16
+
17
+ * y_true (``Union[npt.NDArray, list]``): Ground truth (correct) labels. \
18
+ Expected to be of the same length as `y_pred`.
19
+ * y_pred (``Union[npt.NDArray, list]``): Predicted labels. Expected to \
20
+ be of the same length as `y_true`.
21
+
22
+ Returns:
23
+ ---
24
+ * Accuracy (``float``): The ratio of correct predictions to the total \
25
+ number of predictions.
26
+
27
+ Raises:
28
+ ---
29
+ * ValueError: If `y_true` or `y_pred` are empty or if they do not have the same length.
30
+
31
+ ---
32
+
33
+ Função para calcular a acurácia de precisão com base em listas de rótulos
34
+ verdadeiros e nos rótulos previstos.
35
+
36
+ Parâmetros:
37
+ ---
38
+ * y_true (``Union[npt.NDArray, list]``): Rótulos verdadeiros (corretos)..
39
+ * y_pred (``Union[npt.NDArray, list]``): Rótulos previstos.
40
+
41
+ Retornos:
42
+ ---
43
+ * Precisão (``float``): A proporção de previsões corretas em relação
44
+ ao número total de previsões.
45
+
46
+ Lança:
47
+ ---
48
+ * ValueError: Se `y_true` ou `y_pred` estiverem vazios ou se não
49
+ tiverem o mesmo tamanho.
50
+ """
51
+ n = len(y_true)
52
+ if n == 0:
53
+ raise ValueError(
54
+ "Division by zero: y_true cannot be an empty list or array."
55
+ )
56
+ elif n != len(y_pred):
57
+ raise ValueError(
58
+ f"Error: The arrays must have the same size. Size of y_true: "
59
+ f"{len(y_true)}, Size of y_pred: {len(y_pred)}"
60
+ )
61
+ return np.sum(np.sum(np.array(y_true) == np.array(y_pred))) / n
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aisp
3
- Version: 0.1.31
3
+ Version: 0.1.33
4
4
  Summary: Package with techniques of artificial immune systems.
5
5
  Author-email: João Paulo da Silva Barros <jpsilvabarr@gmail.com>
6
6
  Maintainer-email: Alison Zille Lopes <alisonzille@gmail.com>
@@ -63,7 +63,6 @@ Requires-Dist: tqdm >=4.64.1
63
63
  > 2. [Installation.](#installation)
64
64
  > 1. [Dependencies](#dependencies)
65
65
  > 2. [User installation](#user-installation)
66
- > 3. [How to import the Techniques](#how-to-import-the-techniques)
67
66
  > 3. [Examples.](#examples)
68
67
 
69
68
  ---
@@ -81,8 +80,8 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
81
80
  ##### Algorithms implemented:
82
81
 
83
82
  > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
83
+ > - [ ] *Clonal Selection Algorithms.*
84
84
  > - [ ] *Dendritic Cells.*
85
- > - [ ] *Clonalg.*
86
85
  > - [ ] *Immune Network Theory.*
87
86
 
88
87
  </section>
@@ -119,17 +118,7 @@ pip install aisp
119
118
  ```
120
119
 
121
120
  </section>
122
- <section id='how-to-import-the-techniques'>
123
121
 
124
- ##### **How to import the Techniques**
125
-
126
- ``` Python
127
- from aisp.NSA import RNSA
128
-
129
- nsa = RNSA(N=300, r=0.05)
130
- ```
131
-
132
- </section>
133
122
  </section>
134
123
  <section id='examples'>
135
124
 
@@ -173,7 +162,6 @@ Below are some examples that use a database for classification with the [Jupyter
173
162
  > 2. [Instalação.](#instalação)
174
163
  > 1. [Dependências](#dependências)
175
164
  > 2. [Instalação do usuário](#instalação-do-usuário)
176
- > 3. [Como importar as Tecnicas](#como-importar-as-tecnicas)
177
165
  > 3. [Exemplos.](#exemplos)
178
166
 
179
167
  ---
@@ -190,8 +178,8 @@ Os sistemas imunológicos artificiais (SIA) inspiram-se no sistema imunológico
190
178
  ##### Algoritmos implementados:
191
179
 
192
180
  > - [x] [**Seleção Negativa.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
181
+ > - [ ] *Algoritmos de Seleção Clonal.*
193
182
  > - [ ] *Células Dendríticas.*
194
- > - [ ] *Clonalg.*
195
183
  > - [ ] *Teoria da Rede Imune.*
196
184
 
197
185
  </section>
@@ -229,17 +217,6 @@ pip install aisp
229
217
 
230
218
  </section>
231
219
 
232
- <section id='como-importar-as-tecnicas'>
233
-
234
- ##### **Como importar as Tecnicas**
235
-
236
- ``` Python
237
- from aisp.NSA import RNSA
238
-
239
- nsa = RNSA(N=300, r=0.05)
240
- ```
241
-
242
- </section>
243
220
  </section>
244
221
  <section id='exemplos'>
245
222
 
@@ -0,0 +1,11 @@
1
+ aisp/NSA/__init__.py,sha256=b0KSfocc9p4s4AQatyEBDfQuVmMqZ18rCieTUpWnQ7c,143
2
+ aisp/NSA/_base.py,sha256=iqmsLoM7yBC_EPFZXvdsQjI1bidz1A87ZFyht0P--fI,10861
3
+ aisp/NSA/_negative_selection.py,sha256=AqsOJYkZlb2kP48b35FFIwU_fGedLpZw5JY9hpnzkXM,54897
4
+ aisp/utils/__init__.py,sha256=c8OCaR9IAQ21vpxWqtLplB84W_UKdWceI--yw7sBMi0,163
5
+ aisp/utils/_multiclass.py,sha256=aoxSqnemNjs5uJAk7a1GumNDa-q-LOw6TWluwQ10tR4,1597
6
+ aisp/utils/metrics.py,sha256=xp9A52-QsP0cJGIlYprrI7BrVwQN6gqCLINVJumlIDI,1908
7
+ aisp-0.1.33.dist-info/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
8
+ aisp-0.1.33.dist-info/METADATA,sha256=M2czdHGPXbXXVOKUckfYMwwPpnj_Lt49dU5PRfLd_jw,7562
9
+ aisp-0.1.33.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
+ aisp-0.1.33.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
11
+ aisp-0.1.33.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- aisp/NSA/__init__.py,sha256=cmHx6ydTfhFAOfgCjDcKDJRfH5Sz41LHqVRlDZgVL58,141
2
- aisp/NSA/_base.py,sha256=HjdF4ZnophMY_Ooj2ERjhVoWwqe8Q4rPQ-UxHlfIeNY,9557
3
- aisp/NSA/_negative_selection.py,sha256=pWYgiyd0Jr0w1FIt6StQ_HEQr5-UjF4mVI0QJIcXeHM,54974
4
- aisp-0.1.31.dist-info/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
5
- aisp-0.1.31.dist-info/METADATA,sha256=hKXEal6xERzXfr3b7HX1VP43xq73f3tR1A1YAsIRics,7999
6
- aisp-0.1.31.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
- aisp-0.1.31.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
8
- aisp-0.1.31.dist-info/RECORD,,
File without changes