aisp 0.1.32__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
@@ -6,6 +6,7 @@ from collections import namedtuple
6
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:
@@ -424,8 +426,40 @@ class RNSA(Base):
424
426
  C = np.append(C, [max(average_distance, key=average_distance.get)])
425
427
  return C
426
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
+
427
461
  def __checks_valid_detector(self, X: npt.NDArray = None, vector_x: npt.NDArray = None,
428
- samples_index_class: npt.NDArray = None):
462
+ samples_index_class: npt.NDArray = None):
429
463
  """
430
464
  Function to check if the detector has a valid non-proper ``r`` radius for the class.
431
465
 
@@ -456,26 +490,25 @@ class RNSA(Base):
456
490
  * Validade (``bool``): Retorna se o detector é válido ou não.
457
491
 
458
492
  """
459
- # Se um ou mais array de entrada possuir zero dados, retorna falso.
493
+ # If any of the input arrays have zero size, returns false.
460
494
  if (np.size(samples_index_class) == 0 or np.size(X) == 0 or np.size(vector_x) == 0):
461
495
  return False
462
- # se self.k > 1 utiliza os k vizinhos mais próximos (knn), se não verifica o detector sem
463
- # considerar os knn.
496
+ # If self.k > 1, uses the k nearest neighbors (kNN); otherwise, checks the detector
497
+ # without considering kNN.
464
498
  if self.k > 1:
465
- # Iniciar a lista dos knn vazia.
466
499
  knn_list = np.empty(shape=0)
467
500
  for i in samples_index_class:
468
- # Calcula a distância entre os dois vetores e adiciona a lista dos knn, se a
469
- # 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.
470
503
  knn_list = self.__compare_KnearestNeighbors_List(
471
504
  knn_list, self.__distance(X[i], vector_x)
472
505
  )
473
- # 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.
474
507
  distance_mean = np.mean(knn_list)
475
508
  if self._algorithm == "V-detector":
476
509
  return self.__detector_is_valid_to_Vdetector(distance_mean, vector_x)
477
510
  elif distance_mean > (self.r + self.r_s):
478
- return True # Detector é valido!
511
+ return True
479
512
  else:
480
513
  distance: Union[float, None] = None
481
514
  for i in samples_index_class:
@@ -486,16 +519,16 @@ class RNSA(Base):
486
519
  elif distance > new_distance:
487
520
  distance = new_distance
488
521
  else:
489
- # Calcula a distância entre os vetores, se menor ou igual ao raio + raio da
490
- # 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.
491
524
  if (self.r + self.r_s) >= self.__distance(X[i], vector_x):
492
525
  return False # Detector não é valido!
493
526
 
494
527
  if self._algorithm == "V-detector":
495
528
  return self.__detector_is_valid_to_Vdetector(distance, vector_x)
496
- return True # Detector é valido!
529
+ return True
497
530
 
498
- return False # Detector não é valido!
531
+ return False # Detector is not valid!
499
532
 
500
533
  def __compare_KnearestNeighbors_List(self, knn: npt.NDArray, distance: float) -> npt.NDArray:
501
534
  """
@@ -527,12 +560,12 @@ class RNSA(Base):
527
560
  ---
528
561
  npt.NDArray: Lista de vizinhos mais próximos atualizada e ordenada.
529
562
  """
530
- # 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.
531
564
  if len(knn) < self.k:
532
565
  knn = np.append(knn, distance)
533
566
  knn.sort()
534
567
  else:
535
- # 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.
536
569
  if knn[self.k - 1] > distance:
537
570
  knn[self.k - 1] = distance
538
571
  knn.sort()
@@ -576,16 +609,14 @@ class RNSA(Base):
576
609
  a nenhuma classe.
