def test_predict_distance_weighted_knn(self): ds = DataSet() ds.load_from_arff('./../../datasets/simple_distance_example.arff') target_attribute = 'PlayTennis' X = ds.pandas_df.drop(columns=target_attribute) y = ds.pandas_df[target_attribute] instance = pd.Series({ 'humidity': 75, 'age': 30, 'temperature': 30, }) classifier = KNNClassifier(1, distance_weighted=True) classifier.fit(X, y) assert classifier.predict(instance) == 'YES' # por mas de que se tienen 2 NO y 1 YES, el YES es mas cercano classifier = KNNClassifier(3, distance_weighted=True) classifier.fit(X, y) assert classifier.predict(instance) == 'YES' # por mas de que se tienen 4 YES y 3 NO, los NO son mas cercanos y por tanto pesan mas classifier = KNNClassifier(7, distance_weighted=True) classifier.fit(X, y) assert classifier.predict(instance) == 'NO'
def test_predictions(self): """ Para todos los ejemplos de entrenamiento se debe cumplir que el valor predecido coincida con el valor dado """ ds = DataSet() ds.load_from_arff('../../datasets/tom_mitchell_example.arff') target_attribute = 'PlayTennis' # noinspection PyTypeChecker classifier = Classifier(select_attribute, target_attribute) classifier.fit(ds) for i in range(ds.pandas_df.shape[0]): instance = ds.pandas_df.loc[i] v = classifier.predict(instance) if instance[target_attribute] == 'YES': self.assertTrue( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' ) else: self.assertFalse( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' )
def test_id3_structure(self): ds = DataSet() ds.load_from_arff('../../datasets/tom_mitchell_example.arff') expected_tree_structure = load_expected_tree_structure() built_tree = id3(examples=ds, select_attribute=select_attribute, target_attribute='PlayTennis') assert equal_tree_structure(built_tree, expected_tree_structure)
def k_fold_cross_validation(ds: DataSet, target_attribute: str, k: int, fn_on_empty_value: callable, fn_on_continues_values: callable): # Se parte al conjunto original en k subconjuntos Ti # Se entrena k veces, utilizando a un Ti para validar y a la unión del resto para entrenar # Se toma el promedio de los errores de las k iteraciones. # Validación cruzada de tamaño k. # Partimos el conjunto de datos D en T1,...,Tk de igual tamaño # Para i=1 hasta k # Ei = D - Ti # hi = L(Ei) # δi = error(hi, Ti) # δ = (1/k)Σδi union_ti = pd.DataFrame() n = round(ds.pandas_df.__len__() / k) errors = [0 for i in range(k)] for i in range(k): diff_df_union_ti = ds.pandas_df.loc[ ~ds.pandas_df.index.isin(union_ti.index), :] test_df = diff_df_union_ti.sample(n=min(n, len(diff_df_union_ti))) union_ti = pd.concat([union_ti, test_df]) train_df = ds.pandas_df.loc[~ds.pandas_df.index.isin(test_df.index), :] train = DataSet() train.load_from_pandas_df(train_df, ds.attribute_info, ds.attribute_list) # noinspection PyTypeChecker classifier = Classifier(fn_on_continues_values, target_attribute, fn_on_empty_value) classifier.fit(train) logging.info(f'Arbol iteracion k = {i}') logging.info(RenderTree(classifier._decision_tree)) errors[i] = get_error(test_df, classifier, target_attribute, k) Error = 0 for i in range(k): Error = Error + errors[i] logging.info(f'Errores obtenidoes en iteracion k = {i}, Errores: {errors}') Error = (1 / k) * Error logging.info(f'Error total (1/k)*Error : {Error}') return Error
def predict(self, X: Union[DataFrame, Series]) -> Union[Series, Any]: """ Predict y value for a set of instances :param X: set of instances which y value wants to be predicted (use pandas Series for individual instances) :return: returns the list of predicted values for each instance in X (if X is just an individual instance it just returns its predicted value) """ try: getattr(self, "X_") (X_train_aux, target_attribute) = _generate_train_set(self.X_, self.y_, self.target_attribute) # noinspection PyPep8Naming X_train: DataSet = DataSet() X_train.load_from_pandas_df(X_train_aux, self.attribute_info, self.attribute_list) except AttributeError: raise RuntimeError("You must train classifier before predicting data!") # X is a set if X.__class__ == DataFrame: y = X.apply(lambda row: naive_bayes_classifier(X_train, row, self.target_attribute), axis=1) # X is an instance else: y = naive_bayes_classifier(X_train, X, self.target_attribute) return y
def select_attribute(examples: DataSet, target_attribute: str, node: Node) -> StrategyResult: """ Implementa una estrategia de seleccion de atributos basada en el calculo de ganancia segun entropia. No soporta atributos que tomen valores nulos. :param node: es el nodo para el cual se quiere seleccionar el atributo :param examples: conjunto de ejemplos de entrenamiento que se tienen al momento :param target_attribute: el atributo que se quiere predecir :return: devuelve el atributo seleccionado """ gain_max = -1 for a in [a for a in examples.attribute_list if a != target_attribute]: if examples.is_continuous_attribute(a): partitions = _split_continuous_values(examples, a, target_attribute) else: partitions = _split_discrete_values(examples, a) g = _gain(examples.pandas_df, [p.filter(examples).pandas_df for p in partitions], target_attribute) if g > gain_max: gain_max = g result = StrategyResult(a, partitions) # noinspection PyUnboundLocalVariable return result
def get_train_test(): #Separa 1/5 , 4/5 ds = DataSet() ds.load_from_arff('../../datasets/Autism-Adult-Data.arff') # Fixed by https: // eva.fing.edu.uy / mod / forum / discuss.php?d = 117656 ds.remove_attribute('result') train_pandas_df = ds.pandas_df.sample(frac=0.8) test_pandas_df = ds.pandas_df.loc[~ds.pandas_df.index.isin(train_pandas_df.index), :] train = DataSet() train.load_from_pandas_df(train_pandas_df, ds.attribute_info, ds.attribute_list) return (train, test_pandas_df)
def filter(self, ds: DataSet): ds_new = ds.copy() ds_new.pandas_df = ds_new.pandas_df[operator.and_( self._op1(self._lower_bound, ds_new.pandas_df[self.attribute]), self._op2(ds_new.pandas_df[self.attribute], self._upper_bound) )] return ds_new
def m_estimate(ds: DataSet, a:str, a_value, target_attribute: str, target_attribute_value: str): attributes_info = ds.attribute_info df = ds.pandas_df if ds.is_continuous_attribute(a): # Cuando se trabaja con valores continuos el algoritmo supone que los valores de los atributos estan normalmente # distribuidos y a partir del set de entrenamiento simplemente se calcula la media y la desviacion estandar de los valores # condicionados de los atributos para obtener la probablidiad df_a = df[df[target_attribute] == target_attribute_value] mu = np.mean(df_a[a]) sigma = np.std(df_a[a]) n = norm(mu, sigma) return n.pdf(a_value) # m-estimador: # (e + m.p)/(n + m) # p es la estimación a priori de la probabilidad buscada y m es el “tamaño equivalente de muestra”. # Un método típico para elegir p en ausencia de otra información es asumir prioridades uniformes; # es decir, si un atributo tiene k valores posibles, establecemos p = i/k. n = df[df[target_attribute] == target_attribute_value].shape[0] df_a = df[df[a] == a_value] e = df_a[df_a[target_attribute] == target_attribute_value].shape[0] p = 1 / len (attributes_info[a].domain) if n == 0 or e == 0: m = 3 else: m = 0 return (e + m * p) / (n + m)
def test_data_clase(self): target_attribute = 'Juega' ds: DataSet = DataSet() ds.load_from_arff('../../datasets/dataset_clase.arff') # noinspection PyPep8Naming X: DataFrame = ds.pandas_df.drop(columns=target_attribute) y = ds.pandas_df[target_attribute] classifier = NBClassifier(target_attribute, ds.attribute_info, ds.attribute_list) classifier.fit(X, y) predict_df(ds.pandas_df, target_attribute, classifier) preprocessor = DataSetPreprocessor(ds, target_attribute) df = preprocessor.transform_to_rn() # noinspection PyPep8Naming X: DataFrame = df.drop(columns=target_attribute) y = df[target_attribute] classifier = KNNClassifier(1) classifier.fit(X, y) predict_df(df, target_attribute, classifier) classifier = KNNClassifier(3) classifier.fit(X, y) predict_df(df, target_attribute, classifier) classifier = KNNClassifier(7) classifier.fit(X, y) predict_df(df, target_attribute, classifier)
def test_data_clase(self): ej1 = { "Ded": "Media", "Dif": "Alta", "Hor": "Nocturno", "Hum": "Alta", "Hdoc": "Malo" } ej2 = { "Ded": "Baja", "Dif": "Alta", "Hor": "Matutino", "Hum": "Alta", "Hdoc": "Bueno" } target_attribute = 'Salva' ds = DataSet() ds.load_from_arff('../../datasets/dataset_clase.arff') classifier = Classifier(select_attribute, target_attribute) classifier.fit(ds) print(RenderTree(classifier._decision_tree)) print(f'Predict {ej1}: {classifier.predict(ej1)}') print(f'Predict {ej2}: {classifier.predict(ej2)}') for i in range(ds.pandas_df.shape[0]): instance = ds.pandas_df.loc[i] v = classifier.predict(instance) if instance[target_attribute] == 'YES': self.assertTrue( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' ) else: self.assertFalse( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' )
def test_data_Autism_Adult(self): ds = DataSet() ds.load_from_arff('../../datasets/Autism-Adult-Data.arff') # Fixed by https: // eva.fing.edu.uy / mod / forum / discuss.php?d = 117656 ds.remove_attribute('result') target_attribute = 'Class/ASD' classifier = Classifier(select_attribute, target_attribute, get_value_attribute_1) classifier.fit(ds) print(RenderTree(classifier._decision_tree)) for i in range(ds.pandas_df.shape[0]): instance = ds.pandas_df.loc[i] v = classifier.predict(instance) if instance[target_attribute] == 'YES': self.assertTrue( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' ) else: self.assertFalse( v, f'Para la instancia {i+1}, el valor predecido no coincide con el valor conocido' )
def select_attribute(examples: DataSet, target_attribute: str, node: Node) -> StrategyResult: """ Implementa una estrategia de seleccion de atributos basada en el calculo de ganancia segun entropia. No soporta atributos que tomen valores nulos. :param node: es el nodo para el cual se quiere seleccionar el atributo :param examples: conjunto de ejemplos de entrenamiento que se tienen al momento :param target_attribute: el atributo que se quiere predecir :return: devuelve el atributo seleccionado """ # si no es el nodo raiz obtengo la entropia del conjunto actual guardada en el nodo padre (al final de # este metodo en donde se guardan las entropias) (parte de codigo inutilizada) #if hasattr(node, 'parent') and hasattr(node.parent, 'entropies'): # noinspection PyUnresolvedReferences # branch_value = node.root_value.decode('utf-8') # s_entropy = node.parent.entropies[branch_value] #else: # s_entropy = None # busco el atributo que de mayor ganancia gain_max = -1 for a in [a for a in examples.attribute_list if a != target_attribute]: if examples.is_continuous_attribute(a): (c, g, e_s_under_c, e_s_above_c) = get_discrete_values_from_continuous_values(examples, a, target_attribute) if g > gain_max: gain_max = g best_attribute = a best_attribute_is_continuous = True entropies = { f'< {c}': e_s_under_c, f'>= {c}': e_s_above_c } else: (g, entropies_aux) = gain(examples, a, target_attribute) if g > gain_max: gain_max = g best_attribute = a best_attribute_is_continuous = False entropies = entropies_aux # guardo las entropias obtenidas para las ramas futuras en el nodo actual # noinspection PyUnboundLocalVariable node.entropies = entropies # noinspection PyUnboundLocalVariable if best_attribute_is_continuous: # noinspection PyUnboundLocalVariable return result_for_continuos_attribute(best_attribute, c) else: # noinspection PyUnboundLocalVariable return result_for_discrete_attribute(examples, best_attribute)
def test_predict_knn(self): ds = DataSet() ds.load_from_arff('./../../datasets/simple_distance_example.arff') target_attribute = 'PlayTennis' X = ds.pandas_df.drop(columns=target_attribute) y = ds.pandas_df[target_attribute] instance = pd.Series({ 'humidity': 75, 'age': 30, 'temperature': 30, }) classifier = KNNClassifier(1) classifier.fit(X, y) assert classifier.predict(instance) == 'YES' classifier = KNNClassifier(3) classifier.fit(X, y) assert classifier.predict(instance) == 'NO' classifier = KNNClassifier(7) classifier.fit(X, y) assert classifier.predict(instance) == 'YES'
def test_basic(self): """ En el conjunto de entrenamiento la clasificacion debe ser perfecta (para una instancias x, la instancia que mas se acerca es si misma) """ ds: DataSet = DataSet() ds.load_from_arff('./../../datasets/Autism-Adult-Training-Subset.arff') target_attribute = 'Class/ASD' preprocessor = DataSetPreprocessor(ds, target_attribute) df = preprocessor.transform_to_rn() X: DataFrame = df.drop(columns=target_attribute) y = df[target_attribute] classifier = KNNClassifier(1) classifier.fit(X, y) for i in range(len(df)): self.assertEqual(classifier.predict(X.loc[i]), y.loc[i], f'El valor predecido para la instancia {i} no coincide su valor en el dataset')
def __init__(self, ds: DataSet, target_attribute: str): """ Constructor :param ds: DataSet que se quiere transformar :param target_attribute: atributo objetivo """ self._ds = ds self._target_attribute = target_attribute self._attribute_info = {} for a in ds.attribute_list: if ds.is_continuous_attribute(a): self._attribute_info[a] = {} # se calcula una aproximacion a la esperanza (mu) de los valores tomados por las instancias en 'a' self._attribute_info[a]['mu'] = np.mean(ds.pandas_df[a]) # se calcula una aproximacion a la varianza (sigma) de los valores tomados por las instancias en 'a' self._attribute_info[a]['sigma'] = np.std(ds.pandas_df[a])
def filter(self, ds: DataSet): ds_new = ds.copy() ds_new.pandas_df = ds_new.pandas_df[ds_new.pandas_df[self.attribute] == self._value] return ds_new
def filter(self, ds: DataSet): ds_new = ds.copy() ds_new.pandas_df = ds_new.pandas_df[self._operator(ds_new.pandas_df[self.attribute], self._value)] return ds_new
def test2(self): training_ds = DataSet () training_ds.load_from_arff ('../../datasets/Autism-Adult-Training-Subset.arff') test_ds = DataSet () test_ds.load_from_arff ('../../datasets/Autism-Adult-Test-Subset.arff') training_df = training_ds.pandas_df test_pandas_df = test_ds.pandas_df n = 2 k_for_k_fold = 10 target_attribute = 'Class/ASD' accuracies = [] recall = [] classifier = Classifier(entropy.select_attribute, target_attribute, get_value_attribute_1) table_kfold = [] table = [] for i in range (n): kf = KFold (n_splits=k_for_k_fold, do_shuffle=True) indexes = kf.split (training_df) for test_indexes, training_indexes in indexes: df_test = training_df.iloc[test_indexes] df_train = training_df.iloc[training_indexes] ds_df_train = DataSet() ds_df_train.load_from_pandas_df(df_train, training_ds.attribute_info, training_ds.attribute_list) classifier.fit(ds_df_train) y_predicted = df_test.apply (lambda row: classifier.predict (row), axis=1) y_true = df_test[target_attribute] accuracies.append (accuracy_score (y_predicted, y_true)) recall.append(recall_score(y_predicted, y_true)) # Presentacion de resultados x = [i + 1 for i in range (k_for_k_fold)] for i in range (k_for_k_fold): table_kfold.append ([x[i], accuracies[i], recall[i]]) classifier.fit(training_ds) y_predicted = test_pandas_df.apply (lambda row: classifier.predict (row), axis=1) y_true = test_pandas_df[target_attribute] table.append([i, accuracy_score (y_predicted, y_true), recall_score(y_predicted, y_true)]) print ("K fold validation :\n") print (tabulate (table_kfold, headers=["#", "Accuracy", "Recall"])) print () print ("T=1/5 S=4/5 :\n") print (tabulate (table, headers=["#", "Accuracy", "Recall"])) plt.figure (figsize=(10, 10)) plt.ylabel ('Accuracy/Recall') plt.axis ([0, (n * k_for_k_fold) - 1, 0, 1]) plt.grid (True) plt.plot (accuracies, color='r', label='NB Accuracies') plt.plot (recall, color='g', label='NB Recall') plt.legend (loc=0) plt.show ()