Python data visualization and calculation of error matrices

I am working on load modeling using the KAN model. The following code I am using for KAN model and the issues turn up as explained below:

=============== Step 1: Import Libraries ========================

import torch
import random
import numpy as np
import os
import pandas as pd
from sklearn.preprocessing import StandardScaler
from kan import *
import matplotlib.pyplot as plt
from kan.utils import ex_round
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = “all”

============== Step 2: Set Global Seed First =====================================

def set_global_seed(seed=42):
os.environ[‘PYTHONHASHSEED’] = str(seed)
torch.manual_seed(seed)
np.random.seed(seed)
random.seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

set_global_seed(42)
def save_kan_plot(model, title, filename_prefix):
model.plot()
fig = plt.gcf()
fig.suptitle(title, fontsize=14)
fig.savefig(f"{filename_prefix}.png", dpi=600, bbox_inches=‘tight’)
fig.savefig(f"{filename_prefix}.pdf", bbox_inches=‘tight’)
plt.close(fig)

=== STEP 2: Set precision & device ===

torch.set_default_dtype(torch.float64)

device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)

============= Step 3:Loading the dataset======================================

features = [‘Bus Bus 9 V pu’, ‘Bus Bus 9 Frequency in PU’]

target = [‘Bus Bus 9 Load MW’, ‘Bus Bus 9 Load Mvar’]

Loading training data (3-Phase fault)

train_df = pd.read_csv(“CLM_3Phase_Fault_B9.csv”)

train_df.columns = [col.strip() for col in train_df.columns]

X_train = train_df[features].values

y_train = train_df[target].values

print(“y_train columns:”, train_df[target].columns)

Load test data (SLG Fault)

test_slg_df = pd.read_csv(“CLM_SLG_Fault_B9.csv”)

test_slg_df.columns = [col.strip() for col in test_slg_df.columns]

X_test_slg = test_slg_df[features].values

y_test_slg = test_slg_df[target].values

Load Test Data (DLG Fault)

test_dlg_df = pd.read_csv(“CLM_DLG_Fault_B9.csv”)

test_dlg_df.columns = [col.strip() for col in test_dlg_df.columns]

X_test_dlg = test_dlg_df[features].values

y_test_dlg = test_dlg_df[target].values

=============== Step 4: Scaling ====================================================

scaler_x = StandardScaler()

scaler_y = StandardScaler()

Fit should be done only on training

X_train_scaled = scaler_x.fit_transform(X_train)

y_train_scaled = scaler_y.fit_transform(y_train)

Transform test using training scalers

X_test_slg_scaled = scaler_x.transform(X_test_slg)

y_test_slg_scaled = scaler_y.transform(y_test_slg)

X_test_dlg_scaled = scaler_x.transform(X_test_dlg)

y_test_dlg_scaled = scaler_y.transform(y_test_dlg)

============== Step 5: Convert to torch tensors====================

X_train_tensor = torch.tensor(X_train_scaled, dtype=torch.float64)

y_train_tensor = torch.tensor(y_train_scaled, dtype=torch.float64)

X_test_slg_tensor = torch.tensor(X_test_slg_scaled, dtype=torch.float64)

y_test_slg_tensor = torch.tensor(y_test_slg_scaled, dtype=torch.float64)

X_test_dlg_tensor = torch.tensor(X_test_dlg_scaled, dtype=torch.float64)

y_test_dlg_tensor = torch.tensor(y_test_dlg_scaled, dtype=torch.float64)

print(“Input tensor shape:”, X_train_tensor.shape) # Should be (N, 2)

===========Step 6: Create the KAN model ===========================================

model = KAN(width=[2, 5, 2], grid=5, k=3, seed=0, device=device)

Forward pass to initialize internal structure (spline layers)

model(X_train_tensor)

Save structure at initialization

save_kan_plot(model, “KAN Structure at Initialization”, “kan_init”)

Create dataset dic for KAN

dataset = {

‘train_input’: X_train_tensor,

‘train_label’: y_train_tensor,

‘test_input’: X_test_slg_tensor,

‘test_label’: y_test_slg_tensor

}

============== Step 7: Train the model ====================================

model.fit(dataset, opt=“LBFGS”, steps=100, lamb=0.001, lamb_entropy =0.0, lamb_coefdiff=1e-3)

Save structure after training

save_kan_plot(model, “KAN Structure After Training”, “kan_trained”)

Predict on training data

y_train_pred_scaled = model(dataset[‘train_input’]).detach().cpu().numpy()

Predict on SLG testing data

y_test_pred_scaled = model(dataset[‘test_input’]).detach().cpu().numpy()

