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

Overview

krangl

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.

If you're not sure about how to proceed, check out krangl in 10 minutes section in the krangl user guide.

Installation

To get started simply add it as a dependency to your build.gradle:

repositories {
    mavenCentral() 
}

dependencies {
    implementation "com.github.holgerbrandl:krangl:0.17.1"
}

Declaring the repository is purely optional as it is the default already.

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

repositories {
    maven { url 'https://jitpack.io' }
}
dependencies {
    implementation 'com.github.holgerbrandl:krangl:-SNAPSHOT'
}

To build and install it into your local maven cache, simply clone the repo and run

./gradlew install

Features

  • 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.

Examples

// Read data-frame from disk
val iris = DataFrame.readTSV("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
df.schema()


// 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
df.addColumns(
    "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
df.sortedBy("age")
// and add secondary sorting attributes as varargs
df.sortedBy("age", "weight")
df.sortedByDescending("age")
df.sortedBy { it["weight"].asInts() }


// Subset columns with select
df.select2 { it is IntCol } // functional style column selection
df.select("last_name", "weight")    // positive selection
df.remove("weight", "age")  // negative selection
df.select({ 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
df.summarize(
    "min_age" to { it["age"].min() },
    "max_age" to { it["age"].max() }
)

// for sake of r and python adoptability you can also use `=` here
df.summarize(
    "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
sumDF.ungroup().print()

// generate object bindings for kotlin.
// Unfortunately the syntax is a bit odd since we can not access the variable name by reflection
sumDF.printDataClassSchema("Person")

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

val records = sumDF.rows.map { row -> Person(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
records.first().mean_weight

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

// 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) }

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).

  • Krangl User Guide for detailed information about the API and usage examples.
  • API Docs for detailed information about the API including manu usage examples
  • TBD krangl Cheat Sheet

Another great introduction into data-science with kotlin was presented at 2019's KotlinConf by Roman Belov from JetBrains.

How to contribute?

Feel welcome to post ideas, suggestions and criticism to our tracker.

We always welcome pull requests. :-)

You could also show your spiritual support by upvoting krangl here on github.

Also see

  • Developer Information with technical notes & details about to build, test, release and improve krangl
  • Roadmap complementing the tracker with a backlog

Also, there are a few issues in the IDE itself which limit the applicability/usability of krangl, So, you may want to vote for

  • KT-24789 "Unresolved reference" when running a script which is a symlink to a script outside of source roots
  • KT-12583 IDE REPL should run in project root directory
  • KT-11409 Allow to "Send Selection To Kotlin Console"
  • KT-13319 Support ":paste" for pasting multi-line expressions in REPL
  • KT-21224 REPL output is not aligned with input
Comments
  • Adding Rows: Clearer/More Intuitive API?

    Adding Rows: Clearer/More Intuitive API?

    I posted this in the gitter chat, but it seemed quite dead.

    I have a relatively simple question -- what is the simplest way to append rows to DataFrames? I saw issue #51 which is highly relevant, but I wasn't particularly satisfied with the answer.

    In short what I'm trying to do: I want Pandas' append functionality: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html or dplyr's newer add_row functionality.

    I understand DataFrames are immutable, so I'd ultimately be creating a new DataFrame in the end. However, the only way I've seen so far to create such a DataFrame is to create a row iterable, and feed that into another dataFrameOf like so:

    val frame: DataFrame = dataFrameOf("Col1", "Col2") (
        "A", "B"
    )
    
    val newFrame: DataFrame = dataFrameOf(
        frame.rows + sequenceOf(mapOf(Pair("Col1", "C"), Pair("Col2", "D")))
    )
    /**
     * A DataFrame: 2 x 2
     *     Col1   Col2
     * 1      A      B
     * 2      C      D
    */
    

    This seems particularly ugly, why not have an addRow(row: DataFrameRow) or addRows(row: Iterable<DataFrameRow>) method? Just be extremely explicit that it conducts a full copy.

    At the very least, explicitly including how to add rows into the documentation would be really helpful.

    enhancement 
    opened by CrystalSplitter 13
  • How to append a total row

    How to append a total row

    What's the best way to total a column? Say you have a df like this:

    | Name  | Duration | Color  |
    -----------------------------
    | Foo   | 100      | Blue   |
    | Goo   | 200      | Red    |
    | Bar   | 300      | Yellow |
    

    I don't see a sum() or total() method on DataCol - only mean, min, etc. I can total the column myself like so: val total = df["duration"].asInts().sumBy { it -> it!! } but how to I append this to the data frame to end up with this:

    | Name  | Duration | Color  |
    -----------------------------
    | Foo   | 100      | Blue   |
    | Goo   | 200      | Red    |
    | Bar   | 300      | Yellow |
    | Total | 600      |        |
    
    help wanted 
    opened by cgrinds 10
  • Nice to have `toMap` function in DataFrame

    Nice to have `toMap` function in DataFrame

    This code works for me:

    val mpg_df = DataFrame.readCSV("https://jetbrains.bintray.com/lets-plot/mpg.csv")
    
    val dat = (mpg_df.names.filter {it.isNotBlank()}.map { Pair(it, mpg_df.get(it).values())}).toMap()
    

    but would be nice to just use:

    mpg_df.toMap()
    
    question 
    opened by alshan 8
  • Added Excel read write functions

    Added Excel read write functions

    #58 I've added excel read/write functions and their respective tests. (Plus two files, one for data and another generated by the test) I've also had to add a dependency to apache poi

    For now I kept excel values as strings, as Excel is a bit tricky with the way it stores its data (and their typing), this way we can read a data with the displayed value instead of excel's internal format (same for numbers where the .0 would be added).

    I've been using these a lot on a project but since I've made some changes/refactoring in order to contribute they don't have all the live testing their previous versions had (although I still tested on my projects and a few variations to be sure)

    Please let me know your feedback on this

    Thanks and best regards

    enhancement 
    opened by LeandroC89 7
  • Consider more efficient csv parsers

    Consider more efficient csv parsers

    I believe Krangl is useful for big datasets processing. So parsing huge csv files may be a bottle neck. Based on this resource https://github.com/uniVocity/csv-parsers-comparison commons-csv is one of the slowest parsers in the world :) Maybe it's make sense to migrate to more efficient lib like Univocity?

    opened by mykola-dev 6
  • Add cleanNames function!

    Add cleanNames function!

    Hey, I'm not sure about how contributed to this repository, but is a really good idea to add the equivalent janitor clean_names function. Here you have a start idea about this, but I need to read the suggested way to created Kotlin funs and check the rest of the functionalities that have the janitor function.

    /** Replace current column names with standard lowercased names taking initial names as the base
     * TODO: remove leading and trailing spaces
     * TODO: remove special characters
     * */
    fun DataFrame.cleanNames(): DataFrame {
        var df = this
        var newNames = this.names
        newNames = newNames
            .map { it ->
                it
                    .toLowerCase()
                    .replace(" ", "_")
            }
        var names = this.names.zip(newNames).map { (old, new) -> old to new }.toTypedArray()
        names.map{
            df = df.rename(it)
        }
        return df
    }
    
    opened by carloseguevara 5
  • Fill implementation #61

    Fill implementation #61

    • Implements the fill function.
    • Like in https://tidyr.tidyverse.org/reference/fill.html you can fill values down, up, updown, downup.
    • I add a functionality from https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html : you can fill holes with a given value (not the last value) or a dataframe (pick a value at the same place that original value).
    • Closes #61
    opened by OlivierCavadenti 4
  • - Added option to show a different number of records in Jupyter

    - Added option to show a different number of records in Jupyter

    Added the versatility to allow a different number of records to be displayed in Jupyter Notebooks (Previously locked at 6)

    I'm unable to test it here but I did a POC in Jupyter Notebooks with the following code

    (toHTML() was copied)

    fun DataFrame.toHTML(title: String="A DataFrame", maxRows: Int = 10, truncate: Int = 50): String = with(StringBuilder()) {
            append("<html><body>")
    
    
    
            append("<table><tr>")
    
            cols.forEach { append("""<th style="text-align:left">${it.name}</th>""") }
            append("</tr>")
    
            rows.take(maxRows).forEach {
                append("<tr>")
                it.values.map { it.toString() }.forEach {
                    val truncated = if (truncate > 0 && it.length > truncate) {
                        if (truncate < 4) it.substring(0, truncate)
                        else it.substring(0, truncate - 3) + "..."
                    } else {
                        it
                    }
                    append("""<td style="text-align:left" title="$it">$truncated</td>""")
                }
                append("</tr>")
            }
    
            append("</table>")
    
            // render footer
            append("<p>")
            if (maxRows < rows.count()){
                append("... with ${nrow - maxRows} more rows. ")
            }
    
            appendLine("Shape: ${nrow} x ${ncol}. ")
    
    /*        if (this@toHTML is GroupedDataFrame) {
                appendLine("Grouped by ${by.joinToString()} [${groups.size}]")
            } */
            append("</p>")
    
    
            append("</body></html>")
        }.toString()
    
    fun DataFrame.display(maxRows: Int = 10, allRecords: Boolean = false): MimeTypedResult{
        val nrRecords = if(allRecords) this.nrow else maxRows
        return HTML(this.toHTML(maxRows = nrRecords))
    }
    

    And then calling df.display() and play around with it.

    What I see as a possible point of failure is the variable visibility, I put DISPLAY_MAX_ROWS outside the class hoping it would be accessible as the PRINT_MAX_ROWS variable. Let me know if you think it makes sense.

    Reasoning: I use krangl for work on an almost daily basis, and was previously locked to 20 rows only, the recent change made it 6 rows. Sometimes being able to see the whole DF is very helpful to retrieve a whole list of something or performing some eye checks.

    Took me a few hours to understand how Jupyter worked so I could come up with the workaround I mentioned.

    Thanks

    opened by LeandroC89 4
  • How to use a specific krangl version in Jupiter with Kotlin kernel?

    How to use a specific krangl version in Jupiter with Kotlin kernel?

    %use krangl(0.15.7)
    

    results in error:

    Failed to resolve com.github.holgerbrandl:krangl:0.15.7: File 'com.github.holgerbrandl:krangl:0.15.7' not found unresolved dependency: com.github.holgerbrandl#krangl;0.15.7: not found

    opened by alshan 4
  • Added addRow function to DataFrame(Returns a DataFrame with old rows …

    Added addRow function to DataFrame(Returns a DataFrame with old rows …

    Added function to add row (non-destructive). I based the logic on the example provided here https://www.gitmemory.com/issue/holgerbrandl/krangl/76/525598794 with added logic.

    It is useful for data transformation & validation, and it is what I'm using on my excel sheet reading function too.

    PS: This library is awesome, nice work.

    opened by LeandroC89 4
  • Support of Long type columns

    Support of Long type columns

    Hi.

    I'm having problems when trying to work with dataframes created from a CSV that has a column which contains Long values (that is, some values wouldn't fit in an Int).

    An easy way to reproduce the issue could be to edit the file src/test/resources/krangl/data/test_header_types.csv and change the following line: a,1,1,10,TRUE to a,1,1,1000000000000000,TRUE

    This would break the test it should have the correct column types. The automatic type detection would assign it to string, since it doesn't really know of a more specific type. That doesn't work for my use case.

    Actually, I'd rather specify the column types when creating the dataframe, but if I take a look to the enumeration ColType I can see there is no Long option:

    enum class ColType { Int, Double, Boolean, String, Guess }

    Moreover, if I have a look to the subclasses of DataCol (Columns.kt) I can see there is no class for longs, so it seems the changes to acomplish it could be many.

    Any workaround that comes to mind?

    Thanks.

    enhancement 
    opened by davidpedrosa 4
  • Data Visualization Example with lets-plot does not work.

    Data Visualization Example with lets-plot does not work.

    Hi,

    I would like to do some data visualization using krangl and lets-plot. I tried to get the example working. For this I created a new jupyter-lab notebook with a single section containing the following code from the example:

    %use krangl(0.17)
    %use lets-plot
    
    var sleepDataExt = sleepData.addColumn("rem_proportion"){it["sleep_rem"]/it["sleep_total"]}
    sleepDataExt.letsPlot {x="sleep_total"; y="rem_proportion" } + geomPoint()
    

    Upon execution I get an error message stating: Line_58.jupyter-kts (5:64 - 75) Type mismatch: inferred type is geomPoint but Feature was expected

    Did I do something wrong? Is this a known bug? Is there a workaround?

    Thanks and regards

    opened by muthenberg 6
  • Type conversion with NA or null instead of throwing errors

    Type conversion with NA or null instead of throwing errors

    Hi,

    when reading data from excel or converting to different types from a dataframe, invalid data throws an error instead of being converted to NA or null. Maybe change this behaviour or add functions that have different behaviours like toDoubleOrNull() or toDoubleOrNa().

    Thanks!

    opened by Burtan 0
  • Not parsing embedded json arrays

    Not parsing embedded json arrays

    Screen Shot 2022-06-24 at 10 59 56 AM

    I was parsing a file but I simplified to a string for the example. When reading the file I got a somewhat different casting exception from JsonArray to JsonObject. Here is the full stacktrace from the screenshot:

    class java.lang.String cannot be cast to class com.beust.klaxon.JsonObject (java.lang.String is in module java.base of loader 'bootstrap'; com.beust.klaxon.JsonObject is in unnamed module of loader java.net.URLClassLoader @64d1cb43)
    java.lang.ClassCastException: class java.lang.String cannot be cast to class com.beust.klaxon.JsonObject (java.lang.String is in module java.base of loader 'bootstrap'; com.beust.klaxon.JsonObject is in unnamed module of loader java.net.URLClassLoader @64d1cb43)
    	at krangl.JsonIOKt.fromJsonArray(JsonIO.kt:149)
    	at krangl.JsonIOKt.fromJsonString(JsonIO.kt:50)
    	at Line_231.<init>(Line_231.jupyter-kts:14)
    	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    	at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.evalWithConfigAndOtherScriptsResults(BasicJvmScriptEvaluator.kt:100)
    	at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke$suspendImpl(BasicJvmScriptEvaluator.kt:47)
    	at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke(BasicJvmScriptEvaluator.kt)
    	at kotlin.script.experimental.jvm.BasicJvmReplEvaluator.eval(BasicJvmReplEvaluator.kt:49)
    	at org.jetbrains.kotlinx.jupyter.repl.impl.InternalEvaluatorImpl$eval$resultWithDiagnostics$1.invokeSuspend(InternalEvaluatorImpl.kt:103)
    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    	at org.jetbrains.kotlinx.jupyter.repl.impl.InternalEvaluatorImpl.eval(InternalEvaluatorImpl.kt:103)
    	at org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl$execute$1$result$1.invoke(CellExecutorImpl.kt:70)
    	at org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl$execute$1$result$1.invoke(CellExecutorImpl.kt:68)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl.withHost(repl.kt:642)
    	at org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl.execute(CellExecutorImpl.kt:68)
    	at org.jetbrains.kotlinx.jupyter.repl.CellExecutor$DefaultImpls.execute$default(CellExecutor.kt:14)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl$evalEx$1.invoke(repl.kt:451)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl$evalEx$1.invoke(repl.kt:440)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl.withEvalContext(repl.kt:404)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl.evalEx(repl.kt:440)
    	at org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl.eval(repl.kt:492)
    	at org.jetbrains.kotlinx.jupyter.messaging.ProtocolKt$shellMessagesHandler$res$1.invoke(protocol.kt:301)
    	at org.jetbrains.kotlinx.jupyter.messaging.ProtocolKt$shellMessagesHandler$res$1.invoke(protocol.kt:300)
    	at org.jetbrains.kotlinx.jupyter.JupyterConnection$runExecution$execThread$1.invoke(connection.kt:180)
    	at org.jetbrains.kotlinx.jupyter.JupyterConnection$runExecution$execThread$1.invoke(connection.kt:178)
    	at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
    	```
    opened by iamsteveholmes 1
  • Improve error message for unnest with empty list

    Improve error message for unnest with empty list

    Reduced test case: dataFrameOf("a", "b")(listOf(), 1).unnest("a") fails with "Provided data does not coerce to tabular shape"

    It took me a while to figure out from a larger dataset that the empty list was the problem. I guess unnest could also be defined to unnest to zero rows in this case, but maybe that is too magical.

    opened by ablaagaard 1
  • In Jupyter notebook: can't use a desired version of lets-plot when also using krangl.

    In Jupyter notebook: can't use a desired version of lets-plot when also using krangl.

    In my notebook I'm using both, lets-plot and krangl libraries.

    I've specified a particular version of lets-plot but noticed that notebook loads lets-plot' class-files that belong to a different lets-plot version.

    For example, in the version I wanted to use, function ggtitle() has (new) parameter subtitle. However, when I'm trying to use this function I'm getting an error:

    image

    The issue occurs it seems due to lets-plot dependency in krangl maven artifact:

        <dependency>
          <groupId>org.jetbrains.lets-plot</groupId>
          <artifactId>lets-plot-kotlin-jvm</artifactId>
          <version>3.0.1</version>
          <scope>compile</scope>
        </dependency>
    

    https://search.maven.org/artifact/com.github.holgerbrandl/krangl/0.17/jar


    Would you consider building another krangl artifact - without this dependency, to be used specifically in Jupyter/Datalore notebooks with Jupyter Kotlin Kernel?

    opened by alshan 1
  • Reading

    Reading "big" csv files

    Hello, I increased the heapsize to 16G for my kernel with krangl, but reading a CSV file which has 7 columns (int64, str, str, str, int64, str, str) and about 4*e6 rows (almost 800M with utf8 encoding) didn't quite work.
    I am stuck with heap size error, Python's pandas was able to load it without much trouble. Unfortunately I face a task where I have to iterate through this table row by row, and Python's loops are not quite useful here (I mean they work, but it not takes a couple of hours to work through that).
    Julia's DataFrame.jl was able to load this frame into memory as well (it really is not that big, takes around 2G of RAM on a Windows machine).

    opened by nlhnt 2
Releases(v0.18.4)
Owner
Holger Brandl
machine learning & data science enthusiast.
Holger Brandl
Exposed spring integration and code generator for dsl interface

Infra-ORM 欢迎使用 Infra-ORM, 这是一个基于 Exposed 的 ORM 框架,可以和 Spring Boot 集成良好,如果你是 Kotlin 开发者,推荐你试试 Exposed, 配合 Infra-ORM 可以给你带来最佳的开发体验。 为什么造这个轮子? Exposed 提供

Red Sparrow 1 Jan 2, 2022
Core Data for Android

NexusData Core Data for Android NexusData is an object graph and persistence framework for Android. It allows for organizing and managing relational d

Dia Kharrat 71 Nov 11, 2022
Implementation of MVVM , Live Data and Room DAO for a robust materialistic design

Someday App to manage Weekly tasks Because who needs to remind you every week to do Samething Preview Main Layout Light Dark Main Layout (Expanded) Li

Anshul Saraf 2 May 13, 2021
A tool to convert & query Apache Calcite data sources as GraphQL API's

Apache Calcite <-> Distributed, Federated GraphQL API Apache Calcite <-> Distributed, Federated GraphQL API Goals Roadmap and Current Progress The Roa

Gavin Ray 19 Nov 22, 2022
Kotlin-client-dsl - A kotlin-based dsl project for a (Client) -> (Plugin) styled program

kotlin-client-dsl a kotlin-based dsl project for a (Client) -> (Plugin) styled p

jackson 3 Dec 10, 2022
Tired of manually setup test data of Kotlin data classes or POJOs? Instantiator creates Instances of any class for you so that you can focus on writing tests instead of spending time and effort to setup test data

Instantiator Tired of manually setup test data of Kotlin data classes or POJOs? Instantiator creates Instances of any class for you so that you can fo

Hannes Dorfmann 54 Dec 30, 2022
The most complete and powerful data-binding library and persistence infra for Kotlin 1.3, Android & Splitties Views DSL, JavaFX & TornadoFX, JSON, JDBC & SQLite, SharedPreferences.

Lychee (ex. reactive-properties) Lychee is a library to rule all the data. ToC Approach to declaring data Properties Other data-binding libraries Prop

Mike 112 Dec 9, 2022
The most complete and powerful data-binding library and persistence infra for Kotlin 1.3, Android & Splitties Views DSL, JavaFX & TornadoFX, JSON, JDBC & SQLite, SharedPreferences.

Lychee (ex. reactive-properties) Lychee is a library to rule all the data. ToC Approach to declaring data Properties Other data-binding libraries Prop

Mike 112 Dec 9, 2022
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.6k Dec 29, 2022
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.6k Jan 8, 2023
Don't write a ViewPager Adapter! Hook up your ViewPager to your data model using Android Data Binding Framework. With Kotlin support!

Don't write a ViewPager Adapter! Hook up your ViewPager to your data model using Android Data Binding Framework. Show some ❤️ ?? Sweet and short libra

Rakshak R.Hegde 180 Nov 18, 2022
FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

null 33 Dec 8, 2022
This application features - Modern Minimalistic Design, MVVM, Pagination, Hilt, Retrofit, Room, Data Store, Flow, Live Data, Navigation Component (Clean Architecture)

NewsFly NewsFly is a modern news android application which features virtually ALL recent and recommended android development tech stack and tools used

Ibrahim 121 Nov 4, 2022
Demo Spting REST Service on Kotlin. Works with PostgreSQL via Spring Data. Data initialization provided by liquibase

Spring Boot REST API with Kotlin Spring Boot REST API service. Spring Data with PostgreSQL. Data initialization with Liquibase. Swagger UI Reference D

null 0 Jun 10, 2022
Detailing about the data provided (Data Visualization Application)

Detailing about the data provided (Data Visualization Application): • In the application, the data provided in the CSV is used for the Scatter plot cr

Neha Sharma 0 Nov 20, 2021
Use Android Data Binding wih Live Data to glue View Model and Android

Gruop-C Spliff Summary Use Android Data Binding wih Live Data to glue View Model and Android. Asynchronous communications implemented with KotlinX Cor

null 2 Nov 21, 2021
MiHawk 🦅👁️ is simple and secure 🔒 Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack DataStore Preferences 💽 to store data.

MiHawk MiHawk ?? ??️ is simple and secure ?? Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack

Nedal Hasan Ibrahem 5 Sep 3, 2022
A basic application demonstrating IPFS for collaborative data analysis, from the perspective of a Data Analysis Provider.

Spacebox A basic application demonstrating IPFS for collaborative data analysis, from the perspective of a Data Analysis Provider. Description This pr

null 0 Jan 15, 2022