Skip to content

Instantly share code, notes, and snippets.

@salamanders
Created April 10, 2019 20:09
Show Gist options
  • Save salamanders/b61cb5dfef046d9d304d5f599d6727f3 to your computer and use it in GitHub Desktop.
Save salamanders/b61cb5dfef046d9d304d5f599d6727f3 to your computer and use it in GitHub Desktop.
Double Exponential Smoothing in Kotlin
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