577
610
  """
578
611
 
579
- # 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.
580
613
  possible_classes = []
581
614
  for _class_ in self.classes:
582
- # Variável para identificar, se a classe foi encontrada com os detectores.
615
+ # Variable to indicate if the class was found with the detectors.
583
616
  class_found: bool = True
584
- sum_distance = 0 # Variável para fazer o somatório das distâncias.
617
+ sum_distance = 0
585
618
  for detector in self.detectors[_class_]:
586
- # Calcula a distância entre a amostra e os detectores.
587
619
  distance = self.__distance(detector.position, line)
588
- # Soma as distâncias para calcular a média.
589
620
  sum_distance += distance
590
621
  if self._algorithm == "V-detector":
591
622
  if distance <= detector.radius:
@@ -595,17 +626,16 @@ class RNSA(Base):
595
626
  class_found = False
596
627
  break
597
628
 
598
- # Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
599
- # possível previsão.
629
+ # If the sample passes through all the detectors of a class, adds the class as a possible prediction.
600
630
  if class_found:
601
631
  possible_classes.append([_class_, sum_distance / self.N])
602
- # Se classificar como pertencentes a apenas uma classe, retorna a classe.
632
+ # If classified as belonging to only one class, returns the class.
603
633
  if len(possible_classes) == 1:
604
634
  return possible_classes[0][0]
605
- # 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.
606
636
  elif len(possible_classes) > 1:
607
637
  return max(possible_classes, key=lambda x: x[1])[0]
608
- else: # Se não, retorna None
638
+ else:
609
639
  return None
610
640
 
611
641
  def __distance(self, u: npt.NDArray, v: npt.NDArray):
@@ -673,47 +703,15 @@ class RNSA(Base):
673
703
  """
674
704
  new_detector_r = float(distance - self.r_s)
675
705
  if self.r >= new_detector_r:
676
- return False # Detector não é valido!
706
+ return False
677
707
  else:
678
- # 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.
679
709
  if self._cell_bounds:
680
710
  for p in vector_x:
681
711
  if (p - new_detector_r) < 0 or (p + new_detector_r) > 1:
682
712
  return False
683
713
  return True, new_detector_r
684
714
 
685
- def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
686
- """
687
- The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
688
- according to the output class, to loop through the sample array, only in positions where \
689
- the output is the class being trained.
690
-
691
- Parameters:
692
- ---
693
- * y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
694
- ``X`` sample array.
695
-
696
- returns:
697
- ---
698
- * dict: A dictionary with the list of array positions(``y``), with the classes as key.
699
-
700
- ---
701
-
702
- A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
703
- de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
704
- está sendo treinada.
705
-
706
- Parameters:
707
- ---
708
- * y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
709
- de amostra ``X``.
710
-
711
- Returns:
712
- ---
713
- * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
714
- """
715
- return super()._slice_index_list_by_class(y)
716
-
717
715
  def score(self, X: npt.NDArray, y: list) -> float:
718
716
  """
719
717
  Score function calculates forecast accuracy.
@@ -931,41 +929,41 @@ class BNSA(Base):
931
929
  """
932
930
  super()._check_and_raise_exceptions_fit(X, y, "BNSA")
933
931
 
934
- # Converte todo o array X para boolean
932
+ # Converts the entire array X to boolean
935
933
  if X.dtype != bool:
936
934
  X = X.astype(bool)
937
935
 
938
- # Identificando as classes possíveis, dentro do array de saídas ``y``.
936
+ # Identifying the possible classes within the output array `y`.
939
937
  self.classes = np.unique(y)
940
- # Dict que armazenará os detectores com as classes como key.
938
+ # Dictionary that will store detectors with classes as keys.
941
939
  list_detectors_by_class = dict()
942
- # Separa as classes para o treinamento.
940
+ # Separates the classes for training.
943
941
  sample_index: dict = self.__slice_index_list_by_class(y)
944
- # Barra de progresso para a geração de todos os detectores.
942
+ # Progress bar for generating all detectors.
945
943
  if verbose:
946
944
  progress = tqdm(total=int(self.N * (len(self.classes))),
947
945
  bar_format='{desc} ┇{bar}┇ {n}/{total} detectors', postfix='\n')
948
946
 
949
947
  for _class_ in self.classes:
950
- # Inicia o conjunto vazio que conterá os detectores válidos.
948
+ # Initializes the empty set that will contain the valid detectors.
951
949
  valid_detectors_set: list = []
952
950
  discard_count: int = 0
953
- # 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.
954
952
  if verbose:
955
953
  progress.set_description_str(
956
954
  f"Generating the detectors for the {_class_} class:")
957
955
  while len(valid_detectors_set) < self.N:
958
956
 
959
957
  is_valid_detector: bool = True
960
- # Gera um vetor candidato a detector aleatoriamente com valores 0 e 1.
958
+ # Generates a candidate detector vector randomly with values 0 and 1.
961
959
  vector_x = np.random.choice([False, True], size=X.shape[1])
