From 6cde7b930d1287673f95a94e9fd24c678dc23031 Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Fri, 27 Feb 2026 14:20:51 +0200 Subject: [PATCH 1/6] Add DataFrame.require API for typed selector validation --- core/api/core.api | 4 +++ .../kotlinx/dataframe/api/require.kt | 17 ++++++++++ .../kotlinx/dataframe/impl/api/require.kt | 18 ++++++++++ .../kotlinx/dataframe/api/require.kt | 33 +++++++++++++++++++ docs/StardustDocs/d.tree | 1 + docs/StardustDocs/topics/require.md | 19 +++++++++++ 6 files changed, 92 insertions(+) create mode 100644 core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt create mode 100644 core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/require.kt create mode 100644 core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt create mode 100644 docs/StardustDocs/topics/require.md diff --git a/core/api/core.api b/core/api/core.api index ddeee8f3c2..938b45a534 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -5492,6 +5492,10 @@ public final class org/jetbrains/kotlinx/dataframe/impl/api/MapKt { public static final fun mapNotNullValues (Lorg/jetbrains/kotlinx/dataframe/DataColumn;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlinx/dataframe/DataColumn; } +public final class org/jetbrains/kotlinx/dataframe/impl/api/RequireKt { + public static final fun requireImpl (Lorg/jetbrains/kotlinx/dataframe/DataFrame;Lkotlin/jvm/functions/Function2;Lkotlin/reflect/KType;)Lorg/jetbrains/kotlinx/dataframe/DataFrame; +} + public final class org/jetbrains/kotlinx/dataframe/impl/api/SchemaKt { public static final fun compileTimeSchemaImpl (Lorg/jetbrains/kotlinx/dataframe/schema/DataFrameSchema;Lkotlin/reflect/KClass;)Lorg/jetbrains/kotlinx/dataframe/schema/DataFrameSchema; } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt new file mode 100644 index 0000000000..8ecb0f724f --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlinx.dataframe.api + +import org.jetbrains.kotlinx.dataframe.ColumnSelector +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.Interpretable +import org.jetbrains.kotlinx.dataframe.annotations.Refine +import org.jetbrains.kotlinx.dataframe.impl.api.requireImpl +import kotlin.reflect.typeOf + +/** + * Resolves [column] in this [DataFrame] and checks that its runtime type is a subtype of [C]. + * Throws if the column can't be resolved or if its type doesn't match. + */ +@Refine +@Interpretable("Require0") +public inline fun DataFrame.require(noinline column: ColumnSelector): DataFrame = + requireImpl(column, typeOf()) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/require.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/require.kt new file mode 100644 index 0000000000..dcf4f86a97 --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/require.kt @@ -0,0 +1,18 @@ +package org.jetbrains.kotlinx.dataframe.impl.api + +import org.jetbrains.kotlinx.dataframe.ColumnSelector +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.api.getColumnWithPath +import org.jetbrains.kotlinx.dataframe.api.isSubtypeOf +import org.jetbrains.kotlinx.dataframe.type +import kotlin.reflect.KType + +@PublishedApi +internal fun DataFrame.requireImpl(column: ColumnSelector, type: KType): DataFrame { + val resolvedColumn = getColumnWithPath(column) + val actualType = resolvedColumn.data.type + require(resolvedColumn.data.isSubtypeOf(type)) { + "Column '${resolvedColumn.path.joinToString()}' has type '$actualType', which is not subtype of required '$type' type." + } + return this +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt new file mode 100644 index 0000000000..26402c6b70 --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -0,0 +1,33 @@ +package org.jetbrains.kotlinx.dataframe.api + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import org.junit.Test + +class RequireTests : ColumnsSelectionDslTests() { + + @Test + fun `require returns same dataframe for existing typed column`() { + val checked = df.require { "name"["firstName"]() } + checked shouldBe df + } + + @Test + fun `require throws on type mismatch`() { + val throwable = shouldThrow { + df.require { "name"["firstName"]() } + } + throwable.message shouldBe + "Column 'name/firstName' has type 'kotlin.String', which is not subtype of required 'kotlin.Int' type." + } + + @Test + fun `require throws when column cannot be resolved`() { + val exception = shouldThrowAny { + df.require { "name"["unknown"]() } + } + println(exception) + } +} diff --git a/docs/StardustDocs/d.tree b/docs/StardustDocs/d.tree index d8622bda67..327a651020 100644 --- a/docs/StardustDocs/d.tree +++ b/docs/StardustDocs/d.tree @@ -132,6 +132,7 @@ + diff --git a/docs/StardustDocs/topics/require.md b/docs/StardustDocs/topics/require.md new file mode 100644 index 0000000000..935c05079e --- /dev/null +++ b/docs/StardustDocs/topics/require.md @@ -0,0 +1,19 @@ +[//]: # (title: require) + + +Throws an exception if the specified column is missing or its type is not subtype of `C`. +From the compiler plugin perspective, a new column will appear in the compile-time schema as a result of this operation. +The aim here is to help incrementally migrate workflows to extension properties API. + +Will work in compiler plugin starting from IntelliJ IDEA 2026.2 and Kotlin 2.4.0. + +```text +require { column } +``` + +**Related operations**: [](cast.md), [](convertTo) + +```kotlin +val df = peopleDf.require { "name"["firstName"]() } +val v: String = df.name.firstName[0] +``` From 4557153085b5d80a0a73f4894665ee665554ecbf Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Fri, 27 Feb 2026 15:45:41 +0200 Subject: [PATCH 2/6] Improve missing column error message in CS DSL --- .../dataframe/impl/DataFrameReceiver.kt | 43 +++++++++++++++++-- .../kotlinx/dataframe/api/require.kt | 22 +++++++++- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameReceiver.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameReceiver.kt index f85095bd87..4784d2f9fd 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameReceiver.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameReceiver.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlinx.dataframe.api.asDataColumn import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.isColumnGroup import org.jetbrains.kotlinx.dataframe.api.pathOf +import org.jetbrains.kotlinx.dataframe.api.toPath import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup import org.jetbrains.kotlinx.dataframe.columns.ColumnPath import org.jetbrains.kotlinx.dataframe.columns.ColumnReference @@ -21,6 +22,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.addPath import org.jetbrains.kotlinx.dataframe.impl.columns.missing.MissingColumnGroup import org.jetbrains.kotlinx.dataframe.impl.columns.missing.MissingDataColumn import org.jetbrains.kotlinx.dataframe.nrow +import kotlin.collections.map private fun DataFrame.unbox(): DataFrame = when (this) { @@ -47,9 +49,7 @@ internal open class DataFrameReceiver( host = this@DataFrameReceiver, ).asDataColumn().cast() - UnresolvedColumnsPolicy.Fail -> error( - "Column '${path.joinToString()}' not found among ${df.columnNames()}.", - ) + UnresolvedColumnsPolicy.Fail -> error(formatMissingColumnMessage(path)) } is MissingDataColumn -> this @@ -59,6 +59,43 @@ internal open class DataFrameReceiver( else -> this } + // Context: + // it's strange that we have to reverse-search why the column is missing + // would be nice to "fail fast" exactly where resolve failed, knowing the current path and parent. + // but it's very unclear what to do with resolveSingle. + // at first glance: a lot of changes. + @Suppress("FoldInitializerAndIfToElvis") + private fun formatMissingColumnMessage(path: ColumnPath): String { + val fullPath = path.joinToString() + + for (depth in path.indices) { + val currentPath = path.slice(0..depth).toPath() + val currentPathString = currentPath.joinToString() + val column = df.getColumnOrNull(currentPath) + if (column == null) { + return if (depth == 0) { + "Column '$currentPathString' not found among ${df.columnNames()}." + } else { + val parentPath = currentPath.dropLast() + val parentPathString = parentPath.joinToString() + val parentColumn = df.getColumnOrNull(parentPath) + if (parentColumn != null && parentColumn.isColumnGroup()) { + "Column '$currentPathString' not found among columns of '$parentPathString': ${parentColumn.columnNames()}." + } else { + "Column '$currentPathString' not found among ${df.columnNames()}." + } + } + } + + if (depth != path.lastIndex) { + if (!column.isColumnGroup()) { + return "Column '$fullPath' cannot be resolved: '$currentPathString' is not a column group." + } + } + } + return "Column '$fullPath' not found among ${df.columnNames()}." + } + override fun getColumnOrNull(name: String) = df.getColumnOrNull(name).check(pathOf(name)) override fun getColumnOrNull(index: Int) = df.getColumnOrNull(index).check(pathOf("")) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt index 26402c6b70..2bf170959e 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -3,7 +3,6 @@ package org.jetbrains.kotlinx.dataframe.api import io.kotest.assertions.throwables.shouldThrow import io.kotest.assertions.throwables.shouldThrowAny import io.kotest.matchers.shouldBe -import io.kotest.matchers.string.shouldContain import org.junit.Test class RequireTests : ColumnsSelectionDslTests() { @@ -28,6 +27,25 @@ class RequireTests : ColumnsSelectionDslTests() { val exception = shouldThrowAny { df.require { "name"["unknown"]() } } - println(exception) + exception.message shouldBe + "Column 'name/unknown' not found among columns of 'name': [firstName, lastName]." + } + + @Test + fun `require missing parent message includes available columns`() { + val exception = shouldThrowAny { + df.require { "name2"["unknown"]() } + } + exception.message shouldBe + "Column 'name2' not found among [name, age, city, weight, isHappy]." + } + + @Test + fun `require deep missing parent message uses nearest existing ancestor`() { + val exception = shouldThrowAny { + df.require { "name"["unknownGroup"]["value"]() } + } + exception.message shouldBe + "Column 'name/unknownGroup' not found among columns of 'name': [firstName, lastName]." } } From 0329c9c2ee21b7c537a8f279baf4804a15d25465 Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Mon, 2 Mar 2026 18:35:23 +0200 Subject: [PATCH 3/6] Update require.md --- docs/StardustDocs/topics/require.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/StardustDocs/topics/require.md b/docs/StardustDocs/topics/require.md index 935c05079e..8f1d14f497 100644 --- a/docs/StardustDocs/topics/require.md +++ b/docs/StardustDocs/topics/require.md @@ -3,7 +3,7 @@ Throws an exception if the specified column is missing or its type is not subtype of `C`. From the compiler plugin perspective, a new column will appear in the compile-time schema as a result of this operation. -The aim here is to help incrementally migrate workflows to extension properties API. +The aim here is to help incrementally migrate workflows to [extension properties API](extensionPropertiesApi.md). Will work in compiler plugin starting from IntelliJ IDEA 2026.2 and Kotlin 2.4.0. @@ -14,6 +14,11 @@ require { column } **Related operations**: [](cast.md), [](convertTo) ```kotlin +// Before `require` extension property will not be resolved +// peopleDf.select { name.firstName } + +// Require a column with a runtime check val df = peopleDf.require { "name"["firstName"]() } +// Use extension property after `require` val v: String = df.name.firstName[0] ``` From af2d0e34b5f46b510e3347752d95963f270ead36 Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Mon, 2 Mar 2026 18:35:50 +0200 Subject: [PATCH 4/6] Rename require -> requireColumn --- .../org/jetbrains/kotlinx/dataframe/api/require.kt | 2 +- .../org/jetbrains/kotlinx/dataframe/api/require.kt | 10 +++++----- docs/StardustDocs/topics/require.md | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt index 8ecb0f724f..ce0e34d250 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -13,5 +13,5 @@ import kotlin.reflect.typeOf */ @Refine @Interpretable("Require0") -public inline fun DataFrame.require(noinline column: ColumnSelector): DataFrame = +public inline fun DataFrame.requireColumn(noinline column: ColumnSelector): DataFrame = requireImpl(column, typeOf()) diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt index 2bf170959e..248eb5b923 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -9,14 +9,14 @@ class RequireTests : ColumnsSelectionDslTests() { @Test fun `require returns same dataframe for existing typed column`() { - val checked = df.require { "name"["firstName"]() } + val checked = df.requireColumn { "name"["firstName"]() } checked shouldBe df } @Test fun `require throws on type mismatch`() { val throwable = shouldThrow { - df.require { "name"["firstName"]() } + df.requireColumn { "name"["firstName"]() } } throwable.message shouldBe "Column 'name/firstName' has type 'kotlin.String', which is not subtype of required 'kotlin.Int' type." @@ -25,7 +25,7 @@ class RequireTests : ColumnsSelectionDslTests() { @Test fun `require throws when column cannot be resolved`() { val exception = shouldThrowAny { - df.require { "name"["unknown"]() } + df.requireColumn { "name"["unknown"]() } } exception.message shouldBe "Column 'name/unknown' not found among columns of 'name': [firstName, lastName]." @@ -34,7 +34,7 @@ class RequireTests : ColumnsSelectionDslTests() { @Test fun `require missing parent message includes available columns`() { val exception = shouldThrowAny { - df.require { "name2"["unknown"]() } + df.requireColumn { "name2"["unknown"]() } } exception.message shouldBe "Column 'name2' not found among [name, age, city, weight, isHappy]." @@ -43,7 +43,7 @@ class RequireTests : ColumnsSelectionDslTests() { @Test fun `require deep missing parent message uses nearest existing ancestor`() { val exception = shouldThrowAny { - df.require { "name"["unknownGroup"]["value"]() } + df.requireColumn { "name"["unknownGroup"]["value"]() } } exception.message shouldBe "Column 'name/unknownGroup' not found among columns of 'name': [firstName, lastName]." diff --git a/docs/StardustDocs/topics/require.md b/docs/StardustDocs/topics/require.md index 8f1d14f497..160be819cc 100644 --- a/docs/StardustDocs/topics/require.md +++ b/docs/StardustDocs/topics/require.md @@ -1,4 +1,4 @@ -[//]: # (title: require) +[//]: # (title: requireColumn) Throws an exception if the specified column is missing or its type is not subtype of `C`. @@ -8,17 +8,17 @@ The aim here is to help incrementally migrate workflows to [extension properties Will work in compiler plugin starting from IntelliJ IDEA 2026.2 and Kotlin 2.4.0. ```text -require { column } +requireColumn { column } ``` **Related operations**: [](cast.md), [](convertTo) ```kotlin -// Before `require` extension property will not be resolved +// Before `requireColumn` extension property will not be resolved // peopleDf.select { name.firstName } // Require a column with a runtime check -val df = peopleDf.require { "name"["firstName"]() } -// Use extension property after `require` +val df = peopleDf.requireColumn { "name"["firstName"]() } +// Use extension property after `requireColumn` val v: String = df.name.firstName[0] ``` From d5c55d77d8e77958dea574e2044fe43745a43b03 Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Mon, 2 Mar 2026 18:53:04 +0200 Subject: [PATCH 5/6] Cross-reference use cases with requireColumn in mind --- docs/StardustDocs/topics/adjustSchema.md | 1 + docs/StardustDocs/topics/require.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/StardustDocs/topics/adjustSchema.md b/docs/StardustDocs/topics/adjustSchema.md index 050fb564c3..f9eea02753 100644 --- a/docs/StardustDocs/topics/adjustSchema.md +++ b/docs/StardustDocs/topics/adjustSchema.md @@ -14,3 +14,4 @@ To match your knowledge with expected real-time [`DataFrame`](DataFrame.md) cont * [`cast`](cast.md) — change type argument of [`DataFrame`](DataFrame.md) to the expected schema without changing data in [`DataFrame`](DataFrame.md). * [`convertTo`](convertTo.md) — convert [`DataFrame`](DataFrame.md) contents to match the expected schema. +Alternatively, use [](require.md) to incrementally add type information to compile time schema. diff --git a/docs/StardustDocs/topics/require.md b/docs/StardustDocs/topics/require.md index 160be819cc..921bca8f0d 100644 --- a/docs/StardustDocs/topics/require.md +++ b/docs/StardustDocs/topics/require.md @@ -4,6 +4,7 @@ Throws an exception if the specified column is missing or its type is not subtype of `C`. From the compiler plugin perspective, a new column will appear in the compile-time schema as a result of this operation. The aim here is to help incrementally migrate workflows to [extension properties API](extensionPropertiesApi.md). +We recommend considering declaring a [DataSchema](dataSchema.md) and use [](cast.md) or [](convertTo) if you end up with more than a few `requireColumn` calls. Will work in compiler plugin starting from IntelliJ IDEA 2026.2 and Kotlin 2.4.0. From 0a70e1a79c451ea42234c35f908e451756c0a90c Mon Sep 17 00:00:00 2001 From: Nikita Klimenko Date: Mon, 20 Apr 2026 17:48:27 +0300 Subject: [PATCH 6/6] Expand documentation for requireColumn --- .../kotlinx/dataframe/api/require.kt | 46 +++++++++++++++++++ docs/StardustDocs/topics/require.md | 41 +++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt index ce0e34d250..b07c0169f8 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/require.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlinx.dataframe.api import org.jetbrains.kotlinx.dataframe.ColumnSelector import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.DataSchema import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine import org.jetbrains.kotlinx.dataframe.impl.api.requireImpl @@ -10,6 +11,51 @@ import kotlin.reflect.typeOf /** * Resolves [column] in this [DataFrame] and checks that its runtime type is a subtype of [C]. * Throws if the column can't be resolved or if its type doesn't match. + * + * From the compiler plugin perspective, a new column will appear in the compile-time schema as a result of this operation. + * + * The aim here is to help incrementally migrate workflows to extension properties API. + * + * We recommend considering declaring a [DataSchema] and use [cast] or [convertTo] if you end up with more than a few `requireColumn` calls. + * + * Example: + * + * ```kotlin + * val repos = DataFrame + * .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + * + * repos + * .filter { "stargazers_count"() > 100 } + * .sortByDesc("stargazers_count") + * .select("full_name", "stargazers_count") + * ``` + * + * Notice how `stargazers_count` String is repeated three times. We can refactor this code using `requireColumn`: + * + * ``` + * val repos = DataFrame + * .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + * .requireColumn { "stargazers_count"() } + * + * repos + * .filter { stargazers_count > 100 } + * .sortByDesc { stargazers_count } + * .select { "full_name" and stargazers_count } + * ``` + * + * This way code becomes a bit more robust. For example, usages of a renamed column will become compile time errors that are easy to spot and update: + * ```kotlin + * val repos = DataFrame + * .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + * .requireColumn { "stargazers_count"() } + * .rename { stargazers_count }.into("stars") + * + * repos + * .filter { stars > 100 } + * .sortByDesc { stars } + * .select { "full_name" and stars } + * ``` + * */ @Refine @Interpretable("Require0") diff --git a/docs/StardustDocs/topics/require.md b/docs/StardustDocs/topics/require.md index 921bca8f0d..dedc7f40af 100644 --- a/docs/StardustDocs/topics/require.md +++ b/docs/StardustDocs/topics/require.md @@ -23,3 +23,44 @@ val df = peopleDf.requireColumn { "name"["firstName"]() } // Use extension property after `requireColumn` val v: String = df.name.firstName[0] ``` + +### Advanced example + +Let's start with a pipeline that uses only String Column Accessors and String API overloads: + +```kotlin +val repos = DataFrame + .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + +repos + .filter { "stargazers_count"() > 100 } + .sortByDesc("stargazers_count") + .select("full_name", "stargazers_count") +``` + +Notice how stargazers_count String is repeated three times. We can refactor this code using `requireColumn`: + +```kotlin +val repos = DataFrame + .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + .requireColumn { "stargazers_count"() } + +repos + .filter { stargazers_count > 100 } + .sortByDesc { stargazers_count } + .select { "full_name" and stargazers_count } +``` + +This way code becomes a bit more robust. For example, usages of a renamed column will become compile time errors that are easy to spot and update: + +```kotlin +val repos = DataFrame + .readCsv("https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv") + .requireColumn { "stargazers_count"() } + .rename { stargazers_count }.into("stars") + +repos + .filter { stars > 100 } + .sortByDesc { stars } + .select { "full_name" and stars } +```