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

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

Популярные библиотеки[править]

  • Kotlin-statistics[1] — библиотека с набором функций-расширений для работы с коллекциями, необходимыми в задачах статистики, такими как mode, median, range, variance, standardDeviation, geometricMean и др. Также, библиотека предоставляет расширения для трансформации коллекций, агрегации, сэмплинга данных. Есть реализации алгоритмов классификации, регрессии. Библиотека поддается расшерению API, за счет объявления своих расшерений, реализация которых может использовать Apache Math. Библиотека не содержит собственной реализации структур данных - все опреации производятся над стандартными интерфейсами (Sequence, Iterable и т.п.), функционал которых расширен благодаря механизму фнкций-расширения в Kotlin[2]. Нет встроенной поддержки визуализации данных, но можно использовать TornadoFX, работающий со стандартными коллекциями.
  • KMath[3] — аналог numpy: поддержка алгебраических структур, массиво-подобных коллекций, гистограмм и т.д. На текущий момент 19.04.20 находится в разработке.
  • SMILE[4] — JVM фреймворк, для которого помимо официального API[5] на Kotlin существуют расширения SMILE-NLP-KT[6]. Фреймворк используется для решения различных задач машинного обучения, таких как: классификация, регрессия, кластеризация, использование генетических алгоритмов, KNN, вывод отсутствующих значений набора данных, обработку естественных языков. Есть встроенная поддержка визуализации данных, средств для чтения и нормализации данных различных форматов (csv, apache arrow, json, jdbc).

Так как Kotlin интеропабилен с Java, то помимо поддерживающих для Kotlin библиотек можно использовать библиотеки для Java, например:

  • Deeplearning4j[7] — DSL на Java для конфигурации глубоких нейронных сетей. Библиотека поддерживает распределенные вычисления, используя Apache Spark. Помимо реализаций алгоритмов машинного обучения, библиотека содержит классы для загрузки и нормализации данных.

Также есть возможность[8] работы с 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]
             }
         }
     }
 }

Примечания[править]