962
- # Calcula a distância entre o candidato e as amostras da classe.
963
- distances = cdist(np.expand_dims(vector_x, axis=0),
960
+ # Calculates the distance between the candidate and the class samples.
961
+ distances = cdist(np.expand_dims(vector_x, axis=0),
964
962
  X[sample_index[_class_]], metric='hamming')
965
- # Verifica se alguma das distâncias está abaixo ou igual ao limiar
963
+ # Checks if any of the distances is below or equal to the threshold.
966
964
  is_valid_detector = not np.any(distances <= self.aff_thresh)
967
965
 
968
- # Se o detector for válido, adicione a lista dos válidos.
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,45 +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
-
1061
- # Calcula a distância de Hamming entre a linha e todos os detectores
1057
+ # Calculates the Hamming distance between the row and all detectors.
1062
1058
  distances = cdist(np.expand_dims(line, axis=0),
1063
1059
  self.detectors[_class_], metric='hamming')
1064
1060
 
1065
- # Verificar se alguma distância está abaixo ou igual ao limiar
1061
+ # Check if any distance is below or equal to the threshold.
1066
1062
  if np.any(distances <= self.aff_thresh):
1067
1063
  class_found = False
1068
1064
  else:
1069
- # Somar todas as distâncias
1070
1065
  similarity_sum = np.sum(distances)
1071
1066
 
1072
- # Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
1073
- # possível previsão e sua media de similaridade.
1067
+ # If the sample passes through all detectors of a class, adds the class as a possible prediction
1068
+ # and its average similarity.
1074
1069
  if class_found:
1075
1070
  possible_classes.append([_class_, similarity_sum / self.N])
1076
1071
 
1077
- # 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.
1078
1073
  if len(possible_classes) > 0:
1079
1074
  C = np.append(
1080
1075
  C, [max(possible_classes, key=lambda x: x[1])[0]])
@@ -1082,24 +1077,24 @@ class BNSA(Base):
1082
1077
  else:
1083
1078
  class_found = False
1084
1079
 
1085
- # 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.
1086
1081
  if not class_found and len(self.classes) == 1:
1087
1082
  C = np.append(C, ["non-self"])
1088
- # Se a classe não puder ser identificada pelos detectores
1083
+ # If the class cannot be identified by the detectors
1089
1084
  elif not class_found:
1090
1085
  class_differences: dict = {}
1091
1086
  for _class_ in self.classes:
1092
- # 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.
1093
1088
  if self.no_label_sample_selection == 'nearest_difference':
1094
- difference_min: float = cdist( np.expand_dims(line, axis=0),
1095
- self.detectors[_class_], metric='hamming'
1096
- ).min()
1089
+ difference_min: float = cdist(np.expand_dims(line, axis=0),
1090
+ self.detectors[_class_], metric='hamming'
1091
+ ).min()
1097
1092
  class_differences[_class_] = difference_min
1098
- # Ou com base na maior distância com relação à média da distancias dos detectores
1093
+ # Or based on the greatest distance from the average distances of the detectors.
1099
1094
  else:
1100
- difference_sum: float = cdist( np.expand_dims(line, axis=0),
1101
- self.detectors[_class_], metric='hamming'
1102
- ).sum()
1095
+ difference_sum: float = cdist(np.expand_dims(line, axis=0),
1096
+ self.detectors[_class_], metric='hamming'
1097
+ ).sum()
1103
1098
  class_differences[_class_] = difference_sum / self.N
1104
1099
 
1105
1100
  C = np.append(C, [max(class_differences, key=class_differences.get)])
@@ -1136,7 +1131,7 @@ class BNSA(Base):
1136
1131
  ---
1137
1132
  * dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
1138
1133
  """
1139
- return super()._slice_index_list_by_class(y)
1134
+ return slice_index_list_by_class(self.classes, y)
1140
1135
 
1141
1136
  def score(self, X: npt.NDArray, y: list) -> float:
1142
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.32
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=bYbkorRTY3V7_gS0L1QnYxpoy-KGsGDxwhza0RjhcKM,55330
4
- aisp-0.1.32.dist-info/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
5
- aisp-0.1.32.dist-info/METADATA,sha256=0SxlcxhAKPH1XSkIENEp2zIxu_YKvq-yqSLu-dISNsI,7999
6
- aisp-0.1.32.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
- aisp-0.1.32.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
8
- aisp-0.1.32.dist-info/RECORD,,
File without changes