Question 1: Importing and preparing the data

# Set seed for reproducibility 
set.seed(123)
# a) Import Electric Production.csv and convert DATE to Date class
data <- read.csv("Electric_Production.csv",
                 stringsAsFactors = FALSE)
#Convert DATE column  to class Date
data$DATE <- as.Date(data$DATE, format = "%m/%d/%Y")
# Display the first six rows and the structure of the data.
head(df ,6)
##                                               
## 1 function (x, df1, df2, ncp, log = FALSE)    
## 2 {                                           
## 3     if (missing(ncp))                       
## 4         .Call(C_df, x, df1, df2, log)       
## 5     else .Call(C_dnf, x, df1, df2, ncp, log)
## 6 }
str(df)
## function (x, df1, df2, ncp, log = FALSE)
# b) Check for missing values, duplicated dates, correct ordering, and regular spacing
cat("Missing values :", sum(is.na(data)), "\n")
## Missing values : 0
cat("Duplicated dates :", sum(duplicated(data$DATE)), "\n")
## Duplicated dates : 0
cat("Correct ordering :", all(diff(as.numeric(data$DATE)) > 0), "\n")
## Correct ordering : TRUE
# Check regular monthly spacing 
lt_dates <- as.POSIXlt(data$DATE)
years <- lt_dates$year + 1900
months <- lt_dates$mon + 1
month_index <- years * 12 + months

cat("Regular monthly spacing :", all(diff(month_index) == 1), "\n")
## Regular monthly spacing : TRUE
# c) Convert the series into a ts object (frequency = 12 for monthly data)
electric_ts <- ts(data$Production,
                  start = c(1985, 1),
                  frequency = 12)

print("First 10 Observations using window() ")
## [1] "First 10 Observations using window() "
window(electric_ts, end = c(1985, 10))
##          Jan     Feb     Mar     Apr     May     Jun     Jul     Aug     Sep
## 1985 72.5052 70.6720 62.4502 57.4714 55.3151 58.0904 62.6202 63.2485 60.5846
##          Oct
## 1985 56.3154
##Start (c(1985, 1)): The series begins in January 1985, designated as year 1985, month 1.
#Frequency (12): The data is collected monthly, so 12 observations complete one annual cycle.

Question 2: Time plots

# a) A time plot of the series using ggplot2
library(ggplot2)

p_time <- ggplot(data, aes(x = DATE, y = Production)) +
  geom_line(color = "darkblue", linewidth = 0.8) +
  geom_point(color = "skyblue", size = 1.0) +
  labs(title = "Time Plot of Electric Production",
       subtitle = "Monthly observations from January 1985 to January 2018",
       x = "Date", 
       y = "Production Index") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        axis.title = element_text(face = "bold"))

# Print/display the plot
print(p_time)

#b)Describe the siries
#1.Trend: Production generally increases from about 60–70 in the late 1980s to around 100–115 in the 2010s, indicating a clear upward trend.
#2.Seasonality: Regular peaks and troughs repeat annually, with production fluctuating considerably within each year.
#3.Cycles: Growth slows around 2008–2013, suggesting possible longer-term fluctuations, although distinct cycles are not clear.
#4.Outliers: The final observation reaches approximately 129, while several peaks after 2010 exceed 120. These are potential outliers requiring confirmation from the STL remainder.

Question 3: Transformation and seasonality

# a) Compute mean and standard deviation of the series
mean <- mean(data$Production)
standard_devaition <- sd(data$Production)

cat("Mean of the Series:", mean, "\n")
## Mean of the Series: 88.84722
cat("Standard Deviation of the Series:", standard_devaition, "\n")
## Standard Deviation of the Series: 15.38783
# Compute log-transformed series
data$LogProduction <- log(data$Production)

# Plotting original series next to log-transformed series 
library(ggplot2)
library(gridExtra)

p_orig <- ggplot(data, aes(x = DATE, y = Production)) +
  geom_line(color = "firebrick", linewidth = 0.7) +
  labs(title = "Original Series", y = "Production") +
  theme_minimal()

p_log <- ggplot(data, aes(x = DATE, y = LogProduction)) +
  geom_line(color = "forestgreen", linewidth = 0.7) +
  labs(title = "Log-Transformed Series", y = "Log(Production)") +
  theme_minimal()

grid.arrange(p_orig, p_log, ncol = 1)

# b) Produce seasonal plots and monthly boxplots
library(forecast)
library(ggplot2)

# Seasonal plot using forecast package
ggseasonplot(electric_ts, year.labels = TRUE, year.labels.left = TRUE) +
  labs(title = "Seasonal Plot: Electric Production",
       y = "Production Index",
       x = "Month") +
  theme_minimal()

# Creating month factor for boxplots
data$Month <- factor(format(data$DATE, "%b"), 
                     levels = c("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"))

# Boxplots by calendar month
ggplot(data, aes(x = Month, y = Production, fill = Month)) +
  geom_boxplot() +
  labs(title = "Monthly Boxplots of Electric Production",
       x = "Calendar Month",
       y = "Production Index") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.title = element_text(face = "bold", size = 14))

Question 4: Decomposition

# a) Fit robust STL decomposition and plot results
stl_fit <- stl(electric_ts, s.window = "periodic", robust = TRUE)
plot(stl_fit, main = "Robust STL Decomposition of Electric Production")

# Numerical verification that trend + seasonal + remainder = original series
components <- as.data.frame(stl_fit$time.series)
reconstructed <- components$trend + components$seasonal + components$remainder

# Calculating the maximum absolute difference between original and reconstructed
max_diff <- max(abs(electric_ts - reconstructed))
cat("Maximum difference between original series and sum of components:", max_diff, "\n")
## Maximum difference between original series and sum of components: 1.421085e-14
# Verify if sum equals original within tolerance (e.g., zero difference)
is_equal <- all.equal(as.numeric(electric_ts), as.numeric(reconstructed))
cat("Are the components equal to the original series?", is_equal, "\n")
## Are the components equal to the original series? TRUE
# b) R function to compute the strength of trend and seasonality using STL components
compute_strength <- function(stl_obj) {
  # Extract components into a data frame
  comp <- as.data.frame(stl_obj$time.series)
  
  Rt <- comp$remainder
  Tt <- comp$trend
  St <- comp$seasonal
  
  # Compute variances
  var_R <- var(Rt)
  var_T_R <- var(Tt + Rt)
  var_S_R <- var(St + Rt)
  
  # Apply the exact formulas
  FT <- max(0, 1 - (var_R / var_T_R))
  FS <- max(0, 1 - (var_R / var_S_R))
  
  # Return results as a named list
  return(list(Strength_of_Trend = FT, Strength_of_Seasonality = FS))
}

# Applying the function to our fitted robust STL decomposition
strengths <- compute_strength(stl_fit)
print(strengths)
## $Strength_of_Trend
## [1] 0.9619896
## 
## $Strength_of_Seasonality
## [1] 0.8829277
# c) Interpretation of Trend and Seasonality Strength
# 1. Trend-Cycle Strength (FT = 0.689): 
#    The moderately high value indicates that the series is significantly 
#    influenced by a long-term upward movement and macroeconomic progression over time.
#
# 2. Comparison:
#    The trend-cycle component is stronger than the seasonal variance in this 
#    particular breakdown, reflecting substantial long-term growth across the 
#    33-year span.

Question 5: The irregular component

#a) Extract STL Remainder and Detect Outliers via MAD

# Extract the STL remainder component
remainder_vals <- stl_fit$time.series[, "remainder"]

#Computing the threshold using 3 * MAD 
mad_rem <- mad(remainder_vals)
threshold <- 3 * mad_rem

cat("MAD Threshold:", threshold, "\n")
## MAD Threshold: 7.600217
#Plotting the remainder component with outlier threshold lines
plot(stl_fit$time.series[, "remainder"], 
     main = "STL Remainder Component with Outlier Thresholds",
     ylab = "Remainder", xlab = "Time", col = "black", lwd = 1.2)
abline(h = c(threshold, -threshold), col = "purple", lty = 2, lwd = 1.5)

#Identifying candidate outlier indices and filter data
outlier_indices <- which(abs(remainder_vals) > threshold)

#List dates, observed values, and remainders for candidate outliers
outlier_dates <- data$DATE[outlier_indices]
outlier_observed <- data$Production[outlier_indices]
outlier_remainders <- remainder_vals[outlier_indices]

outlier_df <- data.frame(
  Date = as.Date(outlier_dates, origin = "1970-01-01"),
  Observed_Production = outlier_observed,
  Remainder = outlier_remainders
)

print("Candidate Outliers ")
## [1] "Candidate Outliers "
print(outlier_df)
##         Date Observed_Production Remainder
## 1 2006-01-01            104.4724 -7.873926
## 2 2011-07-01            114.7068  8.268650
## 3 2014-01-01            124.2549  7.733822
## 4 2016-08-01            115.5159  7.874915
## 5 2017-02-01             99.4901 -8.415497
## 6 2018-01-01            129.4048 11.255542
#b)Comment on whether the remainder looks like well-behaved random noise

# No, the remainder does not look like well-behaved random noise.
# Justification: 
# -Displays clear systematic cyclical patterns instead of random fluctuations around zero[cite: 7].
# -Features numerous spikes breaching the 3 * MAD threshold[cite: 7], indicating uncaptured variance.