Integrarea prognozei Python cu Ridyverse R de la R

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

(Acest articol a fost publicat pentru prima dată pe DateAgeeekși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)


Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.

În acest articol, am executat o integrare cu succes a unui model de prognoză Python non-standard în R în R Tidyverse/Tidymodels cadru, în primul rând folosind reticulate pachet.

1. Obiectivul (provocarea)

Scopul a fost utilizarea unui model Python puternic, personalizat (nnetsauceînvelișul MTS în jurul unui cyb$BoosterRegressor) și integrează -i rezultatele – predicții și intervale de predicție – în ecosistemul R, permițând o manipulare constantă a datelor cu dplyr și vizualizare de înaltă calitate folosind ggplot2 (și interactiv plotly)

Provocarea a fost că acest model Python personalizat nu este recunoscut în mod nativ de pachetele standard de prognoză ale R modeltime.

2. Mecanismul de integrare

Integrarea a fost obținută prin următorii pași:

  • reticulate Pod: Am folosit reticulate Pachet pentru a apela perfect la modelul Python, trece datele r (tbl_train), potriviți modelul (regr$fit()) și execută prognoza (regr$predict())
  • Conversia datelor: Produsul de prognoză brută (predicții și intervale de încredere) de la Python a fost extrasă cu atenție și transformată într -un r standard tibble (forecast_data_tbl)
  • Simularea Tidymodels (încercare inițială): Am încercat să forțăm acest tibble într -un modeltime Structura prin utilizarea modelelor de manechin și calibrare, care au eșuat din cauza verificărilor de validare internă rigidă plot_modeltime_forecast().
  • Soluție finală Tidyverse: Am abandonat rigidul modeltime Funcții de vizualizare și a adoptat abordarea principală Tidyverse:
    1. Manipularea datelor: Am folosit dplyr şi tidyr::pivot_longer() pentru a restructura datele prognozate în Format lungcare este optim pentru ggplot2.
    2. Vizualizare: Am folosit pur ggplot2 şi plotly Pentru a desena manual liniile, panglici și pentru a aplica estetica STOXX dorită, ocolind cu succes toate erorile de incompatibilitate a pachetului.

3. Constatări cheie

Procesul a evidențiat o constatare statistică critică:

  • Metric vs. discrepanță vizuală: Calitatea prognozei punctului Python Model a fost excelentă (scăzută MAPE), dar calculul său intern al intervalului de predicție (type_pi="gaussian") a fost grav defectuos (ridicat Eroare în afara benzii)
  • Soluţie: Pentru a ne asigura că vizualizarea a reflectat scorul MAPE scăzut, am calculat adevăratul MAPE pe setul de testare și am generat manual un Interval de încredere realist, bazat pe MAPE Pentru a fi utilizat în complotul final.

În esență, am creat un robust Conductă centrată R. Pentru post-procesare, validare și vizualizare a prognozelor generate de un model personalizat de învățare a mașinilor Python.

library(tidyverse)
library(tidyquant)
library(reticulate)
library(tidymodels)
library(timetk)
library(modeltime)
library(plotly)

# nnetsauce_env ortamını kullanmasını söyleyin
use_python("C:/Users/selcu/AppData/Local/r-miniconda/envs/nnetsauce_env/python.exe", required = TRUE) 


# Import Python packages
np <- import("numpy")
ns <- import("nnetsauce")
cyb <- import("cybooster")
sklearn <- import("sklearn")


#iShares US Aerospace & Defense ETF (ITA)
df_ita <- 
  tq_get("ITA") %>% 
  select(date, close) %>% 
  filter(date >= last(date) - months(36))

#Split Data 
split <- time_series_split(df_ita,
                           assess = "15 days",
                           cumulative = TRUE)


tbl_train <- training(split)
tbl_test <- testing(split)

# 1. import DecisionTreeRegressor from sklearn
skl_tree <- import("sklearn.tree")

# Define the Base Estimator (The core learning component, factored for clarity)
# This uses a Decision Tree with a max depth of 3 as the weak learner for the boosting model.
base_estimator_instance <- skl_tree$DecisionTreeRegressor(max_depth = 3L)

# Define the Booster Object (The main regression engine)
# cyb$BoosterRegressor is the ensemble model that will be fitted.
booster_regressor <- cyb$BoosterRegressor(
  base_estimator_instance, # The weak learner used for boosting
  n_estimators = 100L        # Number of boosting stages (number of trees to build)
)

# Define the MTS (Multi-variate Time Series) Model Wrapper
# ns$MTS wraps the booster model to handle time series features like lags and prediction intervals.
regr <- ns$MTS(
  obj = booster_regressor, # The fitted regression model (BoosterRegressor)
  
  # Time Series Parameters:
  lags = 20L,              # Number of past observations (lags) to use as predictors (X)
  type_pi = "gaussian",    # Method for computing Prediction Intervals (PI). 
  # 'gaussian' assumes errors are normally distributed.
  # (NOTE: We found this to be too narrow, requiring manual adjustment with MAPE.)
  
  # Execution Control:
  show_progress = TRUE     # Displays the training progress (useful for monitoring)
)


#Converting the tibble to data frame for the fitting process
df_train <- 
  tbl_train %>% 
  as.data.frame() %>% 
  mutate(date = as.character(date))

df <- df_train(, -1, drop = FALSE)
rownames(df) <- df_train$date


#Fit the model
regr$fit(df)

#Step 1: Extract Python Predictions and Create the Base R Data Table

