Usage Example

import numpy as np 
import pandas as pd 
import torch
from anglepy import ANGLE
from anglepy.metrics import mean_absolute_angular_deviation, circular_mean_directional_error, mean_circular_crps, accuracy, med_err
from anglepy.kernels import c2_wendland
from sklearn.model_selection import train_test_split
from tqdm.auto import tqdm
import math

1. Indian Wind Dataset - Prediction

df = pd.read_csv('India_Wind_Data.csv')
random_state=18
device= torch.device("cuda" if torch.cuda.is_available() else "cpu")
X, y = df[['Latitude', 'Longitude']], df['Direction_Rad']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, shuffle=True, random_state=random_state)
X_train, X_test, y_train, y_test = torch.from_numpy(X_train.values).float().to(device), torch.from_numpy(X_test.values).float().to(device), torch.from_numpy(y_train.values).float().to(device), torch.from_numpy(y_test.values).float().to(device)
y_train = y_train.unsqueeze(-1)
# ANGLE-Geodesic

accuracies, mederrs, crpss, cmdes, maads, times = [], [], [], [], [], []

for _ in tqdm(range(50)):
    model = ANGLE(
        X_train, y_train, 
        num_layer=3, hidden_dim=100, noise_dim=64, 
            add_bn=True, resblock=False, noise_std=1,
            print_every_nepoch=10000, print_times_per_epoch=0, 
            lr=0.05, num_epochs=500, batch_size=None, 
            standardize=False, device=device,
            dist_method='geodesic', verbose=False, circular_indices=None,
            kernel_func=c2_wendland(), circular_projection='atan2'
    )
    
    y_pred = model.predict(X_test)
    y_pred_ensemble = model.sample(X_test, sample_size=100)
    maad_mean = mean_absolute_angular_deviation(y_pred, y_test, return_degrees=True).item()
    cmde = circular_mean_directional_error(y_pred, y_test).item()
    crps = mean_circular_crps(y_pred_ensemble, y_test, return_degrees=True)[0]
    acc = accuracy(y_pred, y_test, theta=torch.pi/6)
    median_err = med_err(y_pred, y_test)
    accuracies.append(round(acc,3))
    mederrs.append(round(median_err,3))
    crpss.append(round(crps, 3))
    maads.append(round(maad_mean, 3))
    cmdes.append(round(cmde,3))
print(f"{np.mean(accuracies):.2f}\pm{np.std(accuracies):.2f}")
print(f"{np.mean(mederrs):.2f}\pm{np.std(mederrs):.2f}")
print(f"{np.mean(crpss):.2f}\pm{np.std(crpss):.2f}")
print(f"{np.mean(maads):.2f}\pm{np.std(maads):.2f}")
print(f"{np.mean(cmdes):.2f}\pm{np.std(cmdes):.2f}")
# ANGLE-Chordal
accuracies, mederrs, crpss, cmdes, maads, times = [], [], [], [], [], []

for _ in tqdm(range(50)):
    model = ANGLE(
        X_train, y_train, 
        num_layer=3, hidden_dim=100, noise_dim=64, 
            add_bn=True, resblock=False, gamma=1, noise_std=1,
            print_every_nepoch=10000, print_times_per_epoch=0, 
            lr=0.05, num_epochs=800, batch_size=None, 
            standardize=False, device=device,
            dist_method='chordal', verbose=False, circular_indices=None
    )
    
    y_pred = model.predict(X_test)
    y_pred_ensemble = model.sample(X_test, sample_size=100)
    maad_mean = mean_absolute_angular_deviation(y_pred, y_test, return_degrees=True).item()
    cmde = circular_mean_directional_error(y_pred, y_test).item()
    crps = mean_circular_crps(y_pred_ensemble, y_test, return_degrees=True)[0]
    acc = accuracy(y_pred, y_test, theta=torch.pi/6)
    median_err = med_err(y_pred, y_test)
    accuracies.append(round(acc,3))
    mederrs.append(round(median_err,3))
    crpss.append(round(crps, 3))
    maads.append(round(maad_mean, 3))
    cmdes.append(round(cmde,3))

print(f"{np.mean(accuracies):.2f}\pm{np.std(accuracies):.2f}")
print(f"{np.mean(mederrs):.2f}\pm{np.std(mederrs):.2f}")
print(f"{np.mean(crpss):.2f}\pm{np.std(crpss):.2f}")
print(f"{np.mean(maads):.2f}\pm{np.std(maads):.2f}")
print(f"{np.mean(cmdes):.2f}\pm{np.std(cmdes):.2f}")

2. Indian Wind Dataset - Sufficient Dimension Reduction

df = pd.read_csv('India_Wind_Data.csv')
random_state=18
device= torch.device("cuda" if torch.cuda.is_available() else "cpu")
X, y = df[['Latitude', 'Longitude']], df['Direction_Rad']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, shuffle=True, random_state=random_state)
X_train, X_test, y_train, y_test = torch.from_numpy(X_train.values).float().to(device), torch.from_numpy(X_test.values).float().to(device), torch.from_numpy(y_train.values).float().to(device), torch.from_numpy(y_test.values).float().to(device)
y_train = y_train.unsqueeze(-1)
model = ANGLE(
        X_train, y_train, 
        num_layer=3, hidden_dim=100, noise_dim=64, 
            add_bn=True, resblock=False, gamma=1, noise_std=1,
            print_every_nepoch=10000, print_times_per_epoch=0, 
            lr=0.05, num_epochs=800, batch_size=None, 
            standardize=True, device=device,
            dist_method='chordal', verbose=False, circular_indices=None, sdr=True, reduced_dim=1
    )
beta_matrix = model.beta_proj
print(beta_matrix.shape) 
# Output: (1, d)

# Project your data to 1D for fitting stage-2 ANGLE, or plotting
reduced_X = X_train @ beta_matrix.T