In part A, I want you to forecast how much cash is taken out of 4 different ATM machines for May 2010. The data is given in a single file. The variable ‘Cash’ is provided in hundreds of dollars, other than that it is straight forward. I am being somewhat ambiguous on purpose to make this have a little more business feeling. Explain and demonstrate your process, techniques used and not used, and your actual forecast. I am giving you data via an excel file, please provide your written report on your findings, visuals, discussion and your R code via an RPubs link along with the actual.rmd file Also please submit the forecast which you will put in an Excel readable file.
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ tsibble::interval() masks lubridate::interval()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# A tibble: 14 × 3
DATE ATM Cash
<date> <chr> <dbl>
1 2010-05-01 <NA> NA
2 2010-05-02 <NA> NA
3 2010-05-03 <NA> NA
4 2010-05-04 <NA> NA
5 2010-05-05 <NA> NA
6 2010-05-06 <NA> NA
7 2010-05-07 <NA> NA
8 2010-05-08 <NA> NA
9 2010-05-09 <NA> NA
10 2010-05-10 <NA> NA
11 2010-05-11 <NA> NA
12 2010-05-12 <NA> NA
13 2010-05-13 <NA> NA
14 2010-05-14 <NA> NA
Looking at the data, it appears there are 14 rows with no ATM specified so I will filter those data points because they’re irrelevant for the forecasts.
ATM_df <- ATM_df |>filter(!is.na(ATM))
Plotting the data
ggplot(ATM_df, aes(x = DATE, y = Cash, color = ATM)) +geom_line() +facet_wrap(~ ATM, scales ="free_y", ncol =2, axes ="all_x") +labs(title ="Daily Cash Withdrawals by ATM",x ="Date",y ="Cash (hundreds of dollars)") +theme_minimal() +theme(legend.position ="none")
From my initial observations of the plots for the 4 ATMs, a couple things stand out. ATM3 appears to have no withdrawals while the other ATMs have regular activity. ATM4 appears to have one spike in withdrawal where a lot of cash was taken out around February 2010. ATM1 and ATM2 both appear to be seasonal with ATM1 being more steady while ATM2 appears to have higher withdrawals in 2009 that declined in 2010. For the ATM forecasts, I will do them each separately.
ATM 1
ATM1_data <- ATM_df |>filter(ATM =="ATM1") |>arrange(DATE)ggplot(ATM1_data, aes(x = DATE, y = Cash)) +geom_line(color ="steelblue") +labs(title ="Daily Cash Withdrawals for ATM 1",x ="Date",y ="Cash (Hundreds of Dollars)" )
summary(ATM1_data)
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 1.00
1st Qu.:2009-07-31 Class :character 1st Qu.: 73.00
Median :2009-10-30 Mode :character Median : 91.00
Mean :2009-10-30 Mean : 83.89
3rd Qu.:2010-01-29 3rd Qu.:108.00
Max. :2010-04-30 Max. :180.00
NA's :3
Looking at the plot for ATM 1, it appears to have weekly seasonality with no apparent negative or positive trend.
Grabbing the summary statistics for ATM 1, we can see that there are 3 missing values and 365 data points. In order to handle the 3 missing values, I’ll impute the data using the mean value because it’s only missing a few of the values and there are no extreme values.
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 1.00
1st Qu.:2009-07-31 Class :character 1st Qu.: 73.00
Median :2009-10-30 Mode :character Median : 90.00
Mean :2009-10-30 Mean : 83.89
3rd Qu.:2010-01-29 3rd Qu.:108.00
Max. :2010-04-30 Max. :180.00
I’ll calculate the lambda to see if a box-cox transformation is necessary for stabilizing the variance.
Based on the lambda value, I will use a box-cox transformation to stabilize the variance. The data is not stationary since there is clear seasonality so I will use unit root tests to calculate the differences required to make the data stationary.
Recalculating it after the seasonal difference, it appears like no additional difference is needed. Next I’ll check the plots for the data using gg_tsdisplay.
ATM1_ts |>mutate(d_bc_Cash =difference(box_cox(Cash, ATM1_lambda), lag =7)) |>gg_tsdisplay(d_bc_Cash, plot_type ="partial")
Warning: Removed 7 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 7 rows containing missing values or values outside the scale range
(`geom_point()`).
Looking at the plots, the acf plot shows a spike at lag 7 and the pacf plot shows spikes at lag 7, 14, and 21. This confirms a strong weekly seasonality.
The Auto ARIMA had a better AICc score and a better acf plot looks better than that of the ARIMA(0,0,0)(0,1,1) model. Next, I’ll try an auto ETS model and compare it just to check.
This comparison of the RMSE and MAE values shows that the manual ARIMA better fits the historical data compared to the Auto ARIMA and Auto ETS. Even though the manual ARIMA has lower values, I think using the auto ARIMA to generate the forecasts is a better choice because it has a lower AICc value and its acf plot was within the blue dashed lines compared to the acf plot of the manual ARIMA.
ATM2_data <- ATM_df |>filter(ATM =="ATM2") |>arrange(DATE)ggplot(ATM2_data, aes(x = DATE, y = Cash)) +geom_line(color ="steelblue") +labs(title ="Daily Cash Withdrawals for ATM 2",x ="Date",y ="Cash (Hundreds of Dollars)" )
summary(ATM2_data)
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 0.00
1st Qu.:2009-07-31 Class :character 1st Qu.: 25.50
Median :2009-10-30 Mode :character Median : 67.00
Mean :2009-10-30 Mean : 62.58
3rd Qu.:2010-01-29 3rd Qu.: 93.00
Max. :2010-04-30 Max. :147.00
NA's :2
Looking at the plot for ATM 2, it also appears to have weekly seasonality that has slightly declined over time.
The summary statistics for ATM 2 show that there are 2 missing values and 365 data points. In order to handle the 2 missing values, I will impute the data using the mean value because the data is only missing a few values with no extreme outliers.
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 0.00
1st Qu.:2009-07-31 Class :character 1st Qu.: 26.00
Median :2009-10-30 Mode :character Median : 66.00
Mean :2009-10-30 Mean : 62.58
3rd Qu.:2010-01-29 3rd Qu.: 93.00
Max. :2010-04-30 Max. :147.00
Based on the lambda value generated from the Guerrero method, I’ll a box-cox transformation to stabilize the variance.
Next I’ll use unit root tests to figure out how many differences is needed to make the data stationary. Data being stationary is a requirement for ARIMA models.
Warning: Removed 8 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 8 rows containing missing values or values outside the scale range
(`geom_point()`).
The plot for the differenced and seasonal differenced ATM2 data flutuates around 0. The acf and pacf still have spikes that go past the blue dashed lines though. the notable spikes at lag 7 in both plots show that this is weekly seasonality.
# A tibble: 2 × 3
.model RMSE MAE
<chr> <dbl> <dbl>
1 auto_ARIMA 24.4 17.1
2 auto_ETS 25.3 17.8
Since the Auto ARIMA had a better AICc score compared to my manual ARIMA models and a lower RMSE than the Auto ETS model, I’ll use the Auto ARIMA model to generate the forecast for ATM2.
ATM2_ARIMA_fit |>gg_tsresiduals()
Looking at the plots for the auto ARIMA model above, it does resemble white noise so I’ll use the ARIMA model to generate the forecasts for ATM 1.
ATM3_data <- ATM_df |>filter(ATM =="ATM3") |>arrange(DATE)ggplot(ATM3_data, aes(x = DATE, y = Cash)) +geom_line(color ="steelblue") +labs(title ="Daily Cash Withdrawals for ATM 3",x ="Date",y ="Cash (Hundreds of Dollars)" )
summary(ATM3_data)
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 0.0000
1st Qu.:2009-07-31 Class :character 1st Qu.: 0.0000
Median :2009-10-30 Mode :character Median : 0.0000
Mean :2009-10-30 Mean : 0.7205
3rd Qu.:2010-01-29 3rd Qu.: 0.0000
Max. :2010-04-30 Max. :96.0000
Looking at the plot for ATM 3, it appears to be mostly 0 values with only a few data points at the very end. In order to work with the data, I’ll filter the 0s out first because they will bring down the forecast.
ATM4_data <- ATM_df |>filter(ATM =="ATM4") |>arrange(DATE)ggplot(ATM4_data, aes(x = DATE, y = Cash)) +geom_line(color ="steelblue") +labs(title ="Daily Cash Withdrawals for ATM 4",x ="Date",y ="Cash (Hundreds of Dollars)" )
summary(ATM4_data)
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 1.563
1st Qu.:2009-07-31 Class :character 1st Qu.: 124.334
Median :2009-10-30 Mode :character Median : 403.839
Mean :2009-10-30 Mean : 474.043
3rd Qu.:2010-01-29 3rd Qu.: 704.507
Max. :2010-04-30 Max. :10919.762
Looking at the data plot for ATM 4, there is one very big anomaly so I’ll impute the data with the median value because of that extreme outlier value.
DATE ATM Cash
Min. :2009-05-01 Length:365 Min. : 1.563
1st Qu.:2009-07-31 Class :character 1st Qu.: 124.334
Median :2009-10-30 Mode :character Median : 403.839
Mean :2009-10-30 Mean : 445.233
3rd Qu.:2010-01-29 3rd Qu.: 704.192
Max. :2010-04-30 Max. :1712.075
ggplot(ATM4_data, aes(x = DATE, y = Cash)) +geom_line(color ="steelblue") +labs(title ="Daily Cash Withdrawals for ATM 4",x ="Date",y ="Cash (Hundreds of Dollars)" )
ATM4_ts<- ATM4_data |>as_tsibble(index = DATE)
Next I’ll use the Guerrero method to calculate the lambda to see if a box-cox transformation is needed.
ATM4_lambda <- ATM4_ts |>features(Cash, features = guerrero) |>pull(lambda_guerrero)ATM4_lambda
[1] 0.4525693
Based on the lambda value, I’ll use a box-cox transformation to stabilize the variance and use the unit root tests to check if a difference or seasonal difference is needed to make the data stationary.
# A tibble: 2 × 3
.model RMSE MAE
<chr> <dbl> <dbl>
1 auto_ets 344. 262.
2 auto_arima 352. 274.
Since the auto ETS had a better RMSE and MAE score than the auto ARIMA, I’ll use that to generate the forecast after I verify that the residuals resemble white noise.
Part B consists of a simple dataset of residential power usage for January 1998 until December 2013. Your assignment is to model these data and a monthly forecast for 2014. The data is given in a single file. The variable ‘KWH’ is power consumption in Kilowatt hours, the rest is straight forward. Add this to your existing files above.
CaseSequence YYYY-MMM KWH
Min. :733.0 Min. :1998 Jan Min. : 770523
1st Qu.:780.8 1st Qu.:2001 Dec 1st Qu.: 5429912
Median :828.5 Median :2005 Dec Median : 6283324
Mean :828.5 Mean :2005 Dec Mean : 6502475
3rd Qu.:876.2 3rd Qu.:2009 Dec 3rd Qu.: 7620524
Max. :924.0 Max. :2013 Dec Max. :10655730
NA's :1
CaseSequence YYYY-MMM KWH Month
Min. :733.0 Min. :1998 Jan Min. : 770523 Min. :1998 Jan
1st Qu.:780.8 1st Qu.:2001 Dec 1st Qu.: 5434539 1st Qu.:2001 Dec
Median :828.5 Median :2005 Dec Median : 6283324 Median :2005 Dec
Mean :828.5 Mean :2005 Dec Mean : 6501333 Mean :2005 Dec
3rd Qu.:876.2 3rd Qu.:2009 Dec 3rd Qu.: 7608792 3rd Qu.:2009 Dec
Max. :924.0 Max. :2013 Dec Max. :10655730 Max. :2013 Dec
I’ll calculate the lambda to see if a box-cox transformation is necessary for stabilizing the variance.
Power_lambda <- power_ts_median_imputed |>features(KWH, features = guerrero) |>pull(lambda_guerrero)Power_lambda
[1] -0.2134793
Based on the resulting lambda value, I will use a box-cox transformation to stabilize the variance. The data is not stationary since there is clear seasonality so I will use unit root tests to calculate the differences required to make the data stationary.
The acf plot for the auto ARIMA has a couple spikes at 3 and 6 that go past the blue dashed lines but the rest are within the blue dashed lines. Next I’ll check an auto ETS model.
# A tibble: 2 × 3
.model RMSE MAE
<chr> <dbl> <dbl>
1 auto_ETS 651744. 490988.
2 auto_ARIMA 652389. 498499.
Comparing the Auto ARIMA and Auto ETS models, the Auto ETS model fits the historical data better hence the slightly lower RMSE and MAE values. Therefore I will use the Auto ETS model to generate the forecasts for 2014.