Download Build Status Gitter

krangl is a {K}otlin library for data w{rangl}ing. By implementing a grammar of data manipulation using a modern functional-style API, it allows to filter, transform, aggregate and reshape tabular data.

krangl is heavily inspired by the amazing dplyr for R. krangl is written in Kotlin, excels in Kotlin, but emphasizes as well on good java-interop. It is mimicking the API of dplyr, while carefully adding more typed constructs where possible.


To get started simply add it as a dependency via Jcenter:

compile "de.mpicbg.scicomp:krangl:0.7"

You can also use JitPack with Maven or Gradle to build the latest snapshot as a dependency in your project.

repositories {
    maven { url '' }
dependencies {
        compile 'com.github.holgerbrandl:krangl:-SNAPSHOT'

To build it locally and install it into your local maven cache simply run

./gradlew install


  • Filter, transform, aggregate and reshape tabular data
  • Modern, user-friendly and easy-to-learn data-science API
  • Reads from plain and compressed tsv, csv, json, or any delimited format with or without header from local or remote
  • Supports grouped operations
  • Ships with JDBC support
  • Tables can contain atomic columns (int, double, boolean) as well as object columns
  • Reshape tables from wide to long and back
  • Table joins (left, right, semi, inner, outer)
  • Cross tabulation
  • Descriptive statistics (mean, min, max, median, …)
  • Functional API inspired by dplyr, pandas, and Kotlin stdlib

  • many more…

krangl is just about data wrangling. For data visualization we recommend kravis which seamlessly integrates with krangl and implements a grammar to build a wide variety of plots.


// Read data-frame from disk
val iris = DataFrame.readCSV("data/iris.txt")

// Create data-frame in memory
val df: DataFrame = dataFrameOf(
    "first_name", "last_name", "age", "weight")(
    "Max", "Doe", 23, 55,
    "Franz", "Smith", 23, 88,
    "Horst", "Keanes", 12, 82

// Or from csv
// val otherDF = DataFrame.readCSV("path/to/file")

// Print rows
df                              // with implict string conversion using default options
df.print(colNames = false)      // with custom printing options

// Print structure

// Add columns with mutate
// by adding constant values as new column
df.addColumn("salary_category") { 3 }

// by doing basic column arithmetics
df.addColumn("age_3y_later") { it["age"] + 3 }

// Note: krangl dataframes are immutable so we need to (re)assign results to preserve changes.
val newDF = df.addColumn("full_name") { it["first_name"] + " " + it["last_name"] }

// Also feel free to mix types here since krangl overloads  arithmetic operators like + for dataframe-columns
df.addColumn("user_id") { it["last_name"] + "_id" + rowNumber }

// Create new attributes with string operations like matching, splitting or extraction.
df.addColumn("with_anz") { it["first_name"].asStrings().map { it!!.contains("anz") } }

// Note: krangl is using 'null' as missing value, and provides convenience methods to process non-NA bits
df.addColumn("first_name_initial") { it["first_name"].map<String>{ it.first() } }

// or add multiple columns at once
    "age_plus3" to { it["age"] + 3 },
    "initials" to { it["first_name"].map<String> { it.first() } concat it["last_name"].map<String> { it.first() } }

// Sort your data with sortedBy
// and add secondary sorting attributes as varargs
df.sortedBy("age", "weight")
df.sortedBy { it["weight"].asInts() }

// Subset columns with select
df.select2 { it is IntCol } // functional style column selection"last_name", "weight")    // positive selection
df.remove("weight", "age")  // negative selection{ endsWith("name") })    // selector mini-language

// Subset rows with vectorized filter
df.filter { it["age"] eq 23 }
df.filter { it["weight"] gt 50 }
df.filter({ it["last_name"].isMatching { startsWith("Do")  }})

// In case vectorized operations are not possible or available we can also filter tables by row
// which allows for scalar operators
df.filterByRow { it["age"] as Int > 5 }
df.filterByRow { (it["age"] as Int).rem(10) == 0 } // round birthdays :-)

// Summarize

// do simple cross tabulations
df.count("age", "last_name")

// ... or calculate single summary statistic
df.summarize("mean_age" to { it["age"].mean(true) })

// ... or multiple summary statistics
    "min_age" to { it["age"].min() },
    "max_age" to { it["age"].max() }

// for sake of r and python adoptability you can also use `=` here
    "min_age" `=` { it["age"].min() },
    "max_age" `=` { it["age"].max() }

// Grouped operations
val groupedDf: DataFrame = df.groupBy("age") // or provide multiple grouping attributes with varargs
val sumDF = groupedDf.summarize(
    "mean_weight" to { it["weight"].mean(removeNA = true) },
    "num_persons" to { nrow }

// Optionally ungroup the data

// generate object bindings for kotlin.
// Unfortunately the syntax is a bit odd since we can not access the variable name by reflection

// This will generate and print the following conversion code:
data class SumDF(val age: Int, val mean_weight: Double, val num_persons: Int)

val records = { row -> SumDF(row["age"] as Int, row["mean_weight"] as Double, row["num_persons"] as Int) }

// Now we can use the krangl result table in a strongly typed way

// Vice versa we can also convert an existing set of objects into
val recordsDF = records.asDataFrame()

// to populate a data-frame with selected properties only, we can do
val deparsedDF = records.deparseRecords { mapOf("age" to it.age, "weight" to it.mean_weight) }

Support & Documentation

krangl is not yet mature, full of bugs and its API is in constant flux. Nevertheless, feel welcome to submit pull-requests or tickets, or simply get in touch via gitter (see button on top).


How to rewrite common SQL bits with krangl?

  1. select this, that from there where that >5"this", "that").filter{ it["that"] gt 5 }


Similar APIs (not just Kotlin)

  • Scala DataTable: a lightweight, in-memory table structure written in Scala
  • joinery implements data frames for Java
  • tablesaw which is (according to its authors) the The simplest way to slice data in Java
  • paleo which provides immutable Java 8 data frames with typed columns
  • agate isa Python data analysis library that is optimized for humans instead of machines
  • pandas provides high-performance, easy-to-use data structures and data analysis tools for python (cheatsheet)
  • dplyr which is a grammar of data manipulation (R-lang)
  • morpheus-core which is a data science framework implementing an R-like data-frame for the JVM

Other data-science projects:

  • vectorz is a fast and flexible numerical library for Java featuring N-dimensional arrays
  • koma is a scientific computing library written in Kotlin, designed to allow development of cross-platform numerical applications
  • termsql converts text from a file or from stdin into SQL table and query it instantly. Uses sqlite as backend.
  • kotliquery is a handy database access library
  • Dex : The Data Explorer is a data visualization tool written capable of powerful ETL and publishing web visualizations

Data Visualization

  • kravis which implements a Kotlin DSL for scientific data visualization
  • XChart is a light weight Java library for plotting data
  • Charts in TornadoFX provide visualzation examples using javaFX
compile "de.mpicbg.scicomp:krangl:0.7"

Related Libraries


Idiomatic statistical operators for Kotlin

Last updated 3 mins ago


Set of extensions for Kotlin that provides Discrete math functionalities

Last updated 3 mins ago


A scientific computing library for Kotlin. https -//

Last updated 3 mins ago


krangl is a {K}otlin DSL for data w{rangl}ing

Last updated 3 mins ago