# --- Define Python Function and Retrieve Predictions ---
# (Ensure this function has been run in your R session previously.)
reticulate::py_run_string("
def get_full_data_11(preds_obj):
    mean_list = preds_obj(0)('close').tolist() 
    lower_list = preds_obj(1)('close').tolist()
    upper_list = preds_obj(2)('close').tolist()
    return {'mean': mean_list, 'lower': lower_list, 'upper': upper_list}
")

preds_raw <- regr$predict(h = 11L)
full_data_list <- py$get_full_data_11(preds_raw)

preds_mean_vector  <- unlist(full_data_list$mean)
preds_lower_vector <- unlist(full_data_list$lower)
preds_upper_vector <- unlist(full_data_list$upper)

# Align data sets based on prediction length
vector_length <- length(preds_mean_vector)
df_test_final <- tbl_test %>% dplyr::slice(1:vector_length) 

# --- Create the Base Forecast Table ---
forecast_data_tbl <- tibble(
  .index = df_test_final$date,
  .value = df_test_final$close,
  .prediction = preds_mean_vector,
  .conf_lo    = preds_lower_vector, # These are the initially incorrect (too narrow) CIs
  .conf_hi    = preds_upper_vector, # These are the initially incorrect (too narrow) CIs
    .model_desc = "nnetsauce_BoosterRegressor"
)

#Step 2: Calculate the Actual MAPE and Create Realistic Confidence Intervals
library(dplyr)
library(tibble)
library(tidyr) 

# --- Step 2.1: Calculate the Actual MAPE on the Test Set ---

# Create a verification table containing error metrics
check_tbl <- forecast_data_tbl %>%
  mutate(
    Error = .value - .prediction,
    # MAPE formula: abs(Actual - Prediction) / Actual * 100
    Absolute_Percentage_Error = abs(Error) / .value * 100 
  )

# Actual MAPE (Calculated from the Test Set)
actual_mape_test <- mean(check_tbl$Absolute_Percentage_Error, na.rm = TRUE) / 100 
# (Converting the percentage to decimal format: e.g., 2.84% -> 0.0284)

print(paste0("Calculated MAPE (decimal format): ", actual_mape_test))


# --- Step 2.2: Create New, Realistic Intervals Based on MAPE ---

Z_SCORE_95 <- 1.96 # Z-Score for 95% Confidence Interval

# Create the New Realistic Prediction Interval Table
forecast_data_tbl_realistic <- check_tbl %>%
  mutate(
    # Error Margin = Prediction * MAPE * Z-Score
    Error_Amount = .prediction * actual_mape_test * Z_SCORE_95,
    
    # New Confidence Intervals
    .conf_lo_new = .prediction - Error_Amount, 
    .conf_hi_new = .prediction + Error_Amount
  ) %>%
  # Rename/replace columns to use the new, wider intervals
  mutate(
    .conf_lo = .conf_lo_new,
    .conf_hi = .conf_hi_new
  ) %>%
  select(-c(Error_Amount, .conf_lo_new, .conf_hi_new, Error, Absolute_Percentage_Error))

#Visualize Confidence Intervals

# Visualization Settings
min_y <- min(forecast_data_tbl_realistic$.conf_lo) * 0.98 
max_y <- max(forecast_data_tbl_realistic$.conf_hi) * 1.02 
mape_value_for_title <- round(actual_mape_test * 100, 2)
COLOR_PREDICTION <- "red" # Dark Grey/Blue (Prediction)
COLOR_ACTUAL <- "black" # Red (Actual Value)
COLOR_RIBBON <- "dimgrey" 

# Pivot the Base Table to Long Format (required for ggplot)
forecast_tbl_long_final <- forecast_data_tbl_realistic %>%
  pivot_longer(cols = c(.value, .prediction), 
               names_to = ".key", 
               values_to = "Y_Value") %>%
  mutate(
    .conf_lo_plot = ifelse(.key == ".prediction", .conf_lo, NA_real_),
    .conf_hi_plot = ifelse(.key == ".prediction", .conf_hi, NA_real_)
  )

# Create the ggplot Object
p <- ggplot(forecast_tbl_long_final, aes(x = .index, y = Y_Value, color = .key)) +
  
  # Draw the Confidence Interval (Ribbon)
  geom_ribbon(aes(ymin = .conf_lo_plot, ymax = .conf_hi_plot),
              fill = COLOR_RIBBON, 
              alpha = 0.6, 
              color = NA,
              data = forecast_tbl_long_final %>% filter(.key == ".prediction")) +
  
  # Draw the Lines
  geom_line(linewidth = 2.0) +
  
  # Y-Axis Zoom
  coord_cartesian(ylim = c(min_y, max_y)) + 
  
  # Colors and Titles
  labs(title = "iShares US Aerospace & Defense ETF",
       subtitle = paste0("Predictive Intervals based on TEST SET MAPE: ", mape_value_for_title, "% 
nnetsauce-Wrapped Booster Regressor (MTS) Model"), x = "", y = "", color = "") + scale_color_manual(values = c(".value" = COLOR_ACTUAL, ".prediction" = COLOR_PREDICTION), labels = c(".value" = "Actual Value", ".prediction" = "Prediction")) + scale_y_continuous(labels = scales::label_currency()) + scale_x_date(labels = scales::label_date("%b %d"), date_breaks = "2 days") + theme_minimal(base_family = "Roboto Slab", base_size = 16) + theme(plot.title = element_text(face = "bold", size = 16), plot.subtitle = ggtext::element_markdown(face = "bold"), plot.background = element_rect(fill = "azure", color = "azure"), panel.background = element_rect(fill = "snow", color = "snow"), axis.text = element_text(face = "bold"), axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1), legend.position = "none") # Interactive Output plotly::ggplotly(p)

Notă: Acest articol a fost generat cu asistența unui model AI. Conținutul și structura finală au fost revizuite și aprobate de autor.

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.