Predict on DLG test data

y_pred_dlg_scaled = model(X_test_dlg_tensor).detach().cpu().numpy()

Inverse transform predictions and true values (MW)

y_train_true = scaler_y.inverse_transform(dataset[‘train_label’].detach().cpu().numpy())

y_train_pred = scaler_y.inverse_transform(y_train_pred_scaled)

y_test_true = scaler_y.inverse_transform(dataset[‘test_label’].detach().cpu().numpy())

y_test_pred = scaler_y.inverse_transform(y_test_pred_scaled)

y_true_dlg = scaler_y.inverse_transform(y_test_dlg_tensor.detach().cpu().numpy())

y_pred_dlg = scaler_y.inverse_transform(y_pred_dlg_scaled)

P_train_true, Q_train_true = y_train_true[:, 0], y_train_true[:, 1]

P_train_pred, Q_train_pred = y_train_pred[:, 0], y_train_pred[:, 1]

P_test_true, Q_test_true = y_test_true[:, 0], y_test_true[:, 1]

P_test_pred, Q_test_pred = y_test_pred[:, 0], y_test_pred[:, 1]

P_dlg_true, Q_dlg_true = y_true_dlg[:, 0], y_true_dlg[:, 1]

P_dlg_pred, Q_dlg_pred = y_pred_dlg[:, 0], y_pred_dlg[:, 1]

================== Split active (MW) and Reactive (MVAr) components =============================

P_train_true, Q_train_true = y_train_true[:, 0], y_train_true[:, 1]

P_train_pred, Q_train_pred = y_train_pred[:, 0], y_train_pred[:, 1]

P_test_true, Q_test_true = y_test_true[:, 0], y_test_true[:, 1]

P_test_pred, Q_test_pred = y_test_pred[:, 0], y_test_pred[:, 1]

P_dlg_true, Q_dlg_true = y_true_dlg[:, 0], y_true_dlg[:, 1]

P_dlg_pred, Q_dlg_pred = y_pred_dlg[:, 0], y_pred_dlg[:, 1]

=========== Step 11: Plotting the actual vs. predicted power ============================

Generate time axis from 0 to 10 seconds based on number of test samples

time_axis_train = np.linspace(0, 10, num=len(y_train_true))
time_axis_test = np.linspace(0, 10, num=len(y_test_true))
time_axis_dlg = np.linspace(0, 10, num=len(y_true_dlg))

def plot_signal(time, true_vals, pred_vals, signal_type, title, filename):
plt.figure(figsize=(10, 4))
if signal_type == “Active”:
ylabel = “Active Power (MW)”
true_label = “Actual Active Power”
pred_label = “Predicted Active Power”
elif signal_type == “Reactive”:
ylabel = “Reactive Power (MVAr)”
true_label = “Actual Reactive Power”
pred_label = “Predicted Reactive Power”
plt.plot(time, true_vals, label=true_label, linewidth=2)
plt.plot(time, pred_vals, label=“Predicted Active Power”, linestyle=‘–’, color=‘red’)
plt.title(title)
plt.xlabel(“Time (s)”)
plt.ylabel(ylabel)
plt.legend()
plt.grid(True)
plt.xlim(0, 10) # Ensure time axis ends at 10
plt.tight_layout()
os.makedirs(“plots”, exist_ok=True)
plt.savefig(f"plots/{filename}.png", dpi=600)
plt.savefig(f"plots/{filename}.pdf", bbox_inches=‘tight’)
plt.show()
plt.close()

print(f"Saved: plots/{filename}.png/.pdf")

=======Training set ========

plot_signal(time_axis_train, P_train_true, P_train_pred,
signal_type=“Active”,
title=“Training Set: Active Power”, filename=“train_active”
)
plot_signal(time_axis_train, Q_train_true, Q_train_pred,
signal_type = “Reactive”,
title=“Training Set: Reactive Power”, filename=“train_reactive”
)

=========SLG Fault ==============================

plot_signal(time_axis_test, P_test_true, P_test_pred,
signal_type=“Active”,
title=“Test Set (SLG Fault): Active Power”, filename=“slg_active”
)

plot_signal(time_axis_test, Q_test_true, Q_test_pred,
signal_type =“Reactive”,
title=“Test Set (SLG Fault): Reactive Power”, filename=“slg_reactive”
)

================DLG Fault =========================

plot_signal(time_axis_dlg, P_dlg_true, P_dlg_pred,
signal_type = “Active”,
title=“Test Set (DLG Fault): Active Power”, filename=“dlg_active”
)
plot_signal(time_axis_dlg, Q_dlg_true, Q_dlg_pred,
signal_type = “Reactive”,
title=“Test Set (DLG Fault): Active Power”, filename=“dlg_reactive”
)

