Created
April 10, 2019 20:09
-
-
Save salamanders/b61cb5dfef046d9d304d5f599d6727f3 to your computer and use it in GitHub Desktop.
Double Exponential Smoothing in Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package info.benjaminhill.basicbot2 | |
import java.util.concurrent.ThreadLocalRandom | |
/** | |
* From https://geekprompt.github.io/Java-implementation-for-Double-Exponential-Smoothing-for-time-series-with-linear-trend/ | |
* | |
* Performs double exponential smoothing for given time series. | |
* | |
* This method is suitable for fitting series with linear trend. | |
* | |
* @param data An array containing the recorded data of the time series | |
* @param alpha Smoothing factor for data. Closer to 1 means bias more from recent values. | |
* @param beta Smoothing factor for trend. Closer to 1 means trend decays faster | |
* @return Instance of model that can be used to forecast future values | |
*/ | |
class DoubleExponentialSmoothing(var data: DoubleArray, alpha: Double = 0.8, beta: Double = 0.2) { | |
val smoothedData = DoubleArray(data.size) | |
private val trend = DoubleArray(data.size + 1) | |
private val level = DoubleArray(data.size + 1) | |
val sse: Double | |
init { | |
require(alpha in 0.0..1.0) | |
require(beta in 0.0..1.0) | |
//initializing values of parameters | |
smoothedData[0] = data[0] | |
trend[0] = data[1] - data[0] | |
level[0] = data[0] | |
for (t in data.indices) { | |
smoothedData[t] = trend[t] + level[t] | |
level[t + 1] = alpha * data[t] + (1 - alpha) * (level[t] + trend[t]) | |
trend[t + 1] = beta * (level[t + 1] - level[t]) + (1 - beta) * trend[t] | |
} | |
sse = data.indices.sumByDouble { Math.pow(smoothedData[it] - data[it], 2.0) } | |
} | |
/** | |
* Forecasts future values. | |
* | |
* @param size no of future values that you need to forecast | |
* @return forecast data | |
*/ | |
fun forecast(size: Int): DoubleArray { | |
val forecastData = DoubleArray(size) | |
for (i in 0 until size) { | |
forecastData[i] = level[level.size - 1] + (i + 1) * trend[trend.size - 1] | |
} | |
return forecastData | |
} | |
} | |
fun main() { | |
val rnd = ThreadLocalRandom.current()!! | |
val testData = DoubleArray(70) { | |
Math.cos(it / (Math.PI * 2)) + rnd.nextGaussian() / 3.0 | |
} | |
val model = DoubleExponentialSmoothing( | |
data = testData, | |
alpha = 0.3 // more smooth | |
) | |
println("Sum of squared error: " + model.sse) | |
// Print in sheets-friendly view | |
println(listOf("Step", "Input", "Smoothed", "Forecast").joinToString("\t")) | |
testData.mapIndexed { i, input -> | |
println(listOf(i, input, model.smoothedData[i]).joinToString("\t")) | |
} | |
model.forecast(7).mapIndexed { i, forecast -> | |
println(listOf(i + testData.size, "", "", forecast).joinToString("\t")) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment