Примеры кода на Kotlin

Материал из Викиконспекты
Перейти к: навигация, поиск

Популярные библиотеки

  • Kotlin-statistics[1] — библиотека с набором функций-расширений для работы с коллекциями, такими как mode, median, range, variance, standardDeviation, geometricMean и др. Также, библиотека предоставляет расширения для трансформации коллекций и агрегации данных. Есть реализации Наивного Баесового Классификаторы, алгоритмов классификации, линейной регресси.
  • KMath[2] — аналог numpy: поддержка алгебраических структур, массиво-подобных коллекций, гистограмм и т.д.

Так как Kotlin интеропабилен с Java, то помимо специфичных для Kotlin библиотек можно использовать библиотеки для Java. Также есть возможность[3] работы с NumPy.

Примеры кода

Примеры кода написаны на kotlin 1.3.71 для JVM, с использованием kotlin-statistics

Gradle зависимость:

 repositories {
   maven { url 'https://jitpack.io' }
 }
 
 dependencies {
   implementation 'com.github.thomasnield:kotlin-statistics:-SNAPSHOT'
 }

Линейная регрессия

Основная статья: Линейная регрессия

Пример линейной регрессии c применением Kotlin-statistics:

 fun main() {
   val r = sequenceOf(
       1.0 to 3.0,
       2.0 to 6.0,
       3.0 to 9.0,
       4.0 to 11.8
   ).simpleRegression()
 
   println(r.slope)           // 2.9400000000000004
   println(r.meanSquareError) // 0.006000000000000227
   println(r.predict(5.0)).   // 14.8
 }

Байесовская классификация

Основная статья: Байесовская классификация.

Пример классификации при помощи Наивного Байесовского Классификатора:

 import org.nield.kotlinstatistics.toNaiveBayesClassifier
 
 class Email(val message: String, val isSpam: Boolean)
 
 fun main() {
     val emails = listOf(
         Email("Hey! If you really want to enlarge your ML scores click here", isSpam = true),
         Email("Earn 50 more points for ML just by visiting this site!", isSpam = true),
         Email("Still have F grade? Professional help with ML right here", isSpam = true),
 
         Email("Hey, I left my phone at home. Email me if you need anything.", isSpam = false),
         Email("Stay At Home: COVID-19 news", isSpam = false),
         Email("Please see attachment for notes on today's meeting.", isSpam = false),
         Email("JetBrains license certificate", isSpam = false),
         Email("Your Education Pack expires soon ", isSpam = false)
     )
     val nbc = emails.toNaiveBayesClassifier(
         featuresSelector = { it.message.splitWords().toSet() },
         categorySelector = { it.isSpam }
     )
 
     val spamInput = "your grade is still so bad, but I can help you to get more scores".splitWords().toSet()
     require(nbc.predict(spamInput) == true) { spamInput }
 
     val legitInput = "Thank you for placing the order ".splitWords().toSet()
     require(nbc.predict(legitInput) == false) { legitInput }
 }
 
 fun String.splitWords(): Sequence<String> = this.split(Regex("\\s"))
     .asSequence()
     .map { it.replace(Regex("[^A-Za-z]"), "") }
     .map { it.toLowerCase() }
     .filter { it.isNotEmpty() }

Кластеризация

Основная статья: Кластеризация.

Набор точек для кластеризации

Пример кластеризации с DBSCAN:

 import org.nield.kotlinstatistics.dbScanCluster
 import kotlin.math.pow
 import kotlin.math.sin
 
 inline fun <V> IntProgression.mapDouble(mapper: (Double) -> V) = this.map { mapper(it.toDouble()) }
 
 data class Point(val coordinates: Pair<Double, Double>, val cluster: Int)
 
 fun main() {
 
     val firstCluster = (1..100 step 1)
         .mapDouble { x -> Point(x to x / 2, cluster = 1) }
   
     val secondCluster = (1..80 step 3)
         .mapDouble { x -> Point(x to (x / 12).pow(2) + 20, cluster = 2) }
 
     val thirdCluster = (60..150 step 1)
         .mapDouble { x -> Point(x to 10 * sin(x / 5) + 15, cluster = 3) }
 
     val points = firstCluster + secondCluster + thirdCluster
   
     val clusters = points.dbScanCluster(
         xSelector = { (coords) -> coords.first },
         ySelector = { (coords) -> coords.second },
         maximumRadius = 5.0,
         minPoints = 1
     )
   
     val pointsWithMatchedClusters = clusters.withIndex()
         .flatMap { (clusterIdx, matched) -> matched.points.map { p -> p to clusterIdx + 1 } }
 
     require(clusters.size == 3) { clusters.size }
   
     val pointsWithMismatchedCluster = pointsWithMatchedClusters.filterNot { (p, cluster) -> cluster == p.cluster }
     require(pointsWithMismatchedCluster.isEmpty()) { pointsWithMatchedClusters }
 }

Пример работы с матрицами

Пример использования средств языка и методов стандартной библиотеки для работы с матрицами

 typealias Vector = List<Double>
 typealias Matrix = List<Vector>
 
 class MatrixBuilder {
     private var matrixWidth: Int? = null
     private val _result: MutableList<Vector> = mutableListOf()
     val result: Matrix = _result
 
     operator fun invoke(vararg vector: Double) = addVector(vector.toList())
 
     operator fun invoke(vararg vector: Number) = addVector(vector.map { it.toDouble() })
 
     private fun addVector(vectorList: List<Double>) {
         _result.add(vectorList)
         if (matrixWidth != null) {
             require(vectorList.size == matrixWidth) {
                 "Vector size must be the same among all builder invocations: $vectorList, $_result"
             }
         } else {
             matrixWidth = vectorList.size
         }
     }
 }
 
 fun matrix(builder: MatrixBuilder.() -> Unit): Matrix = MatrixBuilder().apply(builder).result
 
 fun main() {
 
     val multiplied = matrix {
         this(1, 2, 3, 4)
         this(1, 2, 3, 4)
         this(1, 2, 3, 4)
     } * matrix {
         this(5, 6)
         this(7, 8)
         this(9, 10)
         this(11, 12)
     }
 
     multiplied
         .transpose()
         .print()
 }
 
 fun Matrix.transpose(): Matrix = this.asSequence()
     .map { it.withIndex() }
     .flatten()
     .groupBy({ it.index }, { it.value })
     .values
     .toList()
 
 operator fun Matrix.times(other: Matrix): Matrix {
     val (rows1, cols1) = this.size()
     val (_, cols2) = other.size()
     return (0 until rows1).map { i ->
         (0 until cols2).map { j ->
             (0 until cols1).fold(0.0) { s, k ->
                 s + this[i][k] * other[k][j]
             }
         }
     }
 }
 
 fun Matrix.size(): Pair<Int, Int> = this.size to this.first().size
 
 fun Pair<Int, Int>.zeroMatrix(): Matrix = List(this.first) { List(this.second) { 0.0 } }
 
 fun Matrix.print() = println(this.joinToString(separator = "\n") { it.joinToString(separator = " ") })
 
 fun List<Matrix>.sum(): Matrix {
     val n = this.size
     val (rowsCount, colCount) = this[0].size()
     return (0 until rowsCount).map { i ->
         (0 until colCount).map { j ->
             (0 until n).fold(0.0) { s, k ->
                 s + this[k][i][j]
             }
         }
     }
 }

Примечания