=============== Step 12: Calaculation of Error Metrices ============================

print(“\n=== Normalized Error Metrics: Train 3Phase Fault ===”)

Get predicted and true (normalized) values from tensors

y_train_pred_norm = model(X_train_tensor).detach().cpu().numpy()
y_train_true_norm = y_train_tensor.detach().cpu().numpy()

Compute normalized error for each output

for i, label in enumerate([“Active Power (MW)”, “Reactive Power (MVAr)”]):
mse = mean_squared_error(y_train_true_norm[:, i], y_train_pred_norm[:, i])
mae = mean_absolute_error(y_train_true_norm[:, i], y_train_pred_norm[:, i])
rmse = np.sqrt(mse)
r2 = r2_score(y_train_true_norm[:, i], y_train_pred_norm[:, i])

print(f"\n{label} (Normalized):")
print(f"  MSE  = {mse:.4e}")
print(f"  MAE  = {mae:.4e}")
print(f"  RMSE = {rmse:.4e}")
print(f"  R²   = {r2:.6f}")

# ================= SLG Normalized Error ======================

print(“\n=== Normalized Error Metrics: Test SLG Fault ===”)

y_slg_pred_norm = model(X_test_slg_tensor).detach().cpu().numpy()
y_slg_true_norm = y_test_slg_tensor.detach().cpu().numpy()

for i, label in enumerate([“Active Power (MW)”, “Reactive Power (MVAr)”]):
mse = mean_squared_error(y_slg_true_norm[:, i], y_slg_pred_norm[:, i])
mae = mean_absolute_error(y_slg_true_norm[:, i], y_slg_pred_norm[:, i])
rmse = np.sqrt(mse)
r2 = r2_score(y_slg_true_norm[:, i], y_slg_pred_norm[:, i])

print(f"\n{label} (Normalized):")
print(f"  MSE  = {mse:.4e}")
print(f"  MAE  = {mae:.4e}")
print(f"  RMSE = {rmse:.4e}")
print(f"  R²   = {r2:.6f}")

================= DLG Normalized Error ======================

print(“\n=== Normalized Error Metrics: Test DLG Fault ===”)

y_dlg_pred_norm = model(X_test_dlg_tensor).detach().cpu().numpy()
y_dlg_true_norm = y_test_dlg_tensor.detach().cpu().numpy()

for i, label in enumerate([“Active Power (MW)”, “Reactive Power (MVAr)”]):
mse = mean_squared_error(y_dlg_true_norm[:, i], y_dlg_pred_norm[:, i])
mae = mean_absolute_error(y_dlg_true_norm[:, i], y_dlg_pred_norm[:, i])
rmse = np.sqrt(mse)
r2 = r2_score(y_dlg_true_norm[:, i], y_dlg_pred_norm[:, i])

print(f"\n{label} (Normalized):")
print(f"  MSE  = {mse:.4e}")
print(f"  MAE  = {mae:.4e}")
print(f"  RMSE = {rmse:.4e}")
print(f"  R²   = {r2:.6f}")

With this script i get the plot as follows:

the error metrics as shown below:
=== Normalized Error Metrics: Train 3Phase Fault ===

Active Power (MW) (Normalized):
MSE = 6.0369e-03
MAE = 1.7239e-02
RMSE = 7.7697e-02
R² = 0.993963

Reactive Power (MVAr) (Normalized):
MSE = 4.8168e-03
MAE = 2.6176e-02
RMSE = 6.9403e-02
R² = 0.995183

=== Normalized Error Metrics: Test SLG Fault ===

Active Power (MW) (Normalized):
MSE = 1.5824e+00
MAE = 2.9090e-01
RMSE = 1.2579e+00
R² = -3658.657584

Reactive Power (MVAr) (Normalized):
MSE = 5.4836e-03
MAE = 1.8555e-02
RMSE = 7.4051e-02
R² = -0.461169

=== Normalized Error Metrics: Test DLG Fault ===

Active Power (MW) (Normalized):
MSE = 6.6191e-05
MAE = 7.2102e-03
RMSE = 8.1358e-03
R² = -740309.050111

Reactive Power (MVAr) (Normalized):
MSE = 2.4674e-06
MAE = 1.4737e-03
RMSE = 1.5708e-03
R² = -7730.426834
In this case, error metrics on training is excellent but when it comes to testing at it is huge error and R2 is negative. is there any error in my code. Help me in debugging my script.

Thank you