Equivalent Kotlin code to my F# functional code


#1

Can any one help me converting my below F# code into Kotlin:

//namespace SysIO

open System.Collections.Generic  // for Dictionary

printf "Hi, Let's start \n"

let series = [|30;21;29;31;40;48;53;47;37;39;31;29;17;9;20;24;27;35;41;38;
          27;31;27;26;21;13;21;18;33;35;40;36;22;24;21;20;17;14;17;19;
          26;29;40;31;20;24;18;26;17;9;17;21;28;32;46;33;23;28;22;27;
          18;8;17;21;31;34;44;38;31;30;26;32|]

let initialSeasonalComponents (series : int []) slen : IDictionary<int, double>  =
    let nSeasons = float  (series.Length / slen)
    let grouped = 
        series 
            |> Array.map float 
            |> Array.chunkBySize slen
    let seasonAverages = grouped |> Array.map Array.average
    Array.init slen (fun i -> i, (Array.zip grouped seasonAverages 
                                    |> Array.fold (fun s (els, av) -> els.[i] + s - av) 0.)
                                        / nSeasons) |> dict

printfn "Seasons Averageß: \n %A" (initialSeasonalComponents series 12)

let initialTrend (series : int []) (slen : int) : double =
     series |> Array.windowed slen
            |> Array.fold (fun s x -> 
                            (x |> Array.rev |> Array.head) -  (x |> Array.head) + s) 0
            |> float
            |> fun x -> x / (float slen)

printfn "Initial Trend: \n %A" (initialTrend series 12)

let tripleExponentialSmoothing series slen alpha beta gamma nPreds =
    let mutable smooth = 0.
    let mutable trend = 0.
    let seasonals = initialSeasonalComponents series 12 |> Dictionary 
    seq {
        for i in 0..(series.Length+nPreds-1) do
          match i with
          | 0 ->     // initial values        
              smooth <- series |> Array.head |> float
              trend <- initialTrend series slen
              yield series |> Array.head |> float
          | i when i >= series.Length -> // we are forecasting
              let m = i - series.Length + 1
              yield (smooth + float m * trend) + seasonals.[i%slen]
          | _ -> 
              let v = series |> Array.head  |> float
              let lastSmooth = smooth
              smooth <- alpha*(v-seasonals.Item(i%slen)) + (1.-alpha)*(smooth+trend)
              trend <- beta * (smooth-lastSmooth) + (1.-beta)*trend
              seasonals.[i%slen] <- gamma*(v-smooth) + (1.-gamma)*seasonals.[i%slen]
              yield smooth + trend + seasonals.Item(i%slen) }
   // result

let f = tripleExponentialSmoothing series 12 0.716 0.029 0.993 24
printfn "Forecast: \n %A" f

#2

I used the same variable names, so you can easily identify each step.

import kotlin.coroutines.experimental.buildSequence

fun main(args: Array<String>) {
    println("Hi, Let's start")

    val series = listOf(
        30, 21, 29, 31, 40, 48, 53, 47, 37, 39, 31, 29, 17, 9, 20, 24, 27, 35, 41, 38,
        27, 31, 27, 26, 21, 13, 21, 18, 33, 35, 40, 36, 22, 24, 21, 20, 17, 14, 17, 19,
        26, 29, 40, 31, 20, 24, 18, 26, 17, 9, 17, 21, 28, 32, 46, 33, 23, 28, 22, 27,
        18, 8, 17, 21, 31, 34, 44, 38, 31, 30, 26, 32
    )

    fun initialSeasonalComponents(series: List<Int>, slen: Int): Map<Int, Double> {
        val nSeasons = (series.size / slen).toFloat()
        val grouped = series.map { it.toFloat() }.chunked(slen)
        val seasonAverages = grouped.map { it.average() }
        return (0 until slen).associate {
            Pair(it, grouped.zip(seasonAverages)
                .fold(0.0) { s, (els, av) -> els[it] + s - av }
                    / nSeasons)
        }
    }

    println("Seasons Averageß: \n ${initialSeasonalComponents(series, 12)}")

    fun initialTrend(series: List<Int>, slen: Int): Double =
        series.windowed(slen)
            .fold(0) { s, x -> x.last() - x.first() + s }
            .toFloat() / slen.toDouble()

    println("Initial Trend: \n ${initialTrend(series, 12)}")

    fun tripleExponentialSmoothing(series: List<Int>, slen: Int, alpha: Double, beta: Double, gamma: Double, nPreds: Int): Sequence<Double> {
        var smooth = 0.0
        var trend = 0.0
        val seasonals = initialSeasonalComponents(series, 12).toMutableMap()
        return buildSequence {
            for (i in 0 until (series.size + nPreds)) {
                when {
                    i == 0 -> {
                        smooth = series.first().toDouble()
                        trend = initialTrend(series, slen)
                        yield(series.first().toDouble())
                    }
                    i >= series.size -> {
                        val m = i - series.size + 1
                        yield((smooth + m * trend) + (seasonals[i % slen] ?: 0.0 /* Need default values because of null safety */))
                    }
                    else -> {
                        val v = series.first().toDouble()
                        val lastSmooth = smooth
                        smooth = alpha * (v - (seasonals[i % slen] ?: 0.0)) + (1.0 - alpha) * (smooth + trend)
                        trend = beta * (smooth - lastSmooth) + (1.0 - beta) * trend
                        seasonals[i % slen] = gamma * (v - smooth) + (1.0 - gamma) * (seasonals[i % slen] ?: 0.0)
                        yield(smooth + trend + (seasonals[i % slen] ?: 0.0))
                    }
                }
            }
        }
    }

    val f = tripleExponentialSmoothing(series, 12, 0.716, 0.029, 0.993, 24).toList()
    println("Forecast: \n $f")
}

#3

Thanks a lot, I could not understand how you came with this, can you pls explain little further :slight_smile:


#4

Of course.

The 0.0 is the initial state for the fold function, { s, (els, av) -> } is the operation applied to each element, where s is the accumulator and (els, av) is the current element (in this case an entry of a map), which has been deconstructed, leaving els as the key and av as the value of the current entry.

Hope you can now understand it :wink:


#5

Thanks a lot, very clear now