# Tutorial: Forecast bike rental service demand with time series analysis and ML.NET

Learn how to forecast demand for a bike rental service using univariate time series analysis on data stored in a SQL Server database with ML.NET.

In this tutorial, you learn how to:

• Understand the problem
• Load data from a database
• Create a forecasting model
• Evaluate forecasting model
• Save a forecasting model
• Use a forecasting model

## Time series forecasting sample overview

This sample is a C# .NET Core console application that forecasts demand for bike rentals using a univariate time series analysis algorithm known as Single Spectrum Analysis. The code for this sample can be found on the dotnet/machinelearning-samples repository on GitHub.

## Understand the problem

In order to run an efficient operation, inventory management plays a key role. Having too much of a product in stock means unsold products sitting on the shelves not generating any revenue. Having too little product leads to lost sales and customers purchasing from competitors. Therefore, the constant question is, what is the optimal amount of inventory to keep on hand? Time-series analysis helps provide an answer to these questions by looking at historical data, identifying patterns, and using this information to forecast values some time in the future.

The technique for analyzing data used in this tutorial is univariate time-series analysis. Univariate time-series analysis takes a look at a single numerical observation over a period of time at specific intervals such as monthly sales.

The algorithm used in this tutorial is Single Spectrum Analysis(SSA). SSA works by decomposing a time-series into a set of principal components. These components can be interpreted as the parts of a signal that correspond to trends, noise, seasonality, and many other factors. Then, these components are reconstructed and used to forecast values some time in the future.

## Create console application

1. Create a new C# .NET Core console application called "BikeDemandForecasting".
2. Install Microsoft.ML version 1.4.0 NuGet package
1. In Solution Explorer, right-click on your project and select Manage NuGet Packages.
2. Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.ML.
3. Check the Include prerelease checkbox.
4. Select the Install button.
5. Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.
6. Repeat these steps for System.Data.SqlClient version 4.7.0 and Microsoft.ML.TimeSeries version 1.4.0.

### Prepare and understand the data

1. Create a directory called Data.
2. Download the DailyDemand.mdf database file and save it to the Data directory.

Note

The data used in this tutorial comes from the UCI Bike Sharing Dataset. Fanaee-T, Hadi, and Gama, Joao, 'Event labeling combining ensemble detectors and background knowledge', Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.

The original dataset contains several columns corresponding to seasonality and weather. For brevity and because the algorithm used in this tutorial only requires the values from a single numerical column, the original dataset has been condensed to include only the following columns:

• dteday: The date of the observation.
• year: The encoded year of the observation (0=2011, 1=2012).
• cnt: The total number of bike rentals for that day.

The original dataset is mapped to a database table with the following schema in a SQL Server database.

CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);


The following is a sample of the data:

RentalDate Year TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

### Create input and output classes

1. Open Program.cs file and replace the existing using statements with the following:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Transforms.TimeSeries;

2. Create ModelInput class. Below the Program class, add the following code.

public class ModelInput
{
public DateTime RentalDate { get; set; }

public float Year { get; set; }

public float TotalRentals { get; set; }
}


The ModelInput class contains the following columns:

• RentalDate: The date of the observation.
• Year: The encoded year of the observation (0=2011, 1=2012).
• TotalRentals: The total number of bike rentals for that day.
3. Create ModelOutput class below the newly created ModelInput class.

public class ModelOutput
{
public float[] ForecastedRentals { get; set; }

public float[] LowerBoundRentals { get; set; }

public float[] UpperBoundRentals { get; set; }
}


The ModelOutput class contains the following columns:

• ForecastedRentals: The predicted values for the forecasted period.
• LowerBoundRentals: The predicted minimum values for the forecasted period.
• UpperBoundRentals: The predicted maximum values for the forecasted period.

### Define paths and initialize variables

1. Inside the Main method, define variables to store the location of your data, connection string, and where to save the trained model.

string rootDir = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../"));
string dbFilePath = Path.Combine(rootDir, "Data", "DailyDemand.mdf");
string modelPath = Path.Combine(rootDir, "MLModel.zip");
Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");  8. Use the Evaluate method inside the Main method. Evaluate(secondYearData, forecaster, mlContext);  ## Save the model If you're satisfied with your model, save it for later use in other applications. 1. In the Main method, create a TimeSeriesPredictionEngine. TimeSeriesPredictionEngine is a convenience method to make single predictions. var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);  2. Save the model to a file called MLModel.zip as specified by the previously defined modelPath variable. Use the Checkpoint method to save the model. forecastEngine.CheckPoint(mlContext, modelPath);  ## Use the model to forecast demand 1. Below the Evaluate method, create a new utility method called Forecast. static void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }  2. Inside the Forecast method, use the Predict method to forecast rentals for the next seven days. ModelOutput forecast = forecaster.Predict();  3. Align the actual and forecast values for seven periods. IEnumerable<string> forecastOutput = mlContext.Data.CreateEnumerable<ModelInput>(testData, reuseRowObject: false) .Take(horizon) .Select((ModelInput rental, int index) => { string rentalDate = rental.RentalDate.ToShortDateString(); float actualRentals = rental.TotalRentals; float lowerEstimate = Math.Max(0, forecast.LowerBoundRentals[index]); float estimate = forecast.ForecastedRentals[index]; float upperEstimate = forecast.UpperBoundRentals[index]; return$"Date: {rentalDate}\n" +
$"Actual Rentals: {actualRentals}\n" +$"Lower Estimate: {lowerEstimate}\n" +
$"Forecast: {estimate}\n" +$"Upper Estimate: {upperEstimate}\n";
});

4. Iterate through the forecast output and display it on the console.

Console.WriteLine("Rental Forecast");
Console.WriteLine("---------------------");
foreach (var prediction in forecastOutput)
{
Console.WriteLine(prediction);
}


## Run the application

1. Inside the Main method, call the Forecast method.

Forecast(secondYearData, 7, forecastEngine, mlContext);

2. Run the application. Output similar to that below should appear on the console. For brevity, the output has been condensed.

Evaluation Metrics
---------------------
Mean Absolute Error: 726.416
Root Mean Squared Error: 987.658

Rental Forecast
---------------------
Date: 1/1/2012
Actual Rentals: 2294
Lower Estimate: 1197.842
Forecast: 2334.443
Upper Estimate: 3471.044

Date: 1/2/2012
Actual Rentals: 1951
Lower Estimate: 1148.412
Forecast: 2360.861
Upper Estimate: 3573.309


Inspection of the actual and forecasted values shows the following relationships:

While the forecasted values are not predicting the exact number of rentals, they provide a more narrow range of values that allows an operation to optimize their use of resources.

Congratulations! You've now successfully built a time series machine learning model to forecast bike rental demand.

You can find the source code for this tutorial at the dotnet/machinelearning-samples repository.