From 386e2d1f93b96c10a57d16cba185bebdb68fcd23 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:43:28 +0530 Subject: [PATCH 01/30] Create final_ann.py --- .../models/final_ann.py | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py new file mode 100644 index 000000000..a2b15eae2 --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py @@ -0,0 +1,275 @@ + + +import pandas as pd +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split +import numpy as np +from sklearn.model_selection import train_test_split +from sklearn.metrics import accuracy_score +from sklearn.metrics import average_precision_score +from sklearn.metrics import mean_squared_error + + +data = pd.read_csv('Ground Water .csv') + + + +#data = data.fillna(data.mean()) +# Fill NaN values only in numeric columns with median +numeric_columns = data.select_dtypes(include='number').columns +data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) + + +# Select only numeric columns for quantile calculations +numeric_data = data.select_dtypes(include=[np.number]) +data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) + +#data = data.clip(lower=data.quantile(0.01), upper=data.quantile(0.99), axis=1) + +data = pd.get_dummies(data) + +scaler = StandardScaler() +data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) + +data = data.drop(data.columns[[1, 2]], axis=1) +X = data.iloc[:,:-3] +y = data.iloc[:, -1] + +#print("\nX: ",X) +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) + +#print("\nx: ") +#print("\nx:",X_train) +#print(X_train.shape()) + +from sklearn.model_selection import KFold +from sklearn.linear_model import LinearRegression +from sklearn.metrics import mean_squared_error + +model = LinearRegression() + +kf = KFold(n_splits=5, shuffle=True) + + +mse_scores = [] +for train_index, test_index in kf.split(X_train): + # Split the data into training and testing sets for this fold + X_train_fold, X_test_fold = X_train.iloc[train_index], X_train.iloc[test_index] + y_train_fold, y_test_fold = y_train.iloc[train_index], y_train.iloc[test_index] + # Train the model on the training set and test it on the testing set + model.fit(X_train_fold, y_train_fold) + y_pred = model.predict(X_test_fold) + mse = mean_squared_error(y_test_fold, y_pred) + mse_scores.append(mse) + +# Compute the average mean squared error across all folds +avg_mse = sum(mse_scores) / len(mse_scores) +print("\navgmse: ",avg_mse) + +Y_train=np.unique(y_train_fold) +#y_train=y_train.reshape(-1,1) +print("\ny_train: ",y_train) +print("\ny_train shape: ",y_train.shape) + +#neural Network +input_layer_size = X_train.shape[1] +print("\ninput_layer_shape: ",X_train.shape[1]) +import numpy as np + +class NeuralNetwork: + def __init__(self,input_layer_size,hidden_layer_size,output_layer_size,X): + self.input_layer_size = input_layer_size + self.hidden_layer_size = hidden_layer_size + self.output_layer_size = output_layer_size + + # Initialize the weights with random values + self.W1 = np.random.randn(input_layer_size,hidden_layer_size) + self.W2 = np.random.randn(hidden_layer_size,output_layer_size) + print("\nW1: ",self.W1) + print("\nw1_size: ",self.W1.shape) + print("\nW2: ",self.W2) + print("\nw2_size: ",self.W2.shape) + # Initialize the biases with zeros + self.b1 = np.random.randn(len(X),hidden_layer_size)#x_train + self.b2 = np.random.randn(len(X),output_layer_size)#x_train + print("\nB1:",self.b1) + print("\nB2:",self.b2) + + def sigmoid(self, x): + x = np.array(x, dtype=float) # Ensure x is a NumPy array of floats + if np.isnan(x).any(): + raise ValueError("Input contains NaN values.") + return 1 / (1 + np.exp(-x)) + + + + def forward_propagation(self, X): + self.Z1 =np.dot(X,self.W1) + self.b1 + #print("\nZ1: ",self.Z1) + #print("\nz1size: ",self.Z1.shape) + self.A1 = self.sigmoid(self.Z1) + #print("\nA1: ",self.A1) + #print("\nA1size: ",self.A1.shape) + self.Z2 = np.dot(self.A1,self.W2) + self.b2 + #print("\nZ2: ",self.Z2) + #print("\nZ2size: ",self.Z2.shape) + self.A2 = self.sigmoid(self.Z2) + #print("\nA2: ",self.A2) + #print("\n A2 shape",self.A2.shape) + return self.A2 + + def generate_wt(self,x, y): + l =[] + for i in range(len(x) * len(y)): + l.append(np.random.randn()) + return(np.array(l).reshape(len(x),len(y))) + + def backward_propagation(self, X, Y, output,learning_rate): + #print("\n Output",output) + #print("\noutput: ",output.shape) + Y=np.array(Y) + Y=Y.reshape(-1,1) + #print("\ny_shape: ",Y.shape) + dZ2 = output - Y + #print("\ndz2 ",dZ2.shape) + #print(dZ2) + #print("\n w2: ",self.W2) + dW2 = np.dot(self.A1.T,dZ2) + + #print("\ndw2: ",dW2) + db2 = np.sum(dZ2, axis=1, keepdims=True) + + #print("\nw2 size:",self.W2.shape) + #print("\ndw2 size:",dZ2.shape) + dZ1=np.multiply((self.W2.dot((dZ2.T))).T,(np.multiply(self.A1, 1-self.A1))) + #print('\ndz1:',dZ1) + #dZ1 = np.dot(self.W2.T, dZ2) * (self.A1 * (1 - self.A1)) + dW1 = np.dot(X.T,dZ1) + db1 = np.sum(dZ1, axis=1, keepdims=True) + #print("\ndw1: ",dW1) + #print("\ndb1: ",db1) + #print("\ndw2: ",dW2) + #print("\ndb2: ",db2) + # Update the weights and biases + self.W1 = self.W1.astype('float64') + dW1 = dW1.astype('float64') + + self.W1 -= learning_rate*dW1 + self.b1 -= learning_rate*db1 + self.W2 -= learning_rate*dW2 + self.b2 -= learning_rate*db2 + #return self.W1,self.W2 + + #1 + + def loss(self,y_pred, y_true): + y_true = y_true.values.reshape(-1, 1) + #print("\n y_p:",y_pred) + #print("\n y_t:",y_true) + y_pred_binary = (y_pred >= 0.5).astype(int) + y_true_binary = (y_true >= 0.5).astype(int) + mse = np.mean((y_pred - y_true_binary)**2) + print(f"\nMSE: {mse}") + return mse + + def accuracy(self,y_pred,y_true): + y_true = y_true.values.reshape(-1, 1) + y_pred_binary = (y_pred >= 0.5).astype(int) + + y_true_binary = (y_true >= 0.5).astype(int) + + return (y_pred_binary == y_true_binary).mean() * 100 + + def rmsee(self,y_pred,y_train): + mse = mean_squared_error(y_train, y_pred) + rmse = mean_squared_error(y_train, y_pred, squared=False) + #print(f"MSE: {mse}") + #print(f"RMSE: {rmse}") + return rmse + + + def train(self,x, Y, epoch =10,alpha = 0.01): + acc =[] + losss =[] + rm=[] + for j in range(epoch): + out = self.forward_propagation(x) + self.backward_propagation(x, Y,out,alpha) + #print("\n out: ",out) + #print("\n Y:",Y) + print("epochs:", j + 1, "======== acc:", self.accuracy(out, Y)) + acc.append(self.accuracy(out,Y)) + losss.append(self.loss(out,Y)) + rm.append(self.rmsee(out,Y)) + #print("\n rm:",rm) + return(acc, losss,rm) + + + + def predict(self,x): + out = self.forward_propagation(x) + #print("\n out: ",out) + new_arr=[] + for i in range(len(out)): + if(out[i][0]<0.05): + new_arr.append(0) + else: + new_arr.append(1) + #print("\n new_arr: ",new_arr) + + +# Define your ANN architecture +input_layer_size = X_train.shape[1] +#print("\nils: ",X_train.shape[1]) +hidden_layer_size = 10 +output_layer_size = 1 +#(len(y_train)) +#print("\nols: ",output_layer_size) +weights = np.random.rand(input_layer_size*hidden_layer_size + hidden_layer_size*output_layer_size) + +def fitness_function(weights): + + nn=NeuralNetwork(input_layer_size,hidden_layer_size,output_layer_size,X_train) + + return nn + + + + + +ff=fitness_function(weights) +val=ff.forward_propagation( X_train) + +acc,losss,rm=ff.train(X_train,y_train,10,0.01) +ac=acc[0] +# print("\ntrain: ",acc,losss) +# print("\n\n round: ",ff.predict(X_train)) +print("rmse: ",rm[len(rm)-1]) +print("Accuracy: ",ac) +print("Loss: ",losss[len(losss)-1]) + + + +import matplotlib.pyplot as plt1 + +# plotting accuracy +plt1.subplot(1, 3, 1) +plt1.plot(acc) +plt1.ylabel('Accuracy') +plt1.xlabel("Epochs:") +plt1.show() + + +# plotting Loss +plt1.subplot(1, 3, 2) +plt1.plot(losss) +plt1.ylabel('Loss') +plt1.xlabel("Epochs:") +plt1.show() + +plt1.subplot(1, 3, 3) +plt1.plot(rm) +plt1.ylabel('RMSE value: ') +plt1.xlabel("Epochs:") +plt1.show() + From 6975aa82f82e5c7ba292f4c2f331b4a47634de61 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:44:37 +0530 Subject: [PATCH 02/30] Create ann+woa_final.py --- .../models/ann+woa_final.py | 364 ++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py new file mode 100644 index 000000000..084d8a09a --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py @@ -0,0 +1,364 @@ + +import pandas as pd +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split +import numpy as np +from sklearn.model_selection import train_test_split +from sklearn.metrics import accuracy_score +from sklearn.metrics import average_precision_score +from sklearn.metrics import mean_squared_error +from flask import Flask, request, jsonify, render_template +import pandas as pd +from sklearn.preprocessing import StandardScaler +import numpy as np +data = pd.read_csv('../Ground Water .csv') + +numeric_columns = data.select_dtypes(include='number').columns +data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) + + +# Select only numeric columns for quantile calculations +numeric_data = data.select_dtypes(include=[np.number]) +data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) + + +# Encode categorical variables +data = pd.get_dummies(data) + +# Scale numerical variables +scaler = StandardScaler() +data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) + + +data = data.drop(data.columns[[1, 2]], axis=1) +X = data.iloc[:,:-3] +y = data.iloc[:, -1] +#print("\nX") +#print(X) +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) + +#print("\nx: ") +print("\nx:",X_train) +#print(X_train.shape()) +print("\ny: ") +from sklearn.model_selection import KFold +from sklearn.linear_model import LinearRegression +from sklearn.metrics import mean_squared_error + +# Choose a model to use +model = LinearRegression() + +# Split the dataset into k-folds +kf = KFold(n_splits=5, shuffle=True) + +# Perform cross-validation +mse_scores = [] +for train_index, test_index in kf.split(X_train): + # Split the data into training and testing sets for this fold + X_train_fold, X_test_fold = X_train.iloc[train_index], X_train.iloc[test_index] + y_train_fold, y_test_fold = y_train.iloc[train_index], y_train.iloc[test_index] + # Train the model on the training set and test it on the testing set + model.fit(X_train_fold, y_train_fold) + y_pred = model.predict(X_test_fold) + mse = mean_squared_error(y_test_fold, y_pred) + mse_scores.append(mse) + +# Compute the average mean squared error across all folds +avg_mse = sum(mse_scores) / len(mse_scores) +#print("\navgmse: ",avg_mse) + + + +Y_train=np.unique(y_train_fold) +#y_train=y_train.reshape(-1,1) +#print("\nunique: ",y_train) +#print("\nunique shape: ",y_train.shape) +#print("\nlength unique shape: ",len(y_train)) + + +#from woa import WOA +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.pyplot as plt1 +from functools import partial +import numpy as np + + +def f(X): + A = 10 + sol = [] + for ind in X: + sol.append(A*len(ind) + sum([(i**2 - A * np.cos(2 * np.pi * i)) for i in ind]) )#output-Y + + return np.array(sol) +x_lb=y_lb=-500 +x_ub=y_ub=500 + + + + + + + + +class WOA: + def __init__(self, obj_func, n_whale, spiral_constant, n_iter,lb, ub,W): + self.obj_func = obj_func + self.n_whale = n_whale + self.spiral_constant = spiral_constant + self.n_iter = n_iter + #print('--------------------') + self.whale = {} + self.prey = {} + self.W=W + #print('----------------------------') + self.lb = np.array([x_lb, y_lb]) + + self.ub = np.array([x_ub, y_ub]) + + def init_whale(self): + tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),)) + for i in range(self.n_whale)] + print("\n temp:",tmp) + self.whale['position'] = np.array(tmp) + self.whale['fitness'] = self.obj_func(self.whale['position']) + + def init_prey(self): + + tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),))] + + self.prey['position'] = np.array(tmp) + self.prey['fitness'] = self.obj_func(self.prey['position']) + + + def update_prey(self): + if self.whale['fitness'].min() < self.prey['fitness'][0]: + self.prey['position'][0] = self.whale['position'][self.whale['fitness'].argmin()] + self.prey['fitness'][0] = self.whale['fitness'].min() + + def search(self, idx, A, C): + random_whale = self.whale['position'][np.random.randint(low=0, high=self.n_whale, + size=len(idx[0]))] + d = np.abs(C[..., np.newaxis] * random_whale - self.whale['position'][idx]) + self.whale['position'][idx] = np.clip(random_whale - A[..., np.newaxis] * d, self.lb, self.ub) + + def encircle(self, idx, A, C): + #d = np.abs(C[..., np.newaxis] * self.prey['position'].reshape(1, -1) - self.whale['position'][idx]) + d = np.abs(np.reshape(C, (-1, 1)) * self.prey['position'].reshape(1, -1) - self.whale['position'][idx]) + + self.whale['position'][idx] = np.clip(self.prey['position'][0] - A[..., np.newaxis] * d, self.lb, self.ub) + + def bubble_net(self, idx): + d_prime = np.abs(self.prey['position'] - self.whale['position'][idx]) + l = np.random.uniform(-1, 1, size=len(idx[0])) + self.whale["position"][idx] = np.clip( + d_prime * np.exp(self.spiral_constant * l)[..., np.newaxis] * np.cos(2 * np.pi * l)[..., np.newaxis] + + self.prey["position"], + self.lb, + self.ub, + ) + + def optimize(self, a): + + p = np.random.random(self.n_whale) + r1 = np.random.random(self.n_whale) + r2 = np.random.random(self.n_whale) + A = 2 * a * r1 - a + C = 2 * r2 + search_idx = np.where((p < 0.5) & (abs(A) > 1)) + encircle_idx = np.where((p < 0.5) & (abs(A) <= 1)) + bubbleNet_idx = np.where(p >= 0.5) + self.search(search_idx, A[search_idx], C[search_idx]) + self.encircle(encircle_idx, A[encircle_idx], C[encircle_idx]) + self.bubble_net(bubbleNet_idx) + self.whale['fitness'] = self.obj_func(self.whale['position']) + + def run(self): + self.init_whale() + self.init_prey() + f_values = [self.prey['fitness'][0]] + #print("\n\n\n\n\noptimal sol: ",self.prey['position'][0]) + for n in range(self.n_iter): + #print("Iteration = ", n, " f(x) = ", self.prey['fitness'][0]) + a = 2 - n * (2 / self.n_iter) + self.optimize(a) + self.update_prey() + #l.append((self.loss(out, y_wt))) + #acc.append(abs((1-(sum(l)/len(x)))*10)) + f_values.append(self.prey['fitness'][0]) + + optimal_x = self.prey['position'].squeeze() + #print("\n f_val: ",f_values) + #print("\n optimal: ",optimal_x) + return f_values, optimal_x + + + + + + +#neural Network +input_layer_size = X_train.shape[1] +print("\nils: ",X_train.shape) + +import numpy as np + +class NeuralNetwork: + def __init__(self, input_layer_size, hidden_layer_size, output_layer_size, X): + self.input_layer_size = input_layer_size + self.hidden_layer_size = hidden_layer_size + self.output_layer_size = output_layer_size + + # Initialize the weights with random values + self.W1 = np.random.randn(input_layer_size, hidden_layer_size) + self.W2 = np.random.randn(hidden_layer_size, output_layer_size) + + # Initialize the biases with zeros + self.b1 = np.zeros((1, hidden_layer_size)) + self.b2 = np.zeros((1, output_layer_size)) + + def sigmoid(self, x): + x = np.array(x, dtype=float) + return 1 / (1 + np.exp(-x)) + + def forward_propagation(self, X): + # Calculate the hidden layer activations + self.Z1 = np.dot(X, self.W1) + self.b1 + self.A1 = self.sigmoid(self.Z1) + + # Calculate the output layer activations + self.Z2 = np.dot(self.A1, self.W2) + self.b2 + self.A2 = self.sigmoid(self.Z2) + + return self.A2 + + def backward_propagation(self, X, Y, output, learning_rate): + # Reshape Y to match the shape of output + Y = Y.values.reshape(-1, 1) + + # Calculate the error in the output layer + dZ2 = output - Y + dW2 = np.dot(self.A1.T, dZ2) + db2 = np.sum(dZ2, axis=0, keepdims=True) + + # Calculate the error in the hidden layer + dZ1 = np.dot(dZ2, self.W2.T) * (self.A1 * (1 - self.A1)) + dW1 = np.dot(X.T, dZ1) + db1 = np.sum(dZ1, axis=0, keepdims=True) + self.W1 = self.W1.astype('float64') + dW1 = dW1.astype('float64') + # Update the weights and biases + self.W1 -= learning_rate * dW1 + self.b1 -= learning_rate * db1 + self.W2 -= learning_rate * dW2 + self.b2 -= learning_rate * db2 + def loss(self, y_pred, y_true): + y_true = y_true.values.reshape(-1, 1) + y_pred_binary = (y_pred >= 0.5).astype(int) + y_true_binary = (y_true >= 0.5).astype(int) + mse = np.mean((y_pred - y_true_binary)**2) + return mse + + def accuracy(self, y_pred, y_true): + y_true = y_true.values.reshape(-1, 1) + y_pred_binary = (y_pred >= 0.5).astype(int) + y_true_binary = (y_true >= 0.5).astype(int) + return (y_pred_binary == y_true_binary).mean() * 100 + + def rmsee(self, y_pred, y_train): + mse = mean_squared_error(y_train, y_pred) + rmse = mean_squared_error(y_train, y_pred, squared=False) + return rmse + + def train(self, X, Y, epoch=10, alpha=0.01): + acc = [] + losss = [] + rm = [] + for j in range(epoch): + out = self.forward_propagation(X) + self.backward_propagation(X, Y, out, alpha) + acc.append(self.accuracy(out, Y)) + losss.append(self.loss(out, Y)) + rm.append(self.rmsee(out, Y)) + + return acc, losss, rm + + def predict(self, X): + # Forward propagation to get the output + output = self.forward_propagation(X) + + # Apply the threshold to classify the output + predictions = (output >= 0.5).astype(int) + + return predictions + + +# Define your ANN architecture +input_layer_size = X_train.shape[1] + +hidden_layer_size = 10 +output_layer_size = 1 + +weights = np.random.rand(input_layer_size*hidden_layer_size + hidden_layer_size*output_layer_size) + +def fitness_function(weights): + + nn=NeuralNetwork(input_layer_size,hidden_layer_size,output_layer_size,X_train) + return nn + + + + + + +ff=fitness_function(weights) +val=ff.forward_propagation( X_train) + + +acc,losss,rm=ff.train(X_train,y_train,10,0.01) + + +max_accuracy = acc[0] + +for i in range(1, len(acc)): + if acc[i] > max_accuracy: + max_accuracy = acc[i] + +print("Accuracy:", max_accuracy) +print("Loss:",losss[len(losss)-1]) +#print("\ntrain: ",acc,losss) +#print(ff.predict(X_train)) +predictions = ff.predict(X_test) + +# Print the predictions +print("Predictions:", predictions) +accuracy = ff.accuracy(predictions, y_test) +print("Test Accuracy:", accuracy) + +# Print messages for groundwater quality +for i, prediction in enumerate(predictions): + if prediction == 1: + print(f"Sample {i+1}: Groundwater is harmful.") + else: + print(f"Sample {i+1}: Groundwater is not harmful.") + +# Plotting accuracy +plt.subplot(1, 3, 3) +plt.plot(rm) +plt.ylabel('RMSE value') +plt.xlabel("Epochs:") +plt.show() + +# Plotting Loss +plt.subplot(1, 2, 1) +plt.plot(losss) +plt.title("Loss over Time") +plt.xlabel("Epoch") +plt.ylabel("Loss") + +plt.subplot(1, 2, 2) +plt.plot(acc) +plt.title("Accuracy over Time") +plt.xlabel("Epoch") +plt.ylabel("Accuracy") +plt.show() From c2e33c5d7a440934d9d72a77c838208de215a921 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:45:40 +0530 Subject: [PATCH 03/30] Create random_forest.py --- .../models/random_forest.py | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py new file mode 100644 index 000000000..e7a2144cd --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py @@ -0,0 +1,136 @@ +import pandas as pd +import numpy as np +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split, KFold +from sklearn.ensemble import RandomForestRegressor +from sklearn.metrics import mean_squared_error, accuracy_score +import matplotlib.pyplot as plt + +# Load and preprocess data +data = pd.read_csv('Ground Water .csv') + +# Fill NaN values in numeric columns with median +numeric_columns = data.select_dtypes(include='number').columns +data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) + +# Handle outliers using quantile clipping +numeric_data = data.select_dtypes(include=[np.number]) +data[numeric_data.columns] = numeric_data.clip( + lower=numeric_data.quantile(0.01), + upper=numeric_data.quantile(0.99), + axis=1 +) + +# Convert categorical variables to dummy variables +data = pd.get_dummies(data) + +# Scale the features +scaler = StandardScaler() +data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform( + data.select_dtypes(include=['float64']) +) + +# Drop specified columns and split features/target +data = data.drop(data.columns[[1, 2]], axis=1) +X = data.iloc[:,:-3] +y = data.iloc[:, -1] + +# Split the data +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) + +# Initialize Random Forest model +rf_model = RandomForestRegressor( + n_estimators=100, + max_depth=10, + random_state=42, + n_jobs=-1 +) + +# Perform K-fold cross-validation +kf = KFold(n_splits=5, shuffle=True, random_state=42) +mse_scores = [] +accuracy_scores = [] +rmse_scores = [] + +for fold, (train_index, val_index) in enumerate(kf.split(X_train), 1): + # Split data for this fold + X_train_fold = X_train.iloc[train_index] + X_val_fold = X_train.iloc[val_index] + y_train_fold = y_train.iloc[train_index] + y_val_fold = y_train.iloc[val_index] + + # Train the model + rf_model.fit(X_train_fold, y_train_fold) + + # Make predictions + y_pred = rf_model.predict(X_val_fold) + + # Calculate metrics + mse = mean_squared_error(y_val_fold, y_pred) + rmse = np.sqrt(mse) + + # Convert predictions to binary for accuracy calculation + y_pred_binary = (y_pred >= 0.5).astype(int) + y_val_binary = (y_val_fold >= 0.5).astype(int) + acc = accuracy_score(y_val_binary, y_pred_binary) * 100 + + # Store scores + mse_scores.append(mse) + accuracy_scores.append(acc) + rmse_scores.append(rmse) + + print(f"\nFold {fold}:") + print(f"MSE: {mse:.4f}") + print(f"RMSE: {rmse:.4f}") + print(f"Accuracy: {acc:.2f}%") + +# Calculate and print average metrics +avg_mse = np.mean(mse_scores) +avg_rmse = np.mean(rmse_scores) +avg_accuracy = np.mean(accuracy_scores) + +print("\nAverage Metrics across all folds:") +print(f"Average MSE: {avg_mse:.4f}") +print(f"Average RMSE: {avg_rmse:.4f}") + +# Plot the metrics +plt.figure(figsize=(15, 5)) + +# Plot Accuracy +plt.subplot(1, 3, 1) +plt.plot(range(1, 6), accuracy_scores, marker='o') +plt.title('Accuracy across Folds') +plt.xlabel('Fold') +plt.ylabel('Accuracy (%)') + +# Plot MSE +plt.subplot(1, 3, 2) +plt.plot(range(1, 6), mse_scores, marker='o', color='red') +plt.title('MSE across Folds') +plt.xlabel('Fold') +plt.ylabel('MSE') + +# Plot RMSE +plt.subplot(1, 3, 3) +plt.plot(range(1, 6), rmse_scores, marker='o', color='green') +plt.title('RMSE across Folds') +plt.xlabel('Fold') +plt.ylabel('RMSE') + +plt.tight_layout() +plt.show() + +# Feature Importance Analysis +feature_importance = pd.DataFrame({ + 'feature': X_train.columns, + 'importance': rf_model.feature_importances_ +}) +feature_importance = feature_importance.sort_values('importance', ascending=False) + +# Plot feature importance +plt.figure(figsize=(10, 6)) +plt.bar(range(len(feature_importance)), feature_importance['importance']) +plt.xticks(range(len(feature_importance)), feature_importance['feature'], rotation=45, ha='right') +plt.title('Feature Importance in Random Forest Model') +plt.tight_layout() +plt.show() From ef55f76f3cbda7ca1ba0fb549de625bd5d4d6e78 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:46:15 +0530 Subject: [PATCH 04/30] Create data --- .../data | 1 + 1 file changed, 1 insertion(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data new file mode 100644 index 000000000..9c558e357 --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data @@ -0,0 +1 @@ +. From eae39c087b39bbcde1163791d9aa87a4eedd347b Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:47:24 +0530 Subject: [PATCH 05/30] Delete Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data --- .../data | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data deleted file mode 100644 index 9c558e357..000000000 --- a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data +++ /dev/null @@ -1 +0,0 @@ -. From 27d7e397ac9cf4ad13dc5d30100255b6509ec1a2 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:47:47 +0530 Subject: [PATCH 06/30] Create test --- .../data/test | 1 + 1 file changed, 1 insertion(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test new file mode 100644 index 000000000..9c558e357 --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test @@ -0,0 +1 @@ +. From 07e3b34336757a919fb13789321740404d6e76bd Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:48:07 +0530 Subject: [PATCH 07/30] Add files via upload --- .../data/Ground Water .csv | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv new file mode 100644 index 000000000..2bd5fc86c --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv @@ -0,0 +1,134 @@ +Station Code,Station Name,STATE,Temperature Min,Temperature Max,pH Min,pH Max,Conductivity (µmhos/cm) Min,Conductivity (µmhos/cm) Max,BOD min(mg/L),BOD max(mg/L),Nitrate N min(mg/L),NitrateN max(mg/L) ,Faecal Coliform min (MPN/100ml),Faecal Coliform max(MPN/100ml),Total Coliformmin (MPN/100ml),Total Coliform max (MPN/100ml),Total Dissolved Solidsmin (mg/L),Total Dissolved Solids max(mg/L),Fluoride min(mg/L),Fluoride max (mg/L),Arsenic min (mg/L),Arsenic max (mg/L),Average arsenic(mg/L) +3093,"BORE WELL AT ALLADAPALEM +VILLAGE, PYDIBHIMAVARAM",ANDHRA PRADESH,29,30,7.3,7.4,840,1481,2,2.1,0.9,16.75,2,4,64,75,584,980,0.4,0.8,0.001,0.001,0.001 +3092,"BORE WELL AT ARINAMA +AKKIVALASA, SRIKAKULAM",ANDHRA PRADESH,28,31,7.4,8.1,712,1139,2,2.4,0.9,7.95,3,4,64,75,492,754,1,1.1,0.001,0.001,0.001 +4360,"BORE WELL AT IDA, +RAMANAYYAPETA, KKAINADA",ANDHRA PRADESH,27,29,7.7,7.8,990,1280,1.8,2,1,3.48,2,7,75,93,660,868,0.4,0.4,0.007,0.009,0.005 +3091,"BORE WELL AT KAPULUPPADA +DUMPSITE, VISHAKHAPATNAM",ANDHRA PRADESH,26,26,7,7.1,3340,4260,2.2,3.6,4.9,41.6,4,7,93,93,2320,2832,0.8,1.1,0.001,0.001,0.004 +4353,"BORE WELL AT MANGARAJU +HOUSE, GAJAPATHINAGARAM +VILLAGE, PAYAKARAOPETA (M)",ANDHRA PRADESH,25,27,7.9,8.1,2660,3940,1.4,2.7,3,46.25,3,4,63,93,1844,2640,0.4,0.5,0.001,0.001,0.001 +3087,"BORE WELL NEAR VILLAGE +SECRETARIAT, PATHAPADU (V)",ANDHRA PRADESH,24,27,7.5,7.8,2031,2050,1.4,1.8,0.97,7.1,3,3,21,28,1210,1218,0.4,1,0.001,0.001,0.001 +4355,"BORE WELL, CHIPPADA VILLAGE, +BHEEMUNIPATNAM (M)",ANDHRA PRADESH,25,27,7.9,8,660,1482,2.2,2.2,2.05,3.6,4,7,43,120,442,980,0.4,1.4,0.001,0.001,0.001 +4354,"BORE WELL, SRI NOOKATATA +TEMPLE RAJAYYAPETA VILLAGE, +NAKKAPALLI (M)",ANDHRA PRADESH,25,27,7.4,7.6,11120,11420,2.2,2.5,2.2,15.4,7,9,120,150,7340,7968,0.3,0.7,0.001,0.001,0.001 +4394,"BOREWELL AT APLLC OFFICE +INDUSTRIAL ESTATE GROWTH +CENTRE, THUMAKUNTA, +HINDUPUR (M)",ANDHRA PRADESH,26,28,6.9,7.1,1584,3239,1,1,5.8,7.9,2,4,2,21,901,1764,0.6,1.3,0.001,0.001,0.001 +4395,"BOREWELL AT APLLC OFFICE +INDUSTRIAL ESTATE, +ANANTAPURAM (M)",ANDHRA PRADESH,26,28,7,7.1,3558,3780,1,1,10.4,11.4,2,6,2,33,1868,2144,0.5,1.5,0.001,0.001,0.001 +4377,"BOREWELL AT IMITATION +JEWELLARY PARK, +MACHILIPTNAM",ANDHRA PRADESH,27,27,8.1,8.1,1896,1920,1.4,1.8,0.3,2.8,3,3,14,21,1158,1170,0.8,0.9,0.001,0.001,0.001 +1519,BOREWELL AT NAGIRI,ANDHRA PRADESH,28,29,7.1,7.3,1872,2741,1,1,5.2,6.8,2,4,2,16,1061,1361,0.5,0.5,0.001,0.001,0.001 +1518,"BOREWELL AT NANDYAL +(KUNDU)",ANDHRA PRADESH,23,27,7.2,7.3,6619,7770,1,1,5.8,6.2,2,2,2,14,4068,4413,1,1.1,0.001,0.001,0.001 +4364,"BOREWELL AT RAJIV GRUHA +KA;PA, NEAR AP PAPER MAILLS +WORKERS COLONY,",ANDHRA PRADESH,28,28,7.6,7.6,1048,1048,1.5,1.5,3.22,3.22,3,3,93,93,680,680,0.7,0.7,0.001,0.001,0.001 +,"MALLAYAPETA, KATHERU, +RAJAMAHENDRAVARAM",,,,,,,,2.4,2.4,8,8,4,4,93,93,746,746,0.8,0.8,0.002,0.003,0.002 +4350,"BOREWELL AT ZP HIGH SCHOOL, +UDDANAM REGION, +AMALAPADU (V) +VAJRAPUKOTTURU",ANDHRA PRADESH,24,24,7.1,7.1,1140,1140,2,2.2,5.2,7.41,3,3,23,28,5634,6600,1.4,1.7,0.001,0.001,0.0015 +4384,"BOREWELL IN FRONT OF M/S +BHAGEERADHA CHEMICALS & +INDUSTRIES LTD, +CHERUVUKOMMUPALEM (V) +ONGOLE (M)",ANDHRA PRADESH,22,23,7.2,7.3,9090,10500,1.4,1.8,0.3,0.7,3,3,21,39,1120,1160,0.3,0.3,0.001,0.001,0.001 +1513,"BOREWELL KRISHNA MURTHY, +D.NO. 48-16-43 AUTONAGAR +VIJJAYAWADA",ANDHRA PRADESH,25,25,7.1,7.8,1877,1970,2,2,0.79,1.1,4,7,75,93,212,988,0.2,1.1,0.001,0.001,0.001 +1523,"BOREWELL NEAR M/S ANDHRA +SUGARS LTD. , KOVVUR",ANDHRA PRADESH,24,26,7,7.6,310,1447,1,1,1.4,2.6,2,3,2,12,714,784,0.6,1.1,0.001,0.001,0.001 +4392,"BOREWELL NEAR SRI +GOVINDARAJA SWAMY TEMPLE, +TIRUPATI TOWN",ANDHRA PRADESH,27,27,6.9,7.5,1380,1414,1,1,2.2,2.4,2,2,2,11,582,642,0.4,0.5,0.002,0.002,0.0015 +1520,"BOREWELL NEAR +SWARNAMUKHI RIVER AT +SRIKALAHASTI",ANDHRA PRADESH,28,29,7.3,7.3,1027,1266,1,1,6.7,10.1,2,3,2,29,2568,3370,1.3,1.8,0.001,0.001,0.0015 +1517,"BOREWELL NEAR TUNGBHADRA +RIVER KURNOOL",ANDHRA PRADESH,23,26,7.8,7.9,4520,5663,1.2,1.4,1.1,1.18,3,3,15,21,630,658,0.4,0.5,0.001,0.001,0.001 +1516,"BOREWELL OF NAVLOK +GARDENS, NELLORE",ANDHRA PRADESH,20,21,8.1,8.1,1080,1100,1.4,1.8,0.3,0.6,3,3,20,23,1050,1220,0.2,0.5,0.001,0.001,0.001 +1514,"BOREWELL VIJAY KUMAR +AUTONAGAR VIJAYAWADA",ANDHRA PRADESH,25,25,7.2,7.9,1780,2080,2,2.2,2.47,3.8,3,3,20,21,2310,2348,0.8,1,0.001,0.001,0.001 +4376,"BOREWELL WATER , APIIC +OFFICE, IDA KONDAPALLI, +IBRAHIMPATNAM (M)",ANDHRA PRADESH,26,26,7.1,7.6,3850,3860,1,1.8,0.3,1.5,3,3,14,15,508,550,0.5,0.6,0.001,0.001,0.001 +4380,"BOREWELL WATER , SRI +VENKATESWARA SWAMY +TEMPLE, VENKATAPALEM (V), +TULLUR (M)",ANDHRA PRADESH,22,22,7.1,7.5,860,899,1,1.8,0.3,1.5,3,3,14,15,508,550,0.5,0.6,0.001,0.001,0.001 +4382,"BOREWELL WATER POLICE +STATION, AUTONAGAR, +PEDAKAKANI, GUNTUR",ANDHRA PRADESH,21,22,7.1,8.1,4450,4580,2,2.2,3.5,3.86,3,3,28,28,2790,2840,0.2,0.8,0.001,0.001,0.001 +4393,"CLOSED MSW DUMPSITE, +UKKAYAPALLI (V), KADAPA (M)",ANDHRA PRADESH,24,25,7.3,7.4,2810,3352,1,1,10.4,13.1,2,9,2,71,1589,1785,0.6,1.2,0.004,0.004,0.0025 +3090,"HAND PUMP AT +PITTAVANIPALEM, +VISHAKHAPATNAM",ANDHRA PRADESH,27,27,6.7,7.6,1610,1983,2.2,2.2,2.5,18.8,3,4,75,75,1120,1312,0.3,0.5,0.001,0.001,0.0025 +3089,"HAND PUMP AT TANAM VILL., +VISHAKHAPATNAM",ANDHRA PRADESH,27,28,7.2,7.9,2180,2180,2.1,2.2,1.6,13.1,4,4,75,93,1480,1524,1.1,1.3,0.001,0.001,0.001 +1524,"OPEN WELL NEAR PARTAP +NAGAR BRIDGE -KAKINADA",ANDHRA PRADESH,27,30,7.3,7.7,1147,1295,2.1,2.1,0.9,4.78,4,4,75,75,784,848,0.3,1,0.001,0.001,0.001 +1521,"OPEN WELL NEAR RAMA +TEMPLE , WARD NO.2 , MINDI , +VISAKHAPATNAM",ANDHRA PRADESH,26,27,6.8,7.5,1977,2580,1.8,4,1.5,12.4,7,7,93,120,1296,1812,0.8,0.8,0.001,0.001,0.001 +1522,OPEN WELL PEDDANUVVI -VIZIANAGARAM,ANDHRA PRADESH,27,28,7.4,7.9,1120,1190,1.6,2.1,1.5,2.87,3,9,75,75,780,792,0.5,0.8,0.001,0.001,0.001 +26,"WELL AT GRAM PANCHAYAT +OFFICE, KAANURU, VIJAYWADA",ANDHRA PRADESH,25,25,7,8.1,1597,2160,1,2.2,0.7,1.89,3,3,20,23,942,1314,0.4,0.7,0.001,0.001,0.001 +1537,"GROUND WATER AT (JORHAT, +ASSAM)",ASSAM,25,26,7,7.2,276,278,2.3,2.3,0.7,0.8,2,2,2,2,156,224,0.3,0.3,0.01,0.01,0.0055 +1539,"GROUND WATER AT BARPETA, +ASSAM",ASSAM,30,31,7.4,7.5,304,329,2,2.1,0.8,0.8,2,2,2,2,172,180,0.3,0.3,0.01,0.01,0.01 +1540,"GROUND WATER AT +BONAIGAON, ASSAM",ASSAM,27,29,7.2,7.6,366,390,2,2.6,0.7,1,2,2,2,2,202,218,0.3,0.3,0.01,0.01,0.01 +1533,"GROUND WATER AT DIGBOI, +TINSUKIA DISTT., ASSAM",ASSAM,24,26,6.8,7.1,328,332,2.3,2.5,1.1,1.4,2,2,2,2,184,218,0.2,0.3,0.01,0.01,0.01 +1534,"GROUND WATER AT KARBI +ANGLONG DISTT., ASSAM",ASSAM,25,25,7.2,7.2,308,308,2.3,2.3,0.8,0.8,-,-,-,-,176,176,0.3,0.3,-,-,0.01 +1541,"GROUND WATER AT NOONMATI +GUWAHATI, ASSAM",ASSAM,27,29,7.3,7.6,401,408,2.3,2.5,0.8,1,2,2,2,2,218,224,0.3,0.3,0.01,0.01,0.01 +1535,"GROUND WATER AT SIBSAGAR +GOVT ME SCHOOL WARD 6, RED +CROSS ROAD , ASSAM",ASSAM,25,26,6.8,7.3,284,288,2.2,2.6,0.7,0.8,2,2,2,2,160,166,0.3,0.3,0.01,0.01,0.01 +1542,"GROUND WATER AT SIJUBARI +MAZAR, NATBOMA HATIGAON",ASSAM,27,28,7.8,7.8,352,354,2,2.6,0.8,1,2,2,2,2,194,200,0.3,0.4,0.01,0.01,0.01 +2599,"GROUND WATER BUS STAND +SASARAM, ROHTAS",BIHAR,20,20,7.9,7.9,735,735,,,0.88,0.88,7,7,9,9,482,482,0.2,0.2,0.001,0.001,0.01 +2044,"GROUND WATER FROM DADU +MAJRA, CHANDIGARH",CHANDIGARH,24.9,26.4,7.1,7.2,714,804,1,1.2,4.9,8.4,2,2,33,41,390,518,0.3,0.7,0.001,0.001,0.01 +2043,"GROUND WATER FROM +PALSORA VILLAGE, +CHANDIGARH",CHANDIGARH,25,26.4,7.2,7.4,836,909,1,1.5,2.2,3.8,2,5,49,49,430,570,0.3,0.3,0.001,0.001,0.01 +2039,"GROUND WATER FROM SECTOR +15, CHANDIGARH",CHANDIGARH,25,26.3,7.2,7.3,505,550,1,1,4.1,7.2,2,2,26,49,306,380,0.2,0.2,0.001,0.001,0.01 +2448,"GROUND WATER FROM VILLAGE +BHIMPORE, DAMAN","DAMAN AND DIU, +DADRA AND +NAGAR HAVELI",27.2,29.2,7.1,7.2,437,443,1,1,3.54,3.8,22,23,48,52,279,294,0.2,0.2,0.01,0.01,0.01 +1440,"WELL AT SOMNATH INDUSTRIAL +ESTATE, DAMAN","DAMAN AND DIU, +DADRA AND +NAGAR HAVELI",28.1,28.3,6.7,7.1,814,835,1,1,3.67,3.9,2,2,2,10,570,590,0.3,0.4,0.01,0.01,0.01 +2451,"WELL AT VILLAGE DABHEL, +DAMAN","DAMAN AND DIU, +DADRA AND +NAGAR HAVELI",28.4,29.1,7,7.1,1120,1324,1,1,1.67,1.78,2,2,6,8,790,823,0.3,0.4,0.01,0.01,0.01 +3194,"BORE WELL AT BETHORA +INDUSTRIAL ESTATE",GOA,29,29,7.1,7.1,118,118,1.8,1.8,0.5,0.5,2,2,2,2,77,77,0.2,0.2,0.001,0.001,0.01 +3195,"BORE WELL AT MADKAIM +INDUSTRIAL ESTATE",GOA,28,28,7.9,7.9,101,101,1.9,1.9,0.5,0.5,2,2,2,2,64,64,0.2,0.2,0.001,0.001,0.01 +2281,WELL AT CORLIM INDL. ESTATE,GOA,29,29,5,5,91,91,1.2,1.2,0.3,0.3,2,2,2,2,54,54,0.2,0.2,0.001,0.001,0.01 +2280,"WELL AT KUDAI INDL. +ESTATE(M/S CADILA +HEALTHCARE LIMITED)",GOA,28,28,5.6,5.6,122,122,4.2,4.2,0.3,0.3,2,2,2,2,77,77,0.2,0.2,0.001,0.001,0.01 From 54b1d7aff1ef33a0795bb348895acf22683f542a Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:48:40 +0530 Subject: [PATCH 08/30] Delete Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test --- .../data/test | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test deleted file mode 100644 index 9c558e357..000000000 --- a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/test +++ /dev/null @@ -1 +0,0 @@ -. From b50eeb0f2b7b9a90149752f46bfc3bf618a48437 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:49:23 +0530 Subject: [PATCH 09/30] Update final_ann.py --- .../models/final_ann.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py index a2b15eae2..4d3cdb8a6 100644 --- a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py @@ -10,7 +10,7 @@ from sklearn.metrics import mean_squared_error -data = pd.read_csv('Ground Water .csv') +data = pd.read_csv('../Ground Water .csv') From 2012602fdfb00d7cb4a89b701be71acff8827568 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:49:49 +0530 Subject: [PATCH 10/30] Update random_forest.py --- .../models/random_forest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py index e7a2144cd..120d1a126 100644 --- a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py @@ -7,7 +7,7 @@ import matplotlib.pyplot as plt # Load and preprocess data -data = pd.read_csv('Ground Water .csv') +data = pd.read_csv('../Ground Water .csv') # Fill NaN values in numeric columns with median numeric_columns = data.select_dtypes(include='number').columns From 06d9f228495f9dec56786e39c885be32a9697182 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:53:09 +0530 Subject: [PATCH 11/30] Create app.py --- .../models/app.py | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py new file mode 100644 index 000000000..d50610604 --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py @@ -0,0 +1,175 @@ +import streamlit as st +import pandas as pd +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split +import numpy as np +from sklearn.metrics import mean_squared_error +import matplotlib.pyplot as plt + +# Define the neural network class +class NeuralNetwork: + def __init__(self, input_layer_size, hidden_layer_size, output_layer_size, X): + self.input_layer_size = input_layer_size + self.hidden_layer_size = hidden_layer_size + self.output_layer_size = output_layer_size + + # Initialize the weights with random values + self.W1 = np.random.randn(input_layer_size, hidden_layer_size) + self.W2 = np.random.randn(hidden_layer_size, output_layer_size) + + # Initialize the biases with zeros + self.b1 = np.zeros((1, hidden_layer_size)) + self.b2 = np.zeros((1, output_layer_size)) + + def sigmoid(self, x): + x = np.array(x, dtype=float) + return 1 / (1 + np.exp(-x)) + + def forward_propagation(self, X): + # Calculate the hidden layer activations + self.Z1 = np.dot(X, self.W1) + self.b1 + self.A1 = self.sigmoid(self.Z1) + + # Calculate the output layer activations + self.Z2 = np.dot(self.A1, self.W2) + self.b2 + self.A2 = self.sigmoid(self.Z2) + + return self.A2 + + def backward_propagation(self, X, Y, output, learning_rate): + # Reshape Y to match the shape of output + Y = Y.values.reshape(-1, 1) + + # Calculate the error in the output layer + dZ2 = output - Y + dW2 = np.dot(self.A1.T, dZ2) + db2 = np.sum(dZ2, axis=0, keepdims=True) + + # Calculate the error in the hidden layer + dZ1 = np.dot(dZ2, self.W2.T) * (self.A1 * (1 - self.A1)) + dW1 = np.dot(X.T, dZ1) + db1 = np.sum(dZ1, axis=0, keepdims=True) + self.W1 = self.W1.astype('float64') + dW1 = dW1.astype('float64') + # Update the weights and biases + self.W1 -= learning_rate * dW1 + self.b1 -= learning_rate * db1 + self.W2 -= learning_rate * dW2 + self.b2 -= learning_rate * db2 + + def loss(self, y_pred, y_true): + y_true = y_true.values.reshape(-1, 1) + y_pred_binary = (y_pred >= 0.5).astype(int) + y_true_binary = (y_true >= 0.5).astype(int) + mse = np.mean((y_pred - y_true_binary)**2) + return mse + + def accuracy(self, y_pred, y_true): + y_true = y_true.values.reshape(-1, 1) + y_pred_binary = (y_pred >= 0.5).astype(int) + y_true_binary = (y_true >= 0.5).astype(int) + return (y_pred_binary == y_true_binary).mean() * 100 + + def rmsee(self, y_pred, y_train): + mse = mean_squared_error(y_train, y_pred) + rmse = mean_squared_error(y_train, y_pred, squared=False) + return rmse + + def train(self, X, Y, epoch=10, alpha=0.01): + acc = [] + losss = [] + rm = [] + for j in range(epoch): + out = self.forward_propagation(X) + self.backward_propagation(X, Y, out, alpha) + acc.append(self.accuracy(out, Y)) + losss.append(self.loss(out, Y)) + rm.append(self.rmsee(out, Y)) + + return acc, losss, rm + + def predict(self, X): + # Forward propagation to get the output + output = self.forward_propagation(X) + + # Apply the threshold to classify the output + predictions = (output >= 0.5).astype(int) + + return predictions + +# Streamlit app +st.title("Groundwater Quality Prediction") + +uploaded_file = st.file_uploader("../Ground Water.csv", type="csv") + +if uploaded_file is not None: + data = pd.read_csv(uploaded_file) + + # Data preprocessing + numeric_columns = data.select_dtypes(include='number').columns + data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) + + # Select only numeric columns for quantile calculations + numeric_data = data.select_dtypes(include=[np.number]) + data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) + + # Encode categorical variables + data = pd.get_dummies(data) + + # Scale numerical variables + scaler = StandardScaler() + data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) + + data = data.drop(data.columns[[1, 2]], axis=1) + X = data.iloc[:, :-3] + y = data.iloc[:, -1] + + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) + + # Define your ANN architecture + input_layer_size = X_train.shape[1] + hidden_layer_size = 10 + output_layer_size = 1 + + # Initialize and train the neural network + nn = NeuralNetwork(input_layer_size, hidden_layer_size, output_layer_size, X_train) + acc, losss, rm = nn.train(X_train, y_train, epoch=10, alpha=0.01) + + # Make predictions + predictions = nn.predict(X_test) + + # Display results + st.write("Predictions:", predictions) + accuracy = nn.accuracy(predictions, y_test) + st.write("Test Accuracy:", accuracy) + + # Print messages for groundwater quality + for i, prediction in enumerate(predictions): + if prediction == 1: + st.write(f"Sample {i+1}: Groundwater is harmful.") + else: + st.write(f"Sample {i+1}: Groundwater is not harmful.") + + # Plotting accuracy + st.subheader("Accuracy over Time") + fig, ax = plt.subplots() + ax.plot(acc) + ax.set_xlabel("Epoch") + ax.set_ylabel("Accuracy") + st.pyplot(fig) + + # Plotting Loss + st.subheader("Loss over Time") + fig, ax = plt.subplots() + ax.plot(losss) + ax.set_xlabel("Epoch") + ax.set_ylabel("Loss") + st.pyplot(fig) + + # Plotting RMSE + st.subheader("RMSE over Time") + fig, ax = plt.subplots() + ax.plot(rm) + ax.set_xlabel("Epoch") + ax.set_ylabel("RMSE value") + st.pyplot(fig) From f4a1cce334b92f5c9b8e5b174a268703f907f24c Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:53:57 +0530 Subject: [PATCH 12/30] Create test --- .../images/test | 1 + 1 file changed, 1 insertion(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test new file mode 100644 index 000000000..9c558e357 --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test @@ -0,0 +1 @@ +. From ae5e7d12a73470e2776f0d73ee3b97e7700c3c67 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:55:10 +0530 Subject: [PATCH 13/30] Add files via upload --- .../images/Loss and accuracy.png | Bin 0 -> 40803 bytes .../images/RMSE Graph.png | Bin 0 -> 17476 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/Loss and accuracy.png create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/RMSE Graph.png diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/Loss and accuracy.png b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/Loss and accuracy.png new file mode 100644 index 0000000000000000000000000000000000000000..7da4b86b200600643f0f4ac64bd6bb8fe2e3e884 GIT binary patch literal 40803 zcmdqJcRbbq|37?0v`|@vsAPvCiEzl4y|)Hgq3lhm6tYM5-l1$V6ImIDQuaQ`J~+1D z+kD*yY+U;Ij`6A`FuUceLc=Q6(t$cQ?#d0C={ujtfU$Wb!-d% zNS`2vf8m~3oq-<&_G&WsPz9azbMO};GjT<66zX&E>Ai=?;qND($?DppQ0Ex5)5kcZa?iT)ToJo`?Nt~9W^>Tq>5alYx7)1?Q-|(}&zFWSQc#EeDR%A@ z>F+d=hMS{M4^C{hF&w=*gCY<`UiiGYTu*|$xN(*Ux&r?ZzeF)2KkLQLju9d+E}S^# zf&4@fiu;fwKObB^u|j~nI79UR+l%-eduV3HN;lh8WZJ%TxNG7ZC*;U+oS0bti+Yw) zoAxo(Pw&Hh=R^E-BRN{bjbN?0t84k7OVdf(UF_y$WIwk^h;eo#nJRz+S`da@LQ~qc+QS zu#D7~SBmJE2iw^bE7rj~woY>x=8OFDoL8>A;5Mq3U0PbwsI)W9ph0apr+I9zoqrq2 zsU2i9R3W~*xtOMvug9*9(jiBy?vMGBo3D&DobSkDy>sWo*T%+aqaXOEH$>BCQDOym z1O-(SgdEQf5vH$Vlf31hjh0#K(~7v2tmo@|zPE{b-O%5!z0`kVDS(V*i{(yX~!hw}AJwQBkL) zq)g4uvN5@}wzkUKTGk!x4=9b6i8_T<`tVt2x$|w=tO1q4O z?Zj_%`d^�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 literal 0 HcmV?d00001 From 9ee4a6d739ed3cdab85487fa4bff5bbca93ab503 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:59:41 +0530 Subject: [PATCH 14/30] Add files via upload --- .../README.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md new file mode 100644 index 000000000..810acbd6e --- /dev/null +++ b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md @@ -0,0 +1,48 @@ +# Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality + +## Overview +This project implements a hybrid approach combining Artificial Neural Networks (ANN) with the Whale Optimization Algorithm (WOA) to predict arsenic contamination levels in groundwater.Apart from this random forest is also used. The model aims to provide accurate predictions that can help in water resource management and public health protection. + +## Problem Statement +Arsenic contamination in groundwater poses severe health risks worldwide. This project addresses the need for accurate prediction methods by: +- Developing an advanced hybrid model combining BPNN and WOA, random forest classifier +- Analyzing environmental factors affecting arsenic levels +- Providing insights for water management decisions + +## Features +- Hybrid model combining BPNN and WOA +- Environmental factor analysis +- Data preprocessing pipeline +- Model performance comparison +- Variable importance evaluation + + +## Dataset +The dataset includes: +- Arsenic concentration levels +- Environmental parameters +- Geological factors +- Location data + +Data is preprocessed and split into training and testing sets for model evaluation. + +## Model Architecture + +### Backpropagation Neural Network (BPNN) +- Multi-layer perceptron architecture +- Supervised learning approach +- Optimized for regression task + +### Whale Optimization Algorithm (WOA) +- Nature-inspired optimization algorithm +- Used for optimizing BPNN weights and biases +- Enhanced exploration and exploitation capabilities + + +## Results +The project evaluates: +- Prediction accuracy metrics +- Model comparison results +- Variable importance analysis +- Environmental factor impacts + From 7cd893cb2b86382a8dbaeb4ad8f6970b89693b4d Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:43:56 +0530 Subject: [PATCH 15/30] Update and rename Ground Water .csv to Ground Water .csv --- .../data/Ground Water .csv | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/data/Ground Water .csv (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv b/Groundwater Arsenic Content Detection/data/Ground Water .csv similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/data/Ground Water .csv rename to Groundwater Arsenic Content Detection/data/Ground Water .csv From 4d21fe33687778e86185a00d8dc9cafa52dea783 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:45:49 +0530 Subject: [PATCH 16/30] Rename Loss and accuracy.png to Loss and accuracy.png --- .../images/Loss and accuracy.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/images/Loss and accuracy.png (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/Loss and accuracy.png b/Groundwater Arsenic Content Detection/images/Loss and accuracy.png similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/Loss and accuracy.png rename to Groundwater Arsenic Content Detection/images/Loss and accuracy.png From 8328b793c68cdbd776768704f4a0c092ee238fc6 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:46:53 +0530 Subject: [PATCH 17/30] Rename RMSE Graph.png to RMSE Graph.png --- .../images/RMSE Graph.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/images/RMSE Graph.png (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/RMSE Graph.png b/Groundwater Arsenic Content Detection/images/RMSE Graph.png similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/RMSE Graph.png rename to Groundwater Arsenic Content Detection/images/RMSE Graph.png From fb59c6835910f24ccbf955c9555f1448c27ad102 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:49:25 +0530 Subject: [PATCH 18/30] Rename ann+woa_final.py to ann+woa_final.py --- .../models/ann+woa_final.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/models/ann+woa_final.py (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py b/Groundwater Arsenic Content Detection/models/ann+woa_final.py similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/ann+woa_final.py rename to Groundwater Arsenic Content Detection/models/ann+woa_final.py From 4c088be66705a563506ab8a2d59f0bbad67e7888 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:51:20 +0530 Subject: [PATCH 19/30] Rename app.py to app.py --- .../models/app.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/models/app.py (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py b/Groundwater Arsenic Content Detection/models/app.py similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/app.py rename to Groundwater Arsenic Content Detection/models/app.py From 5b297049e027feb04f1c8d1c5c5a0c30539b5ff8 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:55:36 +0530 Subject: [PATCH 20/30] Rename final_ann.py to final_ann.py --- .../models/final_ann.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/models/final_ann.py (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py b/Groundwater Arsenic Content Detection/models/final_ann.py similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/final_ann.py rename to Groundwater Arsenic Content Detection/models/final_ann.py From 18ed62a3018cc56565d6e0b3b2f91878cc2024bb Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:56:41 +0530 Subject: [PATCH 21/30] Rename random_forest.py to random_forest.py --- .../models/random_forest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/models/random_forest.py (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py b/Groundwater Arsenic Content Detection/models/random_forest.py similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/models/random_forest.py rename to Groundwater Arsenic Content Detection/models/random_forest.py From 9695400c7e66d1b7111ed15317a7f5eb9b5d4dd5 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:57:26 +0530 Subject: [PATCH 22/30] Update and rename README.md to README.md --- .../README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality => Groundwater Arsenic Content Detection}/README.md (100%) diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md b/Groundwater Arsenic Content Detection/README.md similarity index 100% rename from Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/README.md rename to Groundwater Arsenic Content Detection/README.md From d0193976f68f994a4c64e2b6455f3906ea1c30ea Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:57:54 +0530 Subject: [PATCH 23/30] Delete Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images directory --- .../images/test | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test diff --git a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test b/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test deleted file mode 100644 index 9c558e357..000000000 --- a/Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality/images/test +++ /dev/null @@ -1 +0,0 @@ -. From 760ebcbcb35bcb962e4f246d8d9d95992014d882 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:24:07 +0530 Subject: [PATCH 24/30] Add files via upload --- .../models/ann+woa.ipynb | 1318 +++++++++++++++++ .../models/ann.ipynb | 0 .../models/random_forest.ipynb | 152 ++ 3 files changed, 1470 insertions(+) create mode 100644 Groundwater Arsenic Content Detection/models/ann+woa.ipynb create mode 100644 Groundwater Arsenic Content Detection/models/ann.ipynb create mode 100644 Groundwater Arsenic Content Detection/models/random_forest.ipynb diff --git a/Groundwater Arsenic Content Detection/models/ann+woa.ipynb b/Groundwater Arsenic Content Detection/models/ann+woa.ipynb new file mode 100644 index 000000000..0a2f6212b --- /dev/null +++ b/Groundwater Arsenic Content Detection/models/ann+woa.ipynb @@ -0,0 +1,1318 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.model_selection import train_test_split\n", + "import numpy as np\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import accuracy_score\n", + "from sklearn.metrics import average_precision_score\n", + "from sklearn.metrics import mean_squared_error\n", + "from flask import Flask, request, jsonify, render_template\n", + "import pandas as pd\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "data = pd.read_csv('../data/Ground Water .csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Station Code pH Min pH Max Conductivity (µmhos/cm) Min \\\n", + "0 0.338985 0.208298 -0.143048 -0.431640 \n", + "1 0.338154 0.413193 1.213569 -0.494694 \n", + "2 1.391339 1.027880 0.632161 -0.357749 \n", + "3 0.337324 -0.406389 -0.724455 0.799876 \n", + "4 1.385525 1.437671 1.213569 0.464903 \n", + "5 0.334002 0.618089 0.632161 0.155054 \n", + "6 1.387186 1.437671 1.019766 -0.520309 \n", + "7 1.386355 0.413193 0.244557 4.112358 \n", + "8 1.419579 -0.611285 -0.724455 -0.065141 \n", + "9 1.419977 -0.406389 -0.724455 0.907264 \n", + "10 1.405459 1.847462 1.213569 0.088552 \n", + "11 -0.968359 -0.201494 -0.336850 0.076729 \n", + "12 -0.969189 0.003402 -0.336850 2.415132 \n", + "13 1.394661 0.822984 0.244557 -0.329178 \n", + "14 -0.266097 0.003402 0.050754 -0.321296 \n", + "15 1.383033 -0.201494 -0.724455 -0.283858 \n", + "16 1.411273 0.003402 -0.336850 3.632362 \n", + "17 -0.973342 -0.201494 0.632161 0.079193 \n", + "18 -0.965036 -0.406389 0.244557 -0.692722 \n", + "19 1.417917 -0.611285 0.050754 -0.165633 \n", + "20 -0.967528 0.208298 -0.336850 -0.339523 \n", + "21 -0.970020 1.232776 0.825964 1.381151 \n", + "22 -0.970850 1.847462 1.213569 -0.313415 \n", + "23 -0.972512 0.003402 0.825964 0.031410 \n", + "24 1.404628 -0.201494 0.244557 1.051105 \n", + "25 1.407950 -0.201494 0.050754 -0.421788 \n", + "26 1.409612 -0.201494 1.213569 1.346669 \n", + "27 1.418748 0.208298 -0.143048 0.538794 \n", + "28 0.336493 -1.021076 0.244557 -0.052333 \n", + "29 0.335663 0.003402 0.825964 0.228452 \n", + "30 -0.964206 0.208298 0.438359 -0.280410 \n", + "31 -0.966698 -0.816180 0.050754 0.128453 \n", + "32 -0.965867 0.413193 0.825964 -0.293711 \n", + "33 -1.597711 -0.406389 1.213569 -0.058737 \n", + "34 -0.953408 -0.406389 -0.530653 -0.709470 \n", + "35 -0.951747 0.413193 0.050754 -0.695677 \n", + "36 -0.950916 0.003402 0.244557 -0.665136 \n", + "37 -0.956730 -0.816180 -0.724455 -0.683855 \n", + "38 -0.955900 0.003402 -0.530653 -0.693707 \n", + "39 -0.950086 0.208298 0.244557 -0.647895 \n", + "40 -0.955069 -0.816180 -0.336850 -0.705529 \n", + "41 -0.949255 1.232776 0.632161 -0.672032 \n", + "42 -0.071325 1.437671 0.825964 -0.483364 \n", + "43 -0.532301 -0.201494 -0.530653 -0.493709 \n", + "44 -0.533131 0.003402 -0.143048 -0.433611 \n", + "45 -0.536454 0.003402 -0.336850 -0.596663 \n", + "46 -0.196743 -0.201494 -0.530653 -0.630161 \n", + "47 -1.033975 -1.021076 -0.724455 -0.444448 \n", + "48 -0.194252 -0.406389 -0.724455 -0.293711 \n", + "49 0.422874 -0.201494 -0.724455 -0.787302 \n", + "50 0.423705 1.437671 0.825964 -0.795676 \n", + "51 -0.335451 -3.865027 -4.189642 -0.798041 \n", + "52 -0.336282 -3.274927 -3.631491 -0.785332 \n", + "\n", + " Conductivity (µmhos/cm) Max BOD min(mg/L) BOD max(mg/L) \\\n", + "0 -0.239813 0.669106 0.235279 \n", + "1 -0.388427 0.669106 0.646191 \n", + "2 -0.327156 0.304189 0.098309 \n", + "3 0.967783 1.034023 2.289838 \n", + "4 0.828729 -0.425644 1.057103 \n", + "5 0.007442 -0.425644 -0.175632 \n", + "6 -0.239378 1.034023 0.372250 \n", + "7 3.871226 1.034023 0.783162 \n", + "8 0.524114 -1.155478 -1.271397 \n", + "9 0.759202 -1.155478 -1.271397 \n", + "10 -0.049048 -0.425644 -0.175632 \n", + "11 0.307712 -1.155478 -1.271397 \n", + "12 2.493029 -1.155478 -1.271397 \n", + "13 -0.427970 -0.243186 -0.586544 \n", + "14 -0.323897 1.398940 0.646191 \n", + "15 -0.387992 0.669106 0.372250 \n", + "16 3.679332 -0.425644 -0.175632 \n", + "17 -0.027321 0.669106 0.098309 \n", + "18 -0.254587 -1.155478 -1.271397 \n", + "19 -0.268927 -1.155478 -1.271397 \n", + "20 -0.333240 -1.155478 -1.271397 \n", + "21 1.577447 -0.790561 -0.723515 \n", + "22 -0.405374 -0.425644 -0.175632 \n", + "23 0.020478 0.669106 0.372250 \n", + "24 0.793965 -1.155478 -0.175632 \n", + "25 -0.492717 -1.155478 -0.175632 \n", + "26 1.106836 0.669106 0.372250 \n", + "27 0.573217 -1.155478 -1.271397 \n", + "28 -0.021672 1.034023 0.372250 \n", + "29 0.063933 0.851564 0.372250 \n", + "30 -0.320638 0.851564 0.235279 \n", + "31 0.237750 0.304189 2.837720 \n", + "32 -0.366265 -0.060728 0.235279 \n", + "33 0.055242 -1.155478 0.372250 \n", + "34 -0.762568 1.216481 0.509220 \n", + "35 -0.740407 0.669106 0.235279 \n", + "36 -0.713900 0.669106 0.920132 \n", + "37 -0.739103 1.216481 0.783162 \n", + "38 -0.749532 1.216481 0.509220 \n", + "39 -0.706078 1.216481 0.783162 \n", + "40 -0.758223 1.034023 0.920132 \n", + "41 -0.729543 0.669106 0.920132 \n", + "42 -0.563982 0.121731 0.098309 \n", + "43 -0.533999 -1.155478 -0.997456 \n", + "44 -0.488372 -1.155478 -0.586544 \n", + "45 -0.644373 -1.155478 -1.271397 \n", + "46 -0.690869 -1.155478 -1.271397 \n", + "47 -0.520528 -1.155478 -1.271397 \n", + "48 -0.308036 -1.155478 -1.271397 \n", + "49 -0.832095 0.304189 -0.175632 \n", + "50 -0.839483 0.486648 -0.038662 \n", + "51 -0.841568 -0.790561 -0.997456 \n", + "52 -0.830357 2.975380 2.969212 \n", + "\n", + " Nitrate N min(mg/L) NitrateN max(mg/L) \\\n", + "0 -0.610752 1.229754 \n", + "1 -0.610752 0.203837 \n", + "2 -0.570066 -0.317282 \n", + "3 1.016692 4.126803 \n", + "4 0.243656 4.387013 \n", + "5 -0.582272 0.104743 \n", + "6 -0.142862 -0.303292 \n", + "7 -0.081833 1.072369 \n", + "8 1.382866 0.198008 \n", + "9 3.254426 0.606043 \n", + "10 -0.854868 -0.396557 \n", + "11 1.138750 0.069769 \n", + "12 1.382866 -0.000180 \n", + "13 0.333165 -0.347593 \n", + "14 2.277960 0.209666 \n", + "15 1.138750 0.140883 \n", + "16 -0.854868 -0.641378 \n", + "17 -0.655507 -0.594746 \n", + "18 -0.407321 -0.419874 \n", + "19 -0.081833 -0.443190 \n", + "20 1.749041 0.454487 \n", + "21 -0.529380 -0.585419 \n", + "22 -0.854868 -0.653037 \n", + "23 0.028020 -0.279976 \n", + "24 -0.854868 -0.548113 \n", + "25 -0.854868 -0.548113 \n", + "26 0.447086 -0.272981 \n", + "27 3.254426 0.804232 \n", + "28 0.040225 1.468746 \n", + "29 -0.325949 0.804232 \n", + "30 -0.610752 -0.165726 \n", + "31 -0.366635 0.722625 \n", + "32 -0.366635 -0.388397 \n", + "33 -0.692124 -0.502646 \n", + "34 -0.692124 -0.629720 \n", + "35 -0.651438 -0.629720 \n", + "36 -0.692124 -0.606404 \n", + "37 -0.529380 -0.559771 \n", + "38 -0.651438 -0.629720 \n", + "39 -0.651438 -0.606404 \n", + "40 -0.692124 -0.629720 \n", + "41 -0.651438 -0.606404 \n", + "42 -0.618889 -0.620394 \n", + "43 1.016692 0.256299 \n", + "44 -0.081833 -0.279976 \n", + "45 0.691203 0.116401 \n", + "46 0.463361 -0.279976 \n", + "47 0.516253 -0.268318 \n", + "48 -0.297469 -0.515470 \n", + "49 -0.773496 -0.664695 \n", + "50 -0.773496 -0.664695 \n", + "51 -0.854868 -0.688011 \n", + "52 -0.854868 -0.688011 \n", + "\n", + " Total Dissolved Solidsmin (mg/L) ... Arsenic min (mg/L)_- \\\n", + "0 -0.373573 ... False \n", + "1 -0.446035 ... False \n", + "2 -0.313714 ... False \n", + "3 0.993744 ... False \n", + "4 0.618834 ... False \n", + "5 0.119480 ... False \n", + "6 -0.485416 ... False \n", + "7 4.248903 ... False \n", + "8 -0.123896 ... False \n", + "9 0.637737 ... False \n", + "10 0.078524 ... False \n", + "11 0.002124 ... False \n", + "12 2.370512 ... False \n", + "13 -0.297961 ... False \n", + "14 -0.245978 ... False \n", + "15 3.603933 ... False \n", + "16 0.048594 ... False \n", + "17 -0.666570 ... False \n", + "18 -0.271182 ... False \n", + "19 -0.375148 ... False \n", + "20 1.189075 ... False \n", + "21 -0.337342 ... False \n", + "22 -0.006540 ... False \n", + "23 0.985868 ... False \n", + "24 -0.433433 ... False \n", + "25 -0.433433 ... False \n", + "26 1.363928 ... False \n", + "27 0.417990 ... False \n", + "28 0.048594 ... False \n", + "29 0.332139 ... False \n", + "30 -0.216048 ... False \n", + "31 0.187216 ... False \n", + "32 -0.219199 ... False \n", + "33 -0.091603 ... False \n", + "34 -0.710677 ... False \n", + "35 -0.698075 ... False \n", + "36 -0.674446 ... False \n", + "37 -0.688623 ... False \n", + "38 -0.694924 ... True \n", + "39 -0.661844 ... False \n", + "40 -0.707526 ... False \n", + "41 -0.680747 ... False \n", + "42 -0.453911 ... False \n", + "43 -0.526372 ... False \n", + "44 -0.494867 ... False \n", + "45 -0.592533 ... False \n", + "46 -0.613799 ... False \n", + "47 -0.384600 ... False \n", + "48 -0.211322 ... False \n", + "49 -0.772899 ... False \n", + "50 -0.783138 ... False \n", + "51 -0.786919 ... False \n", + "52 -0.772899 ... False \n", + "\n", + " Arsenic min (mg/L)_0.001 Arsenic min (mg/L)_0.002 \\\n", + "0 True False \n", + "1 True False \n", + "2 False False \n", + "3 True False \n", + "4 True False \n", + "5 True False \n", + "6 True False \n", + "7 True False \n", + "8 True False \n", + "9 True False \n", + "10 True False \n", + "11 True False \n", + "12 True False \n", + "13 True False \n", + "14 False True \n", + "15 True False \n", + "16 True False \n", + "17 True False \n", + "18 True False \n", + "19 False True \n", + "20 True False \n", + "21 True False \n", + "22 True False \n", + "23 True False \n", + "24 True False \n", + "25 True False \n", + "26 True False \n", + "27 False False \n", + "28 True False \n", + "29 True False \n", + "30 True False \n", + "31 True False \n", + "32 True False \n", + "33 True False \n", + "34 False False \n", + "35 False False \n", + "36 False False \n", + "37 False False \n", + "38 False False \n", + "39 False False \n", + "40 False False \n", + "41 False False \n", + "42 True False \n", + "43 True False \n", + "44 True False \n", + "45 True False \n", + "46 False False \n", + "47 False False \n", + "48 False False \n", + "49 True False \n", + "50 True False \n", + "51 True False \n", + "52 True False \n", + "\n", + " Arsenic min (mg/L)_0.004 Arsenic min (mg/L)_0.007 \\\n", + "0 False False \n", + "1 False False \n", + "2 False True \n", + "3 False False \n", + "4 False False \n", + "5 False False \n", + "6 False False \n", + "7 False False \n", + "8 False False \n", + "9 False False \n", + "10 False False \n", + "11 False False \n", + "12 False False \n", + "13 False False \n", + "14 False False \n", + "15 False False \n", + "16 False False \n", + "17 False False \n", + "18 False False \n", + "19 False False \n", + "20 False False \n", + "21 False False \n", + "22 False False \n", + "23 False False \n", + "24 False False \n", + "25 False False \n", + "26 False False \n", + "27 True False \n", + "28 False False \n", + "29 False False \n", + "30 False False \n", + "31 False False \n", + "32 False False \n", + "33 False False \n", + "34 False False \n", + "35 False False \n", + "36 False False \n", + "37 False False \n", + "38 False False \n", + "39 False False \n", + "40 False False \n", + "41 False False \n", + "42 False False \n", + "43 False False \n", + "44 False False \n", + "45 False False \n", + "46 False False \n", + "47 False False \n", + "48 False False \n", + "49 False False \n", + "50 False False \n", + "51 False False \n", + "52 False False \n", + "\n", + " Arsenic min (mg/L)_0.01 Arsenic max (mg/L)_- Arsenic max (mg/L)_0.001 \\\n", + "0 False False True \n", + "1 False False True \n", + "2 False False False \n", + "3 False False True \n", + "4 False False True \n", + "5 False False True \n", + "6 False False True \n", + "7 False False True \n", + "8 False False True \n", + "9 False False True \n", + "10 False False True \n", + "11 False False True \n", + "12 False False True \n", + "13 False False True \n", + "14 False False False \n", + "15 False False True \n", + "16 False False True \n", + "17 False False True \n", + "18 False False True \n", + "19 False False False \n", + "20 False False True \n", + "21 False False True \n", + "22 False False True \n", + "23 False False True \n", + "24 False False True \n", + "25 False False True \n", + "26 False False True \n", + "27 False False False \n", + "28 False False True \n", + "29 False False True \n", + "30 False False True \n", + "31 False False True \n", + "32 False False True \n", + "33 False False True \n", + "34 True False False \n", + "35 True False False \n", + "36 True False False \n", + "37 True False False \n", + "38 False True False \n", + "39 True False False \n", + "40 True False False \n", + "41 True False False \n", + "42 False False True \n", + "43 False False True \n", + "44 False False True \n", + "45 False False True \n", + "46 True False False \n", + "47 True False False \n", + "48 True False False \n", + "49 False False True \n", + "50 False False True \n", + "51 False False True \n", + "52 False False True \n", + "\n", + " Arsenic max (mg/L)_0.002 Arsenic max (mg/L)_0.003 \n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "5 False False \n", + "6 False False \n", + "7 False False \n", + "8 False False \n", + "9 False False \n", + "10 False False \n", + "11 False False \n", + "12 False False \n", + "13 False False \n", + "14 False True \n", + "15 False False \n", + "16 False False \n", + "17 False False \n", + "18 False False \n", + "19 True False \n", + "20 False False \n", + "21 False False \n", + "22 False False \n", + "23 False False \n", + "24 False False \n", + "25 False False \n", + "26 False False \n", + "27 False False \n", + "28 False False \n", + "29 False False \n", + "30 False False \n", + "31 False False \n", + "32 False False \n", + "33 False False \n", + "34 False False \n", + "35 False False \n", + "36 False False \n", + "37 False False \n", + "38 False False \n", + "39 False False \n", + "40 False False \n", + "41 False False \n", + "42 False False \n", + "43 False False \n", + "44 False False \n", + "45 False False \n", + "46 False False \n", + "47 False False \n", + "48 False False \n", + "49 False False \n", + "50 False False \n", + "51 False False \n", + "52 False False \n", + "\n", + "[53 rows x 142 columns]\n" + ] + } + ], + "source": [ + "numeric_columns = data.select_dtypes(include='number').columns\n", + "data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median())\n", + "\n", + "\n", + "# Select only numeric columns for quantile calculations\n", + "numeric_data = data.select_dtypes(include=[np.number])\n", + "data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1)\n", + "\n", + "\n", + "# Encode categorical variables\n", + "data = pd.get_dummies(data)\n", + "\n", + "# Scale numerical variables\n", + "scaler = StandardScaler()\n", + "data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64']))\n", + "\n", + "\n", + "data = data.drop(data.columns[[1, 2]], axis=1)\n", + "X = data.iloc[:,:-3]\n", + "y = data.iloc[:, -1]\n", + "#print(\"\\nX\")\n", + "print(X)\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "x: Station Code pH Min pH Max Conductivity (µmhos/cm) Min \\\n", + "22 -0.970850 1.847462 1.213569 -0.313415 \n", + "7 1.386355 0.413193 0.244557 4.112358 \n", + "14 -0.266097 0.003402 0.050754 -0.321296 \n", + "34 -0.953408 -0.406389 -0.530653 -0.709470 \n", + "48 -0.194252 -0.406389 -0.724455 -0.293711 \n", + "18 -0.965036 -0.406389 0.244557 -0.692722 \n", + "50 0.423705 1.437671 0.825964 -0.795676 \n", + "35 -0.951747 0.413193 0.050754 -0.695677 \n", + "15 1.383033 -0.201494 -0.724455 -0.283858 \n", + "5 0.334002 0.618089 0.632161 0.155054 \n", + "28 0.336493 -1.021076 0.244557 -0.052333 \n", + "16 1.411273 0.003402 -0.336850 3.632362 \n", + "45 -0.536454 0.003402 -0.336850 -0.596663 \n", + "20 -0.967528 0.208298 -0.336850 -0.339523 \n", + "46 -0.196743 -0.201494 -0.530653 -0.630161 \n", + "8 1.419579 -0.611285 -0.724455 -0.065141 \n", + "13 1.394661 0.822984 0.244557 -0.329178 \n", + "25 1.407950 -0.201494 0.050754 -0.421788 \n", + "17 -0.973342 -0.201494 0.632161 0.079193 \n", + "51 -0.335451 -3.865027 -4.189642 -0.798041 \n", + "42 -0.071325 1.437671 0.825964 -0.483364 \n", + "1 0.338154 0.413193 1.213569 -0.494694 \n", + "12 -0.969189 0.003402 -0.336850 2.415132 \n", + "40 -0.955069 -0.816180 -0.336850 -0.705529 \n", + "24 1.404628 -0.201494 0.244557 1.051105 \n", + "6 1.387186 1.437671 1.019766 -0.520309 \n", + "23 -0.972512 0.003402 0.825964 0.031410 \n", + "36 -0.950916 0.003402 0.244557 -0.665136 \n", + "21 -0.970020 1.232776 0.825964 1.381151 \n", + "19 1.417917 -0.611285 0.050754 -0.165633 \n", + "9 1.419977 -0.406389 -0.724455 0.907264 \n", + "39 -0.950086 0.208298 0.244557 -0.647895 \n", + "49 0.422874 -0.201494 -0.724455 -0.787302 \n", + "3 0.337324 -0.406389 -0.724455 0.799876 \n", + "0 0.338985 0.208298 -0.143048 -0.431640 \n", + "47 -1.033975 -1.021076 -0.724455 -0.444448 \n", + "44 -0.533131 0.003402 -0.143048 -0.433611 \n", + "\n", + " Conductivity (µmhos/cm) Max BOD min(mg/L) BOD max(mg/L) \\\n", + "22 -0.405374 -0.425644 -0.175632 \n", + "7 3.871226 1.034023 0.783162 \n", + "14 -0.323897 1.398940 0.646191 \n", + "34 -0.762568 1.216481 0.509220 \n", + "48 -0.308036 -1.155478 -1.271397 \n", + "18 -0.254587 -1.155478 -1.271397 \n", + "50 -0.839483 0.486648 -0.038662 \n", + "35 -0.740407 0.669106 0.235279 \n", + "15 -0.387992 0.669106 0.372250 \n", + "5 0.007442 -0.425644 -0.175632 \n", + "28 -0.021672 1.034023 0.372250 \n", + "16 3.679332 -0.425644 -0.175632 \n", + "45 -0.644373 -1.155478 -1.271397 \n", + "20 -0.333240 -1.155478 -1.271397 \n", + "46 -0.690869 -1.155478 -1.271397 \n", + "8 0.524114 -1.155478 -1.271397 \n", + "13 -0.427970 -0.243186 -0.586544 \n", + "25 -0.492717 -1.155478 -0.175632 \n", + "17 -0.027321 0.669106 0.098309 \n", + "51 -0.841568 -0.790561 -0.997456 \n", + "42 -0.563982 0.121731 0.098309 \n", + "1 -0.388427 0.669106 0.646191 \n", + "12 2.493029 -1.155478 -1.271397 \n", + "40 -0.758223 1.034023 0.920132 \n", + "24 0.793965 -1.155478 -0.175632 \n", + "6 -0.239378 1.034023 0.372250 \n", + "23 0.020478 0.669106 0.372250 \n", + "36 -0.713900 0.669106 0.920132 \n", + "21 1.577447 -0.790561 -0.723515 \n", + "19 -0.268927 -1.155478 -1.271397 \n", + "9 0.759202 -1.155478 -1.271397 \n", + "39 -0.706078 1.216481 0.783162 \n", + "49 -0.832095 0.304189 -0.175632 \n", + "3 0.967783 1.034023 2.289838 \n", + "0 -0.239813 0.669106 0.235279 \n", + "47 -0.520528 -1.155478 -1.271397 \n", + "44 -0.488372 -1.155478 -0.586544 \n", + "\n", + " Nitrate N min(mg/L) NitrateN max(mg/L) \\\n", + "22 -0.854868 -0.653037 \n", + "7 -0.081833 1.072369 \n", + "14 2.277960 0.209666 \n", + "34 -0.692124 -0.629720 \n", + "48 -0.297469 -0.515470 \n", + "18 -0.407321 -0.419874 \n", + "50 -0.773496 -0.664695 \n", + "35 -0.651438 -0.629720 \n", + "15 1.138750 0.140883 \n", + "5 -0.582272 0.104743 \n", + "28 0.040225 1.468746 \n", + "16 -0.854868 -0.641378 \n", + "45 0.691203 0.116401 \n", + "20 1.749041 0.454487 \n", + "46 0.463361 -0.279976 \n", + "8 1.382866 0.198008 \n", + "13 0.333165 -0.347593 \n", + "25 -0.854868 -0.548113 \n", + "17 -0.655507 -0.594746 \n", + "51 -0.854868 -0.688011 \n", + "42 -0.618889 -0.620394 \n", + "1 -0.610752 0.203837 \n", + "12 1.382866 -0.000180 \n", + "40 -0.692124 -0.629720 \n", + "24 -0.854868 -0.548113 \n", + "6 -0.142862 -0.303292 \n", + "23 0.028020 -0.279976 \n", + "36 -0.692124 -0.606404 \n", + "21 -0.529380 -0.585419 \n", + "19 -0.081833 -0.443190 \n", + "9 3.254426 0.606043 \n", + "39 -0.651438 -0.606404 \n", + "49 -0.773496 -0.664695 \n", + "3 1.016692 4.126803 \n", + "0 -0.610752 1.229754 \n", + "47 0.516253 -0.268318 \n", + "44 -0.081833 -0.279976 \n", + "\n", + " Total Dissolved Solidsmin (mg/L) ... Arsenic min (mg/L)_- \\\n", + "22 -0.006540 ... False \n", + "7 4.248903 ... False \n", + "14 -0.245978 ... False \n", + "34 -0.710677 ... False \n", + "48 -0.211322 ... False \n", + "18 -0.271182 ... False \n", + "50 -0.783138 ... False \n", + "35 -0.698075 ... False \n", + "15 3.603933 ... False \n", + "5 0.119480 ... False \n", + "28 0.048594 ... False \n", + "16 0.048594 ... False \n", + "45 -0.592533 ... False \n", + "20 1.189075 ... False \n", + "46 -0.613799 ... False \n", + "8 -0.123896 ... False \n", + "13 -0.297961 ... False \n", + "25 -0.433433 ... False \n", + "17 -0.666570 ... False \n", + "51 -0.786919 ... False \n", + "42 -0.453911 ... False \n", + "1 -0.446035 ... False \n", + "12 2.370512 ... False \n", + "40 -0.707526 ... False \n", + "24 -0.433433 ... False \n", + "6 -0.485416 ... False \n", + "23 0.985868 ... False \n", + "36 -0.674446 ... False \n", + "21 -0.337342 ... False \n", + "19 -0.375148 ... False \n", + "9 0.637737 ... False \n", + "39 -0.661844 ... False \n", + "49 -0.772899 ... False \n", + "3 0.993744 ... False \n", + "0 -0.373573 ... False \n", + "47 -0.384600 ... False \n", + "44 -0.494867 ... False \n", + "\n", + " Arsenic min (mg/L)_0.001 Arsenic min (mg/L)_0.002 \\\n", + "22 True False \n", + "7 True False \n", + "14 False True \n", + "34 False False \n", + "48 False False \n", + "18 True False \n", + "50 True False \n", + "35 False False \n", + "15 True False \n", + "5 True False \n", + "28 True False \n", + "16 True False \n", + "45 True False \n", + "20 True False \n", + "46 False False \n", + "8 True False \n", + "13 True False \n", + "25 True False \n", + "17 True False \n", + "51 True False \n", + "42 True False \n", + "1 True False \n", + "12 True False \n", + "40 False False \n", + "24 True False \n", + "6 True False \n", + "23 True False \n", + "36 False False \n", + "21 True False \n", + "19 False True \n", + "9 True False \n", + "39 False False \n", + "49 True False \n", + "3 True False \n", + "0 True False \n", + "47 False False \n", + "44 True False \n", + "\n", + " Arsenic min (mg/L)_0.004 Arsenic min (mg/L)_0.007 \\\n", + "22 False False \n", + "7 False False \n", + "14 False False \n", + "34 False False \n", + "48 False False \n", + "18 False False \n", + "50 False False \n", + "35 False False \n", + "15 False False \n", + "5 False False \n", + "28 False False \n", + "16 False False \n", + "45 False False \n", + "20 False False \n", + "46 False False \n", + "8 False False \n", + "13 False False \n", + "25 False False \n", + "17 False False \n", + "51 False False \n", + "42 False False \n", + "1 False False \n", + "12 False False \n", + "40 False False \n", + "24 False False \n", + "6 False False \n", + "23 False False \n", + "36 False False \n", + "21 False False \n", + "19 False False \n", + "9 False False \n", + "39 False False \n", + "49 False False \n", + "3 False False \n", + "0 False False \n", + "47 False False \n", + "44 False False \n", + "\n", + " Arsenic min (mg/L)_0.01 Arsenic max (mg/L)_- Arsenic max (mg/L)_0.001 \\\n", + "22 False False True \n", + "7 False False True \n", + "14 False False False \n", + "34 True False False \n", + "48 True False False \n", + "18 False False True \n", + "50 False False True \n", + "35 True False False \n", + "15 False False True \n", + "5 False False True \n", + "28 False False True \n", + "16 False False True \n", + "45 False False True \n", + "20 False False True \n", + "46 True False False \n", + "8 False False True \n", + "13 False False True \n", + "25 False False True \n", + "17 False False True \n", + "51 False False True \n", + "42 False False True \n", + "1 False False True \n", + "12 False False True \n", + "40 True False False \n", + "24 False False True \n", + "6 False False True \n", + "23 False False True \n", + "36 True False False \n", + "21 False False True \n", + "19 False False False \n", + "9 False False True \n", + "39 True False False \n", + "49 False False True \n", + "3 False False True \n", + "0 False False True \n", + "47 True False False \n", + "44 False False True \n", + "\n", + " Arsenic max (mg/L)_0.002 Arsenic max (mg/L)_0.003 \n", + "22 False False \n", + "7 False False \n", + "14 False True \n", + "34 False False \n", + "48 False False \n", + "18 False False \n", + "50 False False \n", + "35 False False \n", + "15 False False \n", + "5 False False \n", + "28 False False \n", + "16 False False \n", + "45 False False \n", + "20 False False \n", + "46 False False \n", + "8 False False \n", + "13 False False \n", + "25 False False \n", + "17 False False \n", + "51 False False \n", + "42 False False \n", + "1 False False \n", + "12 False False \n", + "40 False False \n", + "24 False False \n", + "6 False False \n", + "23 False False \n", + "36 False False \n", + "21 False False \n", + "19 True False \n", + "9 False False \n", + "39 False False \n", + "49 False False \n", + "3 False False \n", + "0 False False \n", + "47 False False \n", + "44 False False \n", + "\n", + "[37 rows x 142 columns]\n", + "\n", + "y: \n" + ] + } + ], + "source": [ + "print(\"\\nx:\",X_train)\n", + "\n", + "print(\"\\ny: \")\n", + "from sklearn.model_selection import KFold\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error\n", + "\n", + "# Choose a model to use\n", + "model = LinearRegression()\n", + "\n", + "# Split the dataset into k-folds\n", + "kf = KFold(n_splits=5, shuffle=True)\n", + "\n", + "# Perform cross-validation\n", + "mse_scores = []\n", + "for train_index, test_index in kf.split(X_train):\n", + " # Split the data into training and testing sets for this fold\n", + " X_train_fold, X_test_fold = X_train.iloc[train_index], X_train.iloc[test_index]\n", + " y_train_fold, y_test_fold = y_train.iloc[train_index], y_train.iloc[test_index]\n", + " # Train the model on the training set and test it on the testing set\n", + " model.fit(X_train_fold, y_train_fold)\n", + " y_pred = model.predict(X_test_fold)\n", + " mse = mean_squared_error(y_test_fold, y_pred)\n", + " mse_scores.append(mse)\n", + " \n", + "# Compute the average mean squared error across all folds\n", + "avg_mse = sum(mse_scores) / len(mse_scores)\n", + "#print(\"\\navgmse: \",avg_mse)\n", + "\n", + "\n", + "\n", + "Y_train=np.unique(y_train_fold)\n", + "#y_train=y_train.reshape(-1,1)\n", + "#print(\"\\nunique: \",y_train)\n", + "#print(\"\\nunique shape: \",y_train.shape)\n", + "#print(\"\\nlength unique shape: \",len(y_train))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.pyplot as plt1\n", + "from functools import partial\n", + "import numpy as np\n", + "\n", + "\n", + "def f(X):\n", + " A = 10\n", + " sol = []\n", + " for ind in X:\n", + " sol.append(A*len(ind) + sum([(i**2 - A * np.cos(2 * np.pi * i)) for i in ind]) )#output-Y\n", + "\n", + " return np.array(sol)\n", + "x_lb=y_lb=-500\n", + "x_ub=y_ub=500\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "class WOA:\n", + " def __init__(self, obj_func, n_whale, spiral_constant, n_iter,lb, ub,W):\n", + " self.obj_func = obj_func\n", + " self.n_whale = n_whale\n", + " self.spiral_constant = spiral_constant\n", + " self.n_iter = n_iter\n", + " #print('--------------------')\n", + " self.whale = {}\n", + " self.prey = {}\n", + " self.W=W\n", + " #print('----------------------------')\n", + " self.lb = np.array([x_lb, y_lb])\n", + "\n", + " self.ub = np.array([x_ub, y_ub])\n", + "\n", + " def init_whale(self):\n", + " tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),))\n", + " for i in range(self.n_whale)]\n", + " print(\"\\n temp:\",tmp)\n", + " self.whale['position'] = np.array(tmp)\n", + " self.whale['fitness'] = self.obj_func(self.whale['position'])\n", + "\n", + " def init_prey(self):\n", + " \n", + " tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),))]\n", + " \n", + " self.prey['position'] = np.array(tmp)\n", + " self.prey['fitness'] = self.obj_func(self.prey['position'])\n", + "\n", + " \n", + " def update_prey(self):\n", + " if self.whale['fitness'].min() < self.prey['fitness'][0]:\n", + " self.prey['position'][0] = self.whale['position'][self.whale['fitness'].argmin()]\n", + " self.prey['fitness'][0] = self.whale['fitness'].min()\n", + "\n", + " def search(self, idx, A, C):\n", + " random_whale = self.whale['position'][np.random.randint(low=0, high=self.n_whale,\n", + " size=len(idx[0]))]\n", + " d = np.abs(C[..., np.newaxis] * random_whale - self.whale['position'][idx])\n", + " self.whale['position'][idx] = np.clip(random_whale - A[..., np.newaxis] * d, self.lb, self.ub)\n", + "\n", + " def encircle(self, idx, A, C):\n", + " #d = np.abs(C[..., np.newaxis] * self.prey['position'].reshape(1, -1) - self.whale['position'][idx])\n", + " d = np.abs(np.reshape(C, (-1, 1)) * self.prey['position'].reshape(1, -1) - self.whale['position'][idx])\n", + "\n", + " self.whale['position'][idx] = np.clip(self.prey['position'][0] - A[..., np.newaxis] * d, self.lb, self.ub)\n", + "\n", + " def bubble_net(self, idx):\n", + " d_prime = np.abs(self.prey['position'] - self.whale['position'][idx])\n", + " l = np.random.uniform(-1, 1, size=len(idx[0]))\n", + " self.whale[\"position\"][idx] = np.clip(\n", + " d_prime * np.exp(self.spiral_constant * l)[..., np.newaxis] * np.cos(2 * np.pi * l)[..., np.newaxis]\n", + " + self.prey[\"position\"],\n", + " self.lb,\n", + " self.ub,\n", + " )\n", + "\n", + " def optimize(self, a):\n", + "\n", + " p = np.random.random(self.n_whale)\n", + " r1 = np.random.random(self.n_whale)\n", + " r2 = np.random.random(self.n_whale)\n", + " A = 2 * a * r1 - a\n", + " C = 2 * r2\n", + " search_idx = np.where((p < 0.5) & (abs(A) > 1))\n", + " encircle_idx = np.where((p < 0.5) & (abs(A) <= 1))\n", + " bubbleNet_idx = np.where(p >= 0.5)\n", + " self.search(search_idx, A[search_idx], C[search_idx])\n", + " self.encircle(encircle_idx, A[encircle_idx], C[encircle_idx])\n", + " self.bubble_net(bubbleNet_idx)\n", + " self.whale['fitness'] = self.obj_func(self.whale['position'])\n", + "\n", + " def run(self):\n", + " self.init_whale()\n", + " self.init_prey()\n", + " f_values = [self.prey['fitness'][0]]\n", + " #print(\"\\n\\n\\n\\n\\noptimal sol: \",self.prey['position'][0])\n", + " for n in range(self.n_iter):\n", + " #print(\"Iteration = \", n, \" f(x) = \", self.prey['fitness'][0])\n", + " a = 2 - n * (2 / self.n_iter)\n", + " self.optimize(a)\n", + " self.update_prey()\n", + " #l.append((self.loss(out, y_wt)))\n", + " #acc.append(abs((1-(sum(l)/len(x)))*10))\n", + " f_values.append(self.prey['fitness'][0])\n", + " \n", + " optimal_x = self.prey['position'].squeeze()\n", + " #print(\"\\n f_val: \",f_values)\n", + " #print(\"\\n optimal: \",optimal_x)\n", + " return f_values, optimal_x\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "ils: (37, 142)\n" + ] + } + ], + "source": [ + "#neural Network\n", + "input_layer_size = X_train.shape[1]\n", + "print(\"\\nils: \",X_train.shape)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import numpy as np\n", + "\n", + "class NeuralNetwork:\n", + " def __init__(self, input_layer_size, hidden_layer_size, output_layer_size, X):\n", + " self.input_layer_size = input_layer_size\n", + " self.hidden_layer_size = hidden_layer_size\n", + " self.output_layer_size = output_layer_size\n", + "\n", + " # Initialize the weights with random values\n", + " self.W1 = np.random.randn(input_layer_size, hidden_layer_size)\n", + " self.W2 = np.random.randn(hidden_layer_size, output_layer_size)\n", + "\n", + " # Initialize the biases with zeros\n", + " self.b1 = np.zeros((1, hidden_layer_size))\n", + " self.b2 = np.zeros((1, output_layer_size))\n", + "\n", + " def sigmoid(self, x):\n", + " x = np.array(x, dtype=float) \n", + " return 1 / (1 + np.exp(-x))\n", + "\n", + " def forward_propagation(self, X):\n", + " # Calculate the hidden layer activations\n", + " self.Z1 = np.dot(X, self.W1) + self.b1\n", + " self.A1 = self.sigmoid(self.Z1)\n", + "\n", + " # Calculate the output layer activations\n", + " self.Z2 = np.dot(self.A1, self.W2) + self.b2\n", + " self.A2 = self.sigmoid(self.Z2)\n", + "\n", + " return self.A2\n", + "\n", + " def backward_propagation(self, X, Y, output, learning_rate):\n", + " # Reshape Y to match the shape of output\n", + " Y = Y.values.reshape(-1, 1)\n", + "\n", + " # Calculate the error in the output layer\n", + " dZ2 = output - Y\n", + " dW2 = np.dot(self.A1.T, dZ2)\n", + " db2 = np.sum(dZ2, axis=0, keepdims=True)\n", + "\n", + " # Calculate the error in the hidden layer\n", + " dZ1 = np.dot(dZ2, self.W2.T) * (self.A1 * (1 - self.A1))\n", + " dW1 = np.dot(X.T, dZ1)\n", + " db1 = np.sum(dZ1, axis=0, keepdims=True)\n", + " self.W1 = self.W1.astype('float64')\n", + " dW1 = dW1.astype('float64')\n", + " # Update the weights and biases\n", + " self.W1 -= learning_rate * dW1\n", + " self.b1 -= learning_rate * db1\n", + " self.W2 -= learning_rate * dW2\n", + " self.b2 -= learning_rate * db2\n", + " def loss(self, y_pred, y_true):\n", + " y_true = y_true.values.reshape(-1, 1)\n", + " y_pred_binary = (y_pred >= 0.5).astype(int)\n", + " y_true_binary = (y_true >= 0.5).astype(int)\n", + " mse = np.mean((y_pred - y_true_binary)**2)\n", + " return mse\n", + "\n", + " def accuracy(self, y_pred, y_true):\n", + " y_true = y_true.values.reshape(-1, 1)\n", + " y_pred_binary = (y_pred >= 0.5).astype(int)\n", + " y_true_binary = (y_true >= 0.5).astype(int)\n", + " return (y_pred_binary == y_true_binary).mean() * 100\n", + "\n", + " def rmsee(self, y_pred, y_train):\n", + " mse = mean_squared_error(y_train, y_pred)\n", + " rmse = mean_squared_error(y_train, y_pred, squared=False)\n", + " return rmse\n", + "\n", + " def train(self, X, Y, epoch=10, alpha=0.01):\n", + " acc = []\n", + " losss = []\n", + " rm = []\n", + " for j in range(epoch):\n", + " out = self.forward_propagation(X)\n", + " self.backward_propagation(X, Y, out, alpha)\n", + " acc.append(self.accuracy(out, Y))\n", + " losss.append(self.loss(out, Y))\n", + " rm.append(self.rmsee(out, Y))\n", + "\n", + " return acc, losss, rm\n", + "\n", + " def predict(self, X):\n", + " # Forward propagation to get the output\n", + " output = self.forward_propagation(X)\n", + "\n", + " # Apply the threshold to classify the output\n", + " predictions = (output >= 0.5).astype(int)\n", + "\n", + " return predictions\n", + "\n", + "\n", + "# Define your ANN architecture\n", + "input_layer_size = X_train.shape[1]\n", + "\n", + "hidden_layer_size = 10\n", + "output_layer_size = 1\n", + "\n", + "weights = np.random.rand(input_layer_size*hidden_layer_size + hidden_layer_size*output_layer_size)\n", + "\n", + "def fitness_function(weights):\n", + " \n", + " nn=NeuralNetwork(input_layer_size,hidden_layer_size,output_layer_size,X_train)\n", + " return nn\n", + " \n", + "\n", + " \n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accuracy: 83.78378378378379\n", + "Loss: 0.10386266491656121\n", + "Predictions: [[0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [1]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]\n", + " [0]]\n", + "Test Accuracy: 93.75\n", + "Sample 1: Groundwater is not harmful.\n", + "Sample 2: Groundwater is not harmful.\n", + "Sample 3: Groundwater is not harmful.\n", + "Sample 4: Groundwater is not harmful.\n", + "Sample 5: Groundwater is not harmful.\n", + "Sample 6: Groundwater is not harmful.\n", + "Sample 7: Groundwater is not harmful.\n", + "Sample 8: Groundwater is harmful.\n", + "Sample 9: Groundwater is not harmful.\n", + "Sample 10: Groundwater is not harmful.\n", + "Sample 11: Groundwater is not harmful.\n", + "Sample 12: Groundwater is not harmful.\n", + "Sample 13: Groundwater is not harmful.\n", + "Sample 14: Groundwater is not harmful.\n", + "Sample 15: Groundwater is not harmful.\n", + "Sample 16: Groundwater is not harmful.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n", + "c:\\Users\\stuti\\anaconda3\\envs\\deep-learning\\lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "ff=fitness_function(weights)\n", + "val=ff.forward_propagation( X_train)\n", + "\n", + "\n", + "acc,losss,rm=ff.train(X_train,y_train,10,0.01)\n", + "\n", + "\n", + "max_accuracy = acc[0]\n", + "\n", + "for i in range(1, len(acc)):\n", + " if acc[i] > max_accuracy:\n", + " max_accuracy = acc[i]\n", + "\n", + "print(\"Accuracy:\", max_accuracy)\n", + "print(\"Loss:\",losss[len(losss)-1])\n", + "#print(\"\\ntrain: \",acc,losss)\n", + "#print(ff.predict(X_train))\n", + "predictions = ff.predict(X_test)\n", + "\n", + "# Print the predictions\n", + "print(\"Predictions:\", predictions)\n", + "accuracy = ff.accuracy(predictions, y_test)\n", + "print(\"Test Accuracy:\", accuracy)\n", + "\n", + "# Print messages for groundwater quality\n", + "for i, prediction in enumerate(predictions):\n", + " if prediction == 1:\n", + " print(f\"Sample {i+1}: Groundwater is harmful.\")\n", + " else:\n", + " print(f\"Sample {i+1}: Groundwater is not harmful.\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plotting accuracy\n", + "plt.subplot(1, 3, 3)\n", + "plt.plot(rm)\n", + "plt.ylabel('RMSE value')\n", + "plt.xlabel(\"Epochs:\")\n", + "plt.show()\n", + "\n", + "# Plotting Loss\n", + "plt.subplot(1, 2, 1)\n", + "plt.plot(losss)\n", + "plt.title(\"Loss over Time\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Loss\")\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.plot(acc)\n", + "plt.title(\"Accuracy over Time\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Accuracy\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "deep-learning", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Groundwater Arsenic Content Detection/models/ann.ipynb b/Groundwater Arsenic Content Detection/models/ann.ipynb new file mode 100644 index 000000000..e69de29bb diff --git a/Groundwater Arsenic Content Detection/models/random_forest.ipynb b/Groundwater Arsenic Content Detection/models/random_forest.ipynb new file mode 100644 index 000000000..a285413eb --- /dev/null +++ b/Groundwater Arsenic Content Detection/models/random_forest.ipynb @@ -0,0 +1,152 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.model_selection import train_test_split, KFold\n", + "from sklearn.ensemble import RandomForestRegressor\n", + "from sklearn.metrics import mean_squared_error, accuracy_score\n", + "import matplotlib.pyplot as plt\n", + "\n", + "data = pd.read_csv('../data/Ground Water .csv')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Fold 1:\n", + "MSE: 0.0105\n", + "RMSE: 0.1022\n", + "\n", + "Fold 2:\n", + "MSE: 0.0006\n", + "RMSE: 0.0245\n", + "\n", + "Fold 3:\n", + "MSE: 0.0000\n", + "RMSE: 0.0053\n", + "\n", + "Fold 4:\n", + "MSE: 0.0076\n", + "RMSE: 0.0871\n", + "\n", + "Fold 5:\n", + "MSE: 0.0009\n", + "RMSE: 0.0305\n" + ] + } + ], + "source": [ + "\n", + "# Fill NaN values in numeric columns with median\n", + "numeric_columns = data.select_dtypes(include='number').columns\n", + "data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median())\n", + "\n", + "# Handle outliers using quantile clipping\n", + "numeric_data = data.select_dtypes(include=[np.number])\n", + "data[numeric_data.columns] = numeric_data.clip(\n", + " lower=numeric_data.quantile(0.01), \n", + " upper=numeric_data.quantile(0.99), \n", + " axis=1\n", + ")\n", + "\n", + "# Convert categorical variables to dummy variables\n", + "data = pd.get_dummies(data)\n", + "\n", + "# Scale the features\n", + "scaler = StandardScaler()\n", + "data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(\n", + " data.select_dtypes(include=['float64'])\n", + ")\n", + "\n", + "# Drop specified columns and split features/target\n", + "data = data.drop(data.columns[[1, 2]], axis=1)\n", + "X = data.iloc[:,:-3]\n", + "y = data.iloc[:, -1]\n", + "\n", + "# Split the data\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)\n", + "\n", + "# Initialize Random Forest model\n", + "rf_model = RandomForestRegressor(\n", + " n_estimators=100,\n", + " max_depth=10,\n", + " random_state=42,\n", + " n_jobs=-1\n", + ")\n", + "\n", + "# Perform K-fold cross-validation\n", + "kf = KFold(n_splits=5, shuffle=True, random_state=42)\n", + "mse_scores = []\n", + "accuracy_scores = []\n", + "rmse_scores = []\n", + "\n", + "for fold, (train_index, val_index) in enumerate(kf.split(X_train), 1):\n", + " # Split data for this fold\n", + " X_train_fold = X_train.iloc[train_index]\n", + " X_val_fold = X_train.iloc[val_index]\n", + " y_train_fold = y_train.iloc[train_index]\n", + " y_val_fold = y_train.iloc[val_index]\n", + " \n", + " # Train the model\n", + " rf_model.fit(X_train_fold, y_train_fold)\n", + " \n", + " # Make predictions\n", + " y_pred = rf_model.predict(X_val_fold)\n", + " \n", + " # Calculate metrics\n", + " mse = mean_squared_error(y_val_fold, y_pred)\n", + " rmse = np.sqrt(mse)\n", + " \n", + " # Convert predictions to binary for accuracy calculation\n", + " y_pred_binary = (y_pred >= 0.5).astype(int)\n", + " y_val_binary = (y_val_fold >= 0.5).astype(int)\n", + " acc = accuracy_score(y_val_binary, y_pred_binary) * 100\n", + " \n", + " # Store scores\n", + " mse_scores.append(mse)\n", + " accuracy_scores.append(acc)\n", + " rmse_scores.append(rmse)\n", + " \n", + " print(f\"\\nFold {fold}:\")\n", + " print(f\"MSE: {mse:.4f}\")\n", + " print(f\"RMSE: {rmse:.4f}\")\n", + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "deep-learning", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From d3dad96fe95f353bcfb5947e1939a7392f60346d Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:26:55 +0530 Subject: [PATCH 25/30] Delete Groundwater Arsenic Content Detection/models/random_forest.py --- .../models/random_forest.py | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 Groundwater Arsenic Content Detection/models/random_forest.py diff --git a/Groundwater Arsenic Content Detection/models/random_forest.py b/Groundwater Arsenic Content Detection/models/random_forest.py deleted file mode 100644 index 120d1a126..000000000 --- a/Groundwater Arsenic Content Detection/models/random_forest.py +++ /dev/null @@ -1,136 +0,0 @@ -import pandas as pd -import numpy as np -from sklearn.preprocessing import StandardScaler -from sklearn.model_selection import train_test_split, KFold -from sklearn.ensemble import RandomForestRegressor -from sklearn.metrics import mean_squared_error, accuracy_score -import matplotlib.pyplot as plt - -# Load and preprocess data -data = pd.read_csv('../Ground Water .csv') - -# Fill NaN values in numeric columns with median -numeric_columns = data.select_dtypes(include='number').columns -data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) - -# Handle outliers using quantile clipping -numeric_data = data.select_dtypes(include=[np.number]) -data[numeric_data.columns] = numeric_data.clip( - lower=numeric_data.quantile(0.01), - upper=numeric_data.quantile(0.99), - axis=1 -) - -# Convert categorical variables to dummy variables -data = pd.get_dummies(data) - -# Scale the features -scaler = StandardScaler() -data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform( - data.select_dtypes(include=['float64']) -) - -# Drop specified columns and split features/target -data = data.drop(data.columns[[1, 2]], axis=1) -X = data.iloc[:,:-3] -y = data.iloc[:, -1] - -# Split the data -X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) - -# Initialize Random Forest model -rf_model = RandomForestRegressor( - n_estimators=100, - max_depth=10, - random_state=42, - n_jobs=-1 -) - -# Perform K-fold cross-validation -kf = KFold(n_splits=5, shuffle=True, random_state=42) -mse_scores = [] -accuracy_scores = [] -rmse_scores = [] - -for fold, (train_index, val_index) in enumerate(kf.split(X_train), 1): - # Split data for this fold - X_train_fold = X_train.iloc[train_index] - X_val_fold = X_train.iloc[val_index] - y_train_fold = y_train.iloc[train_index] - y_val_fold = y_train.iloc[val_index] - - # Train the model - rf_model.fit(X_train_fold, y_train_fold) - - # Make predictions - y_pred = rf_model.predict(X_val_fold) - - # Calculate metrics - mse = mean_squared_error(y_val_fold, y_pred) - rmse = np.sqrt(mse) - - # Convert predictions to binary for accuracy calculation - y_pred_binary = (y_pred >= 0.5).astype(int) - y_val_binary = (y_val_fold >= 0.5).astype(int) - acc = accuracy_score(y_val_binary, y_pred_binary) * 100 - - # Store scores - mse_scores.append(mse) - accuracy_scores.append(acc) - rmse_scores.append(rmse) - - print(f"\nFold {fold}:") - print(f"MSE: {mse:.4f}") - print(f"RMSE: {rmse:.4f}") - print(f"Accuracy: {acc:.2f}%") - -# Calculate and print average metrics -avg_mse = np.mean(mse_scores) -avg_rmse = np.mean(rmse_scores) -avg_accuracy = np.mean(accuracy_scores) - -print("\nAverage Metrics across all folds:") -print(f"Average MSE: {avg_mse:.4f}") -print(f"Average RMSE: {avg_rmse:.4f}") - -# Plot the metrics -plt.figure(figsize=(15, 5)) - -# Plot Accuracy -plt.subplot(1, 3, 1) -plt.plot(range(1, 6), accuracy_scores, marker='o') -plt.title('Accuracy across Folds') -plt.xlabel('Fold') -plt.ylabel('Accuracy (%)') - -# Plot MSE -plt.subplot(1, 3, 2) -plt.plot(range(1, 6), mse_scores, marker='o', color='red') -plt.title('MSE across Folds') -plt.xlabel('Fold') -plt.ylabel('MSE') - -# Plot RMSE -plt.subplot(1, 3, 3) -plt.plot(range(1, 6), rmse_scores, marker='o', color='green') -plt.title('RMSE across Folds') -plt.xlabel('Fold') -plt.ylabel('RMSE') - -plt.tight_layout() -plt.show() - -# Feature Importance Analysis -feature_importance = pd.DataFrame({ - 'feature': X_train.columns, - 'importance': rf_model.feature_importances_ -}) -feature_importance = feature_importance.sort_values('importance', ascending=False) - -# Plot feature importance -plt.figure(figsize=(10, 6)) -plt.bar(range(len(feature_importance)), feature_importance['importance']) -plt.xticks(range(len(feature_importance)), feature_importance['feature'], rotation=45, ha='right') -plt.title('Feature Importance in Random Forest Model') -plt.tight_layout() -plt.show() From f293173414c6634d6a432d3fe4bcd819ebfc9a90 Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:27:50 +0530 Subject: [PATCH 26/30] Delete Groundwater Arsenic Content Detection/models/final_ann.py --- .../models/final_ann.py | 275 ------------------ 1 file changed, 275 deletions(-) delete mode 100644 Groundwater Arsenic Content Detection/models/final_ann.py diff --git a/Groundwater Arsenic Content Detection/models/final_ann.py b/Groundwater Arsenic Content Detection/models/final_ann.py deleted file mode 100644 index 4d3cdb8a6..000000000 --- a/Groundwater Arsenic Content Detection/models/final_ann.py +++ /dev/null @@ -1,275 +0,0 @@ - - -import pandas as pd -from sklearn.preprocessing import StandardScaler -from sklearn.model_selection import train_test_split -import numpy as np -from sklearn.model_selection import train_test_split -from sklearn.metrics import accuracy_score -from sklearn.metrics import average_precision_score -from sklearn.metrics import mean_squared_error - - -data = pd.read_csv('../Ground Water .csv') - - - -#data = data.fillna(data.mean()) -# Fill NaN values only in numeric columns with median -numeric_columns = data.select_dtypes(include='number').columns -data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) - - -# Select only numeric columns for quantile calculations -numeric_data = data.select_dtypes(include=[np.number]) -data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) - -#data = data.clip(lower=data.quantile(0.01), upper=data.quantile(0.99), axis=1) - -data = pd.get_dummies(data) - -scaler = StandardScaler() -data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) - -data = data.drop(data.columns[[1, 2]], axis=1) -X = data.iloc[:,:-3] -y = data.iloc[:, -1] - -#print("\nX: ",X) -X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) - -#print("\nx: ") -#print("\nx:",X_train) -#print(X_train.shape()) - -from sklearn.model_selection import KFold -from sklearn.linear_model import LinearRegression -from sklearn.metrics import mean_squared_error - -model = LinearRegression() - -kf = KFold(n_splits=5, shuffle=True) - - -mse_scores = [] -for train_index, test_index in kf.split(X_train): - # Split the data into training and testing sets for this fold - X_train_fold, X_test_fold = X_train.iloc[train_index], X_train.iloc[test_index] - y_train_fold, y_test_fold = y_train.iloc[train_index], y_train.iloc[test_index] - # Train the model on the training set and test it on the testing set - model.fit(X_train_fold, y_train_fold) - y_pred = model.predict(X_test_fold) - mse = mean_squared_error(y_test_fold, y_pred) - mse_scores.append(mse) - -# Compute the average mean squared error across all folds -avg_mse = sum(mse_scores) / len(mse_scores) -print("\navgmse: ",avg_mse) - -Y_train=np.unique(y_train_fold) -#y_train=y_train.reshape(-1,1) -print("\ny_train: ",y_train) -print("\ny_train shape: ",y_train.shape) - -#neural Network -input_layer_size = X_train.shape[1] -print("\ninput_layer_shape: ",X_train.shape[1]) -import numpy as np - -class NeuralNetwork: - def __init__(self,input_layer_size,hidden_layer_size,output_layer_size,X): - self.input_layer_size = input_layer_size - self.hidden_layer_size = hidden_layer_size - self.output_layer_size = output_layer_size - - # Initialize the weights with random values - self.W1 = np.random.randn(input_layer_size,hidden_layer_size) - self.W2 = np.random.randn(hidden_layer_size,output_layer_size) - print("\nW1: ",self.W1) - print("\nw1_size: ",self.W1.shape) - print("\nW2: ",self.W2) - print("\nw2_size: ",self.W2.shape) - # Initialize the biases with zeros - self.b1 = np.random.randn(len(X),hidden_layer_size)#x_train - self.b2 = np.random.randn(len(X),output_layer_size)#x_train - print("\nB1:",self.b1) - print("\nB2:",self.b2) - - def sigmoid(self, x): - x = np.array(x, dtype=float) # Ensure x is a NumPy array of floats - if np.isnan(x).any(): - raise ValueError("Input contains NaN values.") - return 1 / (1 + np.exp(-x)) - - - - def forward_propagation(self, X): - self.Z1 =np.dot(X,self.W1) + self.b1 - #print("\nZ1: ",self.Z1) - #print("\nz1size: ",self.Z1.shape) - self.A1 = self.sigmoid(self.Z1) - #print("\nA1: ",self.A1) - #print("\nA1size: ",self.A1.shape) - self.Z2 = np.dot(self.A1,self.W2) + self.b2 - #print("\nZ2: ",self.Z2) - #print("\nZ2size: ",self.Z2.shape) - self.A2 = self.sigmoid(self.Z2) - #print("\nA2: ",self.A2) - #print("\n A2 shape",self.A2.shape) - return self.A2 - - def generate_wt(self,x, y): - l =[] - for i in range(len(x) * len(y)): - l.append(np.random.randn()) - return(np.array(l).reshape(len(x),len(y))) - - def backward_propagation(self, X, Y, output,learning_rate): - #print("\n Output",output) - #print("\noutput: ",output.shape) - Y=np.array(Y) - Y=Y.reshape(-1,1) - #print("\ny_shape: ",Y.shape) - dZ2 = output - Y - #print("\ndz2 ",dZ2.shape) - #print(dZ2) - #print("\n w2: ",self.W2) - dW2 = np.dot(self.A1.T,dZ2) - - #print("\ndw2: ",dW2) - db2 = np.sum(dZ2, axis=1, keepdims=True) - - #print("\nw2 size:",self.W2.shape) - #print("\ndw2 size:",dZ2.shape) - dZ1=np.multiply((self.W2.dot((dZ2.T))).T,(np.multiply(self.A1, 1-self.A1))) - #print('\ndz1:',dZ1) - #dZ1 = np.dot(self.W2.T, dZ2) * (self.A1 * (1 - self.A1)) - dW1 = np.dot(X.T,dZ1) - db1 = np.sum(dZ1, axis=1, keepdims=True) - #print("\ndw1: ",dW1) - #print("\ndb1: ",db1) - #print("\ndw2: ",dW2) - #print("\ndb2: ",db2) - # Update the weights and biases - self.W1 = self.W1.astype('float64') - dW1 = dW1.astype('float64') - - self.W1 -= learning_rate*dW1 - self.b1 -= learning_rate*db1 - self.W2 -= learning_rate*dW2 - self.b2 -= learning_rate*db2 - #return self.W1,self.W2 - - #1 - - def loss(self,y_pred, y_true): - y_true = y_true.values.reshape(-1, 1) - #print("\n y_p:",y_pred) - #print("\n y_t:",y_true) - y_pred_binary = (y_pred >= 0.5).astype(int) - y_true_binary = (y_true >= 0.5).astype(int) - mse = np.mean((y_pred - y_true_binary)**2) - print(f"\nMSE: {mse}") - return mse - - def accuracy(self,y_pred,y_true): - y_true = y_true.values.reshape(-1, 1) - y_pred_binary = (y_pred >= 0.5).astype(int) - - y_true_binary = (y_true >= 0.5).astype(int) - - return (y_pred_binary == y_true_binary).mean() * 100 - - def rmsee(self,y_pred,y_train): - mse = mean_squared_error(y_train, y_pred) - rmse = mean_squared_error(y_train, y_pred, squared=False) - #print(f"MSE: {mse}") - #print(f"RMSE: {rmse}") - return rmse - - - def train(self,x, Y, epoch =10,alpha = 0.01): - acc =[] - losss =[] - rm=[] - for j in range(epoch): - out = self.forward_propagation(x) - self.backward_propagation(x, Y,out,alpha) - #print("\n out: ",out) - #print("\n Y:",Y) - print("epochs:", j + 1, "======== acc:", self.accuracy(out, Y)) - acc.append(self.accuracy(out,Y)) - losss.append(self.loss(out,Y)) - rm.append(self.rmsee(out,Y)) - #print("\n rm:",rm) - return(acc, losss,rm) - - - - def predict(self,x): - out = self.forward_propagation(x) - #print("\n out: ",out) - new_arr=[] - for i in range(len(out)): - if(out[i][0]<0.05): - new_arr.append(0) - else: - new_arr.append(1) - #print("\n new_arr: ",new_arr) - - -# Define your ANN architecture -input_layer_size = X_train.shape[1] -#print("\nils: ",X_train.shape[1]) -hidden_layer_size = 10 -output_layer_size = 1 -#(len(y_train)) -#print("\nols: ",output_layer_size) -weights = np.random.rand(input_layer_size*hidden_layer_size + hidden_layer_size*output_layer_size) - -def fitness_function(weights): - - nn=NeuralNetwork(input_layer_size,hidden_layer_size,output_layer_size,X_train) - - return nn - - - - - -ff=fitness_function(weights) -val=ff.forward_propagation( X_train) - -acc,losss,rm=ff.train(X_train,y_train,10,0.01) -ac=acc[0] -# print("\ntrain: ",acc,losss) -# print("\n\n round: ",ff.predict(X_train)) -print("rmse: ",rm[len(rm)-1]) -print("Accuracy: ",ac) -print("Loss: ",losss[len(losss)-1]) - - - -import matplotlib.pyplot as plt1 - -# plotting accuracy -plt1.subplot(1, 3, 1) -plt1.plot(acc) -plt1.ylabel('Accuracy') -plt1.xlabel("Epochs:") -plt1.show() - - -# plotting Loss -plt1.subplot(1, 3, 2) -plt1.plot(losss) -plt1.ylabel('Loss') -plt1.xlabel("Epochs:") -plt1.show() - -plt1.subplot(1, 3, 3) -plt1.plot(rm) -plt1.ylabel('RMSE value: ') -plt1.xlabel("Epochs:") -plt1.show() - From 4138a2ac2a337f54097569c15b8f4e2dc9447ddf Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:28:13 +0530 Subject: [PATCH 27/30] Delete Groundwater Arsenic Content Detection/models/ann+woa_final.py --- .../models/ann+woa_final.py | 364 ------------------ 1 file changed, 364 deletions(-) delete mode 100644 Groundwater Arsenic Content Detection/models/ann+woa_final.py diff --git a/Groundwater Arsenic Content Detection/models/ann+woa_final.py b/Groundwater Arsenic Content Detection/models/ann+woa_final.py deleted file mode 100644 index 084d8a09a..000000000 --- a/Groundwater Arsenic Content Detection/models/ann+woa_final.py +++ /dev/null @@ -1,364 +0,0 @@ - -import pandas as pd -from sklearn.preprocessing import StandardScaler -from sklearn.model_selection import train_test_split -import numpy as np -from sklearn.model_selection import train_test_split -from sklearn.metrics import accuracy_score -from sklearn.metrics import average_precision_score -from sklearn.metrics import mean_squared_error -from flask import Flask, request, jsonify, render_template -import pandas as pd -from sklearn.preprocessing import StandardScaler -import numpy as np -data = pd.read_csv('../Ground Water .csv') - -numeric_columns = data.select_dtypes(include='number').columns -data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) - - -# Select only numeric columns for quantile calculations -numeric_data = data.select_dtypes(include=[np.number]) -data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) - - -# Encode categorical variables -data = pd.get_dummies(data) - -# Scale numerical variables -scaler = StandardScaler() -data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) - - -data = data.drop(data.columns[[1, 2]], axis=1) -X = data.iloc[:,:-3] -y = data.iloc[:, -1] -#print("\nX") -#print(X) -X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) - -#print("\nx: ") -print("\nx:",X_train) -#print(X_train.shape()) -print("\ny: ") -from sklearn.model_selection import KFold -from sklearn.linear_model import LinearRegression -from sklearn.metrics import mean_squared_error - -# Choose a model to use -model = LinearRegression() - -# Split the dataset into k-folds -kf = KFold(n_splits=5, shuffle=True) - -# Perform cross-validation -mse_scores = [] -for train_index, test_index in kf.split(X_train): - # Split the data into training and testing sets for this fold - X_train_fold, X_test_fold = X_train.iloc[train_index], X_train.iloc[test_index] - y_train_fold, y_test_fold = y_train.iloc[train_index], y_train.iloc[test_index] - # Train the model on the training set and test it on the testing set - model.fit(X_train_fold, y_train_fold) - y_pred = model.predict(X_test_fold) - mse = mean_squared_error(y_test_fold, y_pred) - mse_scores.append(mse) - -# Compute the average mean squared error across all folds -avg_mse = sum(mse_scores) / len(mse_scores) -#print("\navgmse: ",avg_mse) - - - -Y_train=np.unique(y_train_fold) -#y_train=y_train.reshape(-1,1) -#print("\nunique: ",y_train) -#print("\nunique shape: ",y_train.shape) -#print("\nlength unique shape: ",len(y_train)) - - -#from woa import WOA -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.pyplot as plt1 -from functools import partial -import numpy as np - - -def f(X): - A = 10 - sol = [] - for ind in X: - sol.append(A*len(ind) + sum([(i**2 - A * np.cos(2 * np.pi * i)) for i in ind]) )#output-Y - - return np.array(sol) -x_lb=y_lb=-500 -x_ub=y_ub=500 - - - - - - - - -class WOA: - def __init__(self, obj_func, n_whale, spiral_constant, n_iter,lb, ub,W): - self.obj_func = obj_func - self.n_whale = n_whale - self.spiral_constant = spiral_constant - self.n_iter = n_iter - #print('--------------------') - self.whale = {} - self.prey = {} - self.W=W - #print('----------------------------') - self.lb = np.array([x_lb, y_lb]) - - self.ub = np.array([x_ub, y_ub]) - - def init_whale(self): - tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),)) - for i in range(self.n_whale)] - print("\n temp:",tmp) - self.whale['position'] = np.array(tmp) - self.whale['fitness'] = self.obj_func(self.whale['position']) - - def init_prey(self): - - tmp = [np.random.uniform(self.lb, self.ub, size=(len(self.lb),))] - - self.prey['position'] = np.array(tmp) - self.prey['fitness'] = self.obj_func(self.prey['position']) - - - def update_prey(self): - if self.whale['fitness'].min() < self.prey['fitness'][0]: - self.prey['position'][0] = self.whale['position'][self.whale['fitness'].argmin()] - self.prey['fitness'][0] = self.whale['fitness'].min() - - def search(self, idx, A, C): - random_whale = self.whale['position'][np.random.randint(low=0, high=self.n_whale, - size=len(idx[0]))] - d = np.abs(C[..., np.newaxis] * random_whale - self.whale['position'][idx]) - self.whale['position'][idx] = np.clip(random_whale - A[..., np.newaxis] * d, self.lb, self.ub) - - def encircle(self, idx, A, C): - #d = np.abs(C[..., np.newaxis] * self.prey['position'].reshape(1, -1) - self.whale['position'][idx]) - d = np.abs(np.reshape(C, (-1, 1)) * self.prey['position'].reshape(1, -1) - self.whale['position'][idx]) - - self.whale['position'][idx] = np.clip(self.prey['position'][0] - A[..., np.newaxis] * d, self.lb, self.ub) - - def bubble_net(self, idx): - d_prime = np.abs(self.prey['position'] - self.whale['position'][idx]) - l = np.random.uniform(-1, 1, size=len(idx[0])) - self.whale["position"][idx] = np.clip( - d_prime * np.exp(self.spiral_constant * l)[..., np.newaxis] * np.cos(2 * np.pi * l)[..., np.newaxis] - + self.prey["position"], - self.lb, - self.ub, - ) - - def optimize(self, a): - - p = np.random.random(self.n_whale) - r1 = np.random.random(self.n_whale) - r2 = np.random.random(self.n_whale) - A = 2 * a * r1 - a - C = 2 * r2 - search_idx = np.where((p < 0.5) & (abs(A) > 1)) - encircle_idx = np.where((p < 0.5) & (abs(A) <= 1)) - bubbleNet_idx = np.where(p >= 0.5) - self.search(search_idx, A[search_idx], C[search_idx]) - self.encircle(encircle_idx, A[encircle_idx], C[encircle_idx]) - self.bubble_net(bubbleNet_idx) - self.whale['fitness'] = self.obj_func(self.whale['position']) - - def run(self): - self.init_whale() - self.init_prey() - f_values = [self.prey['fitness'][0]] - #print("\n\n\n\n\noptimal sol: ",self.prey['position'][0]) - for n in range(self.n_iter): - #print("Iteration = ", n, " f(x) = ", self.prey['fitness'][0]) - a = 2 - n * (2 / self.n_iter) - self.optimize(a) - self.update_prey() - #l.append((self.loss(out, y_wt))) - #acc.append(abs((1-(sum(l)/len(x)))*10)) - f_values.append(self.prey['fitness'][0]) - - optimal_x = self.prey['position'].squeeze() - #print("\n f_val: ",f_values) - #print("\n optimal: ",optimal_x) - return f_values, optimal_x - - - - - - -#neural Network -input_layer_size = X_train.shape[1] -print("\nils: ",X_train.shape) - -import numpy as np - -class NeuralNetwork: - def __init__(self, input_layer_size, hidden_layer_size, output_layer_size, X): - self.input_layer_size = input_layer_size - self.hidden_layer_size = hidden_layer_size - self.output_layer_size = output_layer_size - - # Initialize the weights with random values - self.W1 = np.random.randn(input_layer_size, hidden_layer_size) - self.W2 = np.random.randn(hidden_layer_size, output_layer_size) - - # Initialize the biases with zeros - self.b1 = np.zeros((1, hidden_layer_size)) - self.b2 = np.zeros((1, output_layer_size)) - - def sigmoid(self, x): - x = np.array(x, dtype=float) - return 1 / (1 + np.exp(-x)) - - def forward_propagation(self, X): - # Calculate the hidden layer activations - self.Z1 = np.dot(X, self.W1) + self.b1 - self.A1 = self.sigmoid(self.Z1) - - # Calculate the output layer activations - self.Z2 = np.dot(self.A1, self.W2) + self.b2 - self.A2 = self.sigmoid(self.Z2) - - return self.A2 - - def backward_propagation(self, X, Y, output, learning_rate): - # Reshape Y to match the shape of output - Y = Y.values.reshape(-1, 1) - - # Calculate the error in the output layer - dZ2 = output - Y - dW2 = np.dot(self.A1.T, dZ2) - db2 = np.sum(dZ2, axis=0, keepdims=True) - - # Calculate the error in the hidden layer - dZ1 = np.dot(dZ2, self.W2.T) * (self.A1 * (1 - self.A1)) - dW1 = np.dot(X.T, dZ1) - db1 = np.sum(dZ1, axis=0, keepdims=True) - self.W1 = self.W1.astype('float64') - dW1 = dW1.astype('float64') - # Update the weights and biases - self.W1 -= learning_rate * dW1 - self.b1 -= learning_rate * db1 - self.W2 -= learning_rate * dW2 - self.b2 -= learning_rate * db2 - def loss(self, y_pred, y_true): - y_true = y_true.values.reshape(-1, 1) - y_pred_binary = (y_pred >= 0.5).astype(int) - y_true_binary = (y_true >= 0.5).astype(int) - mse = np.mean((y_pred - y_true_binary)**2) - return mse - - def accuracy(self, y_pred, y_true): - y_true = y_true.values.reshape(-1, 1) - y_pred_binary = (y_pred >= 0.5).astype(int) - y_true_binary = (y_true >= 0.5).astype(int) - return (y_pred_binary == y_true_binary).mean() * 100 - - def rmsee(self, y_pred, y_train): - mse = mean_squared_error(y_train, y_pred) - rmse = mean_squared_error(y_train, y_pred, squared=False) - return rmse - - def train(self, X, Y, epoch=10, alpha=0.01): - acc = [] - losss = [] - rm = [] - for j in range(epoch): - out = self.forward_propagation(X) - self.backward_propagation(X, Y, out, alpha) - acc.append(self.accuracy(out, Y)) - losss.append(self.loss(out, Y)) - rm.append(self.rmsee(out, Y)) - - return acc, losss, rm - - def predict(self, X): - # Forward propagation to get the output - output = self.forward_propagation(X) - - # Apply the threshold to classify the output - predictions = (output >= 0.5).astype(int) - - return predictions - - -# Define your ANN architecture -input_layer_size = X_train.shape[1] - -hidden_layer_size = 10 -output_layer_size = 1 - -weights = np.random.rand(input_layer_size*hidden_layer_size + hidden_layer_size*output_layer_size) - -def fitness_function(weights): - - nn=NeuralNetwork(input_layer_size,hidden_layer_size,output_layer_size,X_train) - return nn - - - - - - -ff=fitness_function(weights) -val=ff.forward_propagation( X_train) - - -acc,losss,rm=ff.train(X_train,y_train,10,0.01) - - -max_accuracy = acc[0] - -for i in range(1, len(acc)): - if acc[i] > max_accuracy: - max_accuracy = acc[i] - -print("Accuracy:", max_accuracy) -print("Loss:",losss[len(losss)-1]) -#print("\ntrain: ",acc,losss) -#print(ff.predict(X_train)) -predictions = ff.predict(X_test) - -# Print the predictions -print("Predictions:", predictions) -accuracy = ff.accuracy(predictions, y_test) -print("Test Accuracy:", accuracy) - -# Print messages for groundwater quality -for i, prediction in enumerate(predictions): - if prediction == 1: - print(f"Sample {i+1}: Groundwater is harmful.") - else: - print(f"Sample {i+1}: Groundwater is not harmful.") - -# Plotting accuracy -plt.subplot(1, 3, 3) -plt.plot(rm) -plt.ylabel('RMSE value') -plt.xlabel("Epochs:") -plt.show() - -# Plotting Loss -plt.subplot(1, 2, 1) -plt.plot(losss) -plt.title("Loss over Time") -plt.xlabel("Epoch") -plt.ylabel("Loss") - -plt.subplot(1, 2, 2) -plt.plot(acc) -plt.title("Accuracy over Time") -plt.xlabel("Epoch") -plt.ylabel("Accuracy") -plt.show() From d69c3e518d2fe1ba8d4b07a8995580e5fe6f7a9b Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:29:44 +0530 Subject: [PATCH 28/30] Delete Groundwater Arsenic Content Detection/models/app.py --- .../models/app.py | 175 ------------------ 1 file changed, 175 deletions(-) delete mode 100644 Groundwater Arsenic Content Detection/models/app.py diff --git a/Groundwater Arsenic Content Detection/models/app.py b/Groundwater Arsenic Content Detection/models/app.py deleted file mode 100644 index d50610604..000000000 --- a/Groundwater Arsenic Content Detection/models/app.py +++ /dev/null @@ -1,175 +0,0 @@ -import streamlit as st -import pandas as pd -from sklearn.preprocessing import StandardScaler -from sklearn.model_selection import train_test_split -import numpy as np -from sklearn.metrics import mean_squared_error -import matplotlib.pyplot as plt - -# Define the neural network class -class NeuralNetwork: - def __init__(self, input_layer_size, hidden_layer_size, output_layer_size, X): - self.input_layer_size = input_layer_size - self.hidden_layer_size = hidden_layer_size - self.output_layer_size = output_layer_size - - # Initialize the weights with random values - self.W1 = np.random.randn(input_layer_size, hidden_layer_size) - self.W2 = np.random.randn(hidden_layer_size, output_layer_size) - - # Initialize the biases with zeros - self.b1 = np.zeros((1, hidden_layer_size)) - self.b2 = np.zeros((1, output_layer_size)) - - def sigmoid(self, x): - x = np.array(x, dtype=float) - return 1 / (1 + np.exp(-x)) - - def forward_propagation(self, X): - # Calculate the hidden layer activations - self.Z1 = np.dot(X, self.W1) + self.b1 - self.A1 = self.sigmoid(self.Z1) - - # Calculate the output layer activations - self.Z2 = np.dot(self.A1, self.W2) + self.b2 - self.A2 = self.sigmoid(self.Z2) - - return self.A2 - - def backward_propagation(self, X, Y, output, learning_rate): - # Reshape Y to match the shape of output - Y = Y.values.reshape(-1, 1) - - # Calculate the error in the output layer - dZ2 = output - Y - dW2 = np.dot(self.A1.T, dZ2) - db2 = np.sum(dZ2, axis=0, keepdims=True) - - # Calculate the error in the hidden layer - dZ1 = np.dot(dZ2, self.W2.T) * (self.A1 * (1 - self.A1)) - dW1 = np.dot(X.T, dZ1) - db1 = np.sum(dZ1, axis=0, keepdims=True) - self.W1 = self.W1.astype('float64') - dW1 = dW1.astype('float64') - # Update the weights and biases - self.W1 -= learning_rate * dW1 - self.b1 -= learning_rate * db1 - self.W2 -= learning_rate * dW2 - self.b2 -= learning_rate * db2 - - def loss(self, y_pred, y_true): - y_true = y_true.values.reshape(-1, 1) - y_pred_binary = (y_pred >= 0.5).astype(int) - y_true_binary = (y_true >= 0.5).astype(int) - mse = np.mean((y_pred - y_true_binary)**2) - return mse - - def accuracy(self, y_pred, y_true): - y_true = y_true.values.reshape(-1, 1) - y_pred_binary = (y_pred >= 0.5).astype(int) - y_true_binary = (y_true >= 0.5).astype(int) - return (y_pred_binary == y_true_binary).mean() * 100 - - def rmsee(self, y_pred, y_train): - mse = mean_squared_error(y_train, y_pred) - rmse = mean_squared_error(y_train, y_pred, squared=False) - return rmse - - def train(self, X, Y, epoch=10, alpha=0.01): - acc = [] - losss = [] - rm = [] - for j in range(epoch): - out = self.forward_propagation(X) - self.backward_propagation(X, Y, out, alpha) - acc.append(self.accuracy(out, Y)) - losss.append(self.loss(out, Y)) - rm.append(self.rmsee(out, Y)) - - return acc, losss, rm - - def predict(self, X): - # Forward propagation to get the output - output = self.forward_propagation(X) - - # Apply the threshold to classify the output - predictions = (output >= 0.5).astype(int) - - return predictions - -# Streamlit app -st.title("Groundwater Quality Prediction") - -uploaded_file = st.file_uploader("../Ground Water.csv", type="csv") - -if uploaded_file is not None: - data = pd.read_csv(uploaded_file) - - # Data preprocessing - numeric_columns = data.select_dtypes(include='number').columns - data[numeric_columns] = data[numeric_columns].fillna(data[numeric_columns].median()) - - # Select only numeric columns for quantile calculations - numeric_data = data.select_dtypes(include=[np.number]) - data[numeric_data.columns] = numeric_data.clip(lower=numeric_data.quantile(0.01), upper=numeric_data.quantile(0.99), axis=1) - - # Encode categorical variables - data = pd.get_dummies(data) - - # Scale numerical variables - scaler = StandardScaler() - data[data.select_dtypes(include=['float64']).columns] = scaler.fit_transform(data.select_dtypes(include=['float64'])) - - data = data.drop(data.columns[[1, 2]], axis=1) - X = data.iloc[:, :-3] - y = data.iloc[:, -1] - - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0) - - # Define your ANN architecture - input_layer_size = X_train.shape[1] - hidden_layer_size = 10 - output_layer_size = 1 - - # Initialize and train the neural network - nn = NeuralNetwork(input_layer_size, hidden_layer_size, output_layer_size, X_train) - acc, losss, rm = nn.train(X_train, y_train, epoch=10, alpha=0.01) - - # Make predictions - predictions = nn.predict(X_test) - - # Display results - st.write("Predictions:", predictions) - accuracy = nn.accuracy(predictions, y_test) - st.write("Test Accuracy:", accuracy) - - # Print messages for groundwater quality - for i, prediction in enumerate(predictions): - if prediction == 1: - st.write(f"Sample {i+1}: Groundwater is harmful.") - else: - st.write(f"Sample {i+1}: Groundwater is not harmful.") - - # Plotting accuracy - st.subheader("Accuracy over Time") - fig, ax = plt.subplots() - ax.plot(acc) - ax.set_xlabel("Epoch") - ax.set_ylabel("Accuracy") - st.pyplot(fig) - - # Plotting Loss - st.subheader("Loss over Time") - fig, ax = plt.subplots() - ax.plot(losss) - ax.set_xlabel("Epoch") - ax.set_ylabel("Loss") - st.pyplot(fig) - - # Plotting RMSE - st.subheader("RMSE over Time") - fig, ax = plt.subplots() - ax.plot(rm) - ax.set_xlabel("Epoch") - ax.set_ylabel("RMSE value") - st.pyplot(fig) From f5764e7c9b99b5d7ea369df17e49b8ab584b2f6e Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:44:05 +0530 Subject: [PATCH 29/30] Update README.md --- .../README.md | 150 +++++++++++++----- 1 file changed, 114 insertions(+), 36 deletions(-) diff --git a/Groundwater Arsenic Content Detection/README.md b/Groundwater Arsenic Content Detection/README.md index 810acbd6e..64f13bd44 100644 --- a/Groundwater Arsenic Content Detection/README.md +++ b/Groundwater Arsenic Content Detection/README.md @@ -1,48 +1,126 @@ -# Hybridization-Of-ANN-with-metaheuristic-Algorithm-for-predicting-groundwater-quality +Groundwater Arsenic Content Detection +🎯 Goal +To develop an accurate prediction model for arsenic contamination in groundwater using a hybrid approach that combines Artificial Neural Networks (ANN) with the Whale Optimization Algorithm (WOA) and also using third method random forest classifier. This project aims to help water management authorities and public health officials identify potential arsenic contamination risks before they pose a threat to human health. +🧵 Dataset +The dataset contains groundwater quality parameters collected from various locations, including: -## Overview -This project implements a hybrid approach combining Artificial Neural Networks (ANN) with the Whale Optimization Algorithm (WOA) to predict arsenic contamination levels in groundwater.Apart from this random forest is also used. The model aims to provide accurate predictions that can help in water resource management and public health protection. +Arsenic concentration levels +Environmental parameters +Geological factors +Chemical composition indicators -## Problem Statement -Arsenic contamination in groundwater poses severe health risks worldwide. This project addresses the need for accurate prediction methods by: -- Developing an advanced hybrid model combining BPNN and WOA, random forest classifier -- Analyzing environmental factors affecting arsenic levels -- Providing insights for water management decisions +🧾 Description +This project addresses the critical issue of arsenic contamination in groundwater through advanced machine learning techniques. By combining the predictive power of Backpropagation Neural Networks (BPNN) with the optimization capabilities of the Whale Optimization Algorithm (WOA), we create a robust system for predicting arsenic levels in groundwater. This hybrid approach allows for better understanding of the complex relationships between environmental factors and arsenic contamination. +🧮 What I had done! -## Features -- Hybrid model combining BPNN and WOA -- Environmental factor analysis -- Data preprocessing pipeline -- Model performance comparison -- Variable importance evaluation +Data Collection and Preprocessing +Gathered groundwater quality parameters from various locations +Performed data cleaning and handled missing values +Normalized the data for better model performance +Split the dataset into training and testing sets -## Dataset -The dataset includes: -- Arsenic concentration levels -- Environmental parameters -- Geological factors -- Location data -Data is preprocessed and split into training and testing sets for model evaluation. +Model Development -## Model Architecture +Implemented BPNN architecture +Integrated WOA for neural network optimization +Fine-tuned hyperparameters for both algorithms +Implemented random forest classifier -### Backpropagation Neural Network (BPNN) -- Multi-layer perceptron architecture -- Supervised learning approach -- Optimized for regression task -### Whale Optimization Algorithm (WOA) -- Nature-inspired optimization algorithm -- Used for optimizing BPNN weights and biases -- Enhanced exploration and exploitation capabilities +Model Evaluation +Conducted performance analysis using multiple metrics +Compared BPNN and WOA-optimized results +Analyzed feature importance -## Results -The project evaluates: -- Prediction accuracy metrics -- Model comparison results -- Variable importance analysis -- Environmental factor impacts +Visualization and Reporting + +Created visualizations for data analysis +Generated performance comparison charts +Documented findings and recommendations + + + +🚀 Models Implemented + +Backpropagation Neural Network (BPNN) + +Chosen for its ability to learn complex patterns in data +Effective for regression problems +Capable of handling multiple input parameters + + +Whale Optimization Algorithm (WOA) + +Selected for its proven optimization capabilities +Helps avoid local optima problems +Efficient in optimizing neural network weights and biases + + +Hybrid BPNN-WOA Model + +Combines the strengths of both algorithms +Improves prediction accuracy +Reduces overfitting risks + +Random Forest Classifier +Another algorith with good accuracy to check the groundwater quality. + +📚 Libraries Needed + +TensorFlow/Keras (Neural Network Implementation) +NumPy (Numerical Computations) +Pandas (Data Manipulation) +Scikit-learn (Model Evaluation) +Matplotlib (Visualization) +SciPy (Scientific Computing) + +📊 Exploratory Data Analysis Results +![RMSE Graph](https://github.com/user-attachments/assets/00e3cecb-af37-428b-b00d-6d6b4c3e5b19) +![Loss and accuracy](https://github.com/user-attachments/assets/ffc4b37e-f58d-4d20-b1a9-b0a213514d61) + + +Correlation matrix of features +Distribution of arsenic levels +Geographical distribution of sampling points +Feature importance plots +Model performance comparisons + +📈 Performance of the Models based on the Accuracy Scores + +ANN Model: + +Mean Squared Error (MSE): 0.14 +Accuracy:72.97 +Root Mean Squared Error (RMSE): 0.38 + + +WOA-Optimized ANN: + +Mean Squared Error (MSE): 0.12 +Accuracy: 83.78 +Root Mean Squared Error (RMSE): 0.32 + +Rainforest Classifier: + +Mean Squared Error (MSE): 0.20 +Root Mean Squared Error (RMSE): 0.50 + + +📢 Conclusion +The hybrid BPNN-WOA approach demonstrates superior performance in predicting groundwater arsenic levels compared to traditional methods. Key findings include: + +Improved prediction accuracy by 13% using the hybrid approach +Identification of key environmental factors influencing arsenic levels +Potential for real-world application in water quality monitoring +Recommendation for implementation in groundwater management systems + +✒️ Your Signature +Stuti Sharma + +GitHub: https://github.com/Stuti333 +LinkedIn: https://www.linkedin.com/in/stuti-sharma-94057122b/ +Email: stutiemailbox@gmail.com From 66cdf3d8fcde5224b63e0d8808ac85d16afd37cf Mon Sep 17 00:00:00 2001 From: Stuti Sharma <71967787+Stuti333@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:44:46 +0530 Subject: [PATCH 30/30] Update README.md --- Groundwater Arsenic Content Detection/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Groundwater Arsenic Content Detection/README.md b/Groundwater Arsenic Content Detection/README.md index 64f13bd44..095d5e710 100644 --- a/Groundwater Arsenic Content Detection/README.md +++ b/Groundwater Arsenic Content Detection/README.md @@ -1,6 +1,8 @@ Groundwater Arsenic Content Detection + 🎯 Goal To develop an accurate prediction model for arsenic contamination in groundwater using a hybrid approach that combines Artificial Neural Networks (ANN) with the Whale Optimization Algorithm (WOA) and also using third method random forest classifier. This project aims to help water management authorities and public health officials identify potential arsenic contamination risks before they pose a threat to human health. + 🧵 Dataset The dataset contains groundwater quality parameters collected from various locations, including: @@ -11,6 +13,7 @@ Chemical composition indicators 🧾 Description This project addresses the critical issue of arsenic contamination in groundwater through advanced machine learning techniques. By combining the predictive power of Backpropagation Neural Networks (BPNN) with the optimization capabilities of the Whale Optimization Algorithm (WOA), we create a robust system for predicting arsenic levels in groundwater. This hybrid approach allows for better understanding of the complex relationships between environmental factors and arsenic contamination. + 🧮 What I had done! Data Collection and Preprocessing @@ -122,5 +125,7 @@ Recommendation for implementation in groundwater management systems Stuti Sharma GitHub: https://github.com/Stuti333 + LinkedIn: https://www.linkedin.com/in/stuti-sharma-94057122b/ + Email: stutiemailbox@gmail.com