diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/CandidateForRemoval.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/CandidateForRemoval.kt deleted file mode 100644 index 5a7f4d3874..0000000000 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/annotations/CandidateForRemoval.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.kotlinx.dataframe.annotations - -/** - * Functions that can be replaced with other API, such as shortcuts without clean value, - * or something not properly designed, and so will be considered to be removed from API. - * If you see a function marked with it and think it should be kept, please let us know in the GitHub issue: - * https://github.com/Kotlin/dataframe/issues/1028 - */ -internal annotation class CandidateForRemoval diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt index 51e853be21..93bf39eb78 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/DataRowApi.kt @@ -8,7 +8,6 @@ import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.RowExpression import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload -import org.jetbrains.kotlinx.dataframe.annotations.CandidateForRemoval import org.jetbrains.kotlinx.dataframe.annotations.DataSchema import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.impl.columnName @@ -18,14 +17,22 @@ import org.jetbrains.kotlinx.dataframe.indices import org.jetbrains.kotlinx.dataframe.ncol import org.jetbrains.kotlinx.dataframe.nrow import org.jetbrains.kotlinx.dataframe.util.DEPRECATED_ACCESS_API +import org.jetbrains.kotlinx.dataframe.util.MESSAGE_SHORTCUT +import org.jetbrains.kotlinx.dataframe.util.IS_EMPTY_REPLACE +import org.jetbrains.kotlinx.dataframe.util.IS_NOT_EMPTY_REPLACE +import org.jetbrains.kotlinx.dataframe.util.GET_ROW_REPLACE +import org.jetbrains.kotlinx.dataframe.util.GET_ROWS_ITERABLE_REPLACE +import org.jetbrains.kotlinx.dataframe.util.GET_ROWS_RANGE_REPLACE +import org.jetbrains.kotlinx.dataframe.util.GET_ROW_OR_NULL_REPLACE import kotlin.experimental.ExperimentalTypeInference import kotlin.reflect.KProperty import kotlin.reflect.KType -@CandidateForRemoval +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(IS_EMPTY_REPLACE), DeprecationLevel.WARNING) public fun AnyRow.isEmpty(): Boolean = owner.columns().all { it[index] == null } -@CandidateForRemoval +@Suppress("DEPRECATION_ERROR") +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(IS_NOT_EMPTY_REPLACE), DeprecationLevel.WARNING) public fun AnyRow.isNotEmpty(): Boolean = !isEmpty() public inline fun AnyRow.valuesOf(): List = values().filterIsInstance() @@ -178,35 +185,50 @@ public fun AnyRow.columnNames(): List = df().columnNames() public fun AnyRow.columnTypes(): List = df().columnTypes() -@CandidateForRemoval +@Suppress("DEPRECATION_ERROR") +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(GET_ROW_REPLACE), DeprecationLevel.WARNING) public fun DataRow.getRow(index: Int): DataRow = getRowOrNull(index)!! -@CandidateForRemoval +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(GET_ROWS_ITERABLE_REPLACE), DeprecationLevel.WARNING) public fun DataRow.getRows(indices: Iterable): DataFrame = df().getRows(indices) -@CandidateForRemoval +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(GET_ROWS_RANGE_REPLACE), DeprecationLevel.WARNING) public fun DataRow.getRows(indices: IntRange): DataFrame = df().getRows(indices) -@CandidateForRemoval +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(GET_ROW_OR_NULL_REPLACE), DeprecationLevel.WARNING) public fun DataRow.getRowOrNull(index: Int): DataRow? { val df = df() return if (index >= 0 && index < df.nrow) df[index] else null } +/** + * Returns the previous [row][DataRow] in the [DataFrame] relative to the current row. + * If the current row is the first row in the [DataFrame], it returns `null`. + * + * @return The previous [DataRow] if it exists, or `null` if the current row is the first in the [DataFrame]. + */ public fun DataRow.prev(): DataRow? { val index = index() return if (index > 0) df()[index - 1] else null } +/** + * Returns the next [row][DataRow] in the [DataFrame] relative to the current row. + * If the current row is the last row in the [DataFrame], it returns `null`. + * + * @return The previous [DataRow] if it exists, or `null` if the current row is the last in the [DataFrame]. + */ public fun DataRow.next(): DataRow? { val index = index() val df = df() return if (index < df.nrow - 1) df[index + 1] else null } +@Suppress("DEPRECATION_ERROR") public fun DataRow.relative(relativeIndices: Iterable): DataFrame = getRows(relativeIndices.mapNotNull { (index + it).let { if (it >= 0 && it < df().rowsCount()) it else null } }) +@Suppress("DEPRECATION_ERROR") public fun DataRow.relative(relativeIndices: IntRange): DataFrame = getRows( (relativeIndices.first + index).coerceIn(df().indices)..(relativeIndices.last + index).coerceIn(df().indices), diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/copy.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/copy.kt index 01f4fb84ae..42b7ea62a1 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/copy.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/copy.kt @@ -1,10 +1,12 @@ package org.jetbrains.kotlinx.dataframe.api import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.annotations.CandidateForRemoval +import org.jetbrains.kotlinx.dataframe.util.MESSAGE_SHORTCUT +import org.jetbrains.kotlinx.dataframe.util.COPY_REPLACE + // region DataFrame -@CandidateForRemoval +@Deprecated(MESSAGE_SHORTCUT, ReplaceWith(COPY_REPLACE), DeprecationLevel.WARNING) public fun DataFrame.copy(): DataFrame = columns().toDataFrame().cast() // endregion diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/merge.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/merge.kt index 7a1ed49fc9..8760762763 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/merge.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/merge.kt @@ -90,6 +90,7 @@ public fun Merge.intoList(): List = public fun MergeWithTransform.intoList(): List = df.select(selector).rows().map { transform(it, it.values() as List) } +@Suppress("DEPRECATION_ERROR") public fun MergeWithTransform.into(path: ColumnPath): DataFrame { // If target path exists, merge into temp path val mergePath = if (df.getColumnOrNull(path) != null) { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt index 1359db525a..3f6fed731e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/move.kt @@ -6,7 +6,6 @@ import org.jetbrains.kotlinx.dataframe.ColumnSelector import org.jetbrains.kotlinx.dataframe.ColumnsSelector import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.annotations.AccessApiOverload -import org.jetbrains.kotlinx.dataframe.annotations.CandidateForRemoval import org.jetbrains.kotlinx.dataframe.annotations.Interpretable import org.jetbrains.kotlinx.dataframe.annotations.Refine import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup @@ -407,9 +406,20 @@ public fun MoveClause.into( ) /** - * Move a single selected column to top level with a new name + * Moves the selected column, previously specified with [move], + * to the top level of the [DataFrame] and assigns it a new name. + * + * For more information, see {@include [DocumentationUrls.Move]}. + * + * ### Example: + * ```kotlin + * // Move "info"."salary" column to the top level with a new name "income" + * df.move { info.salary }.into("income") + * ``` + * + * @param column The new [String] name of the column after the move. + * @return A new [DataFrame] with the column moved and renamed. */ -@CandidateForRemoval @Refine @Interpretable("MoveInto0") public fun MoveClause.into(column: String): DataFrame = pathOf(column).let { path -> into { path } } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/modes/aggregateBy.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/modes/aggregateBy.kt index 77cf7848da..e48b97373d 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/modes/aggregateBy.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/aggregation/modes/aggregateBy.kt @@ -2,34 +2,15 @@ package org.jetbrains.kotlinx.dataframe.impl.aggregation.modes import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.DataFrameExpression import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.RowExpression -import org.jetbrains.kotlinx.dataframe.annotations.CandidateForRemoval -import org.jetbrains.kotlinx.dataframe.api.GroupBy -import org.jetbrains.kotlinx.dataframe.api.Grouped import org.jetbrains.kotlinx.dataframe.api.asSequence -import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.getOrNull import org.jetbrains.kotlinx.dataframe.columns.ColumnReference -import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregateInternal import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregators.Aggregator import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregators.indexOfAggregationResult -import org.jetbrains.kotlinx.dataframe.impl.namedValues import kotlin.reflect.typeOf -@CandidateForRemoval -internal fun Grouped.aggregateByOrNull(body: DataFrameExpression?>): DataFrame { - require(this is GroupBy<*, T>) - val keyColumns = keys.columnNames().toSet() - return aggregateInternal { - val row = body(df, df) - row?.namedValues()?.forEach { - if (!keyColumns.contains(it.name)) yield(it) - } - }.cast() -} - /** * Selects the best matching value in the [sequence][values] * using the provided [Aggregator] `by` the provided [selector]. diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt index 933d5081e2..63409eaf24 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt @@ -245,6 +245,14 @@ internal const val ADD_VARARG_COLUMNS_REPLACE = "this.addAll(*columns)" internal const val ADD_VARARG_FRAMES = "Deprecated in favor of `addAll(vararg)` to improve completion. $MESSAGE_1_1" internal const val ADD_VARARG_FRAMES_REPLACE = "this.addAll(*dataFrames)" +internal const val IS_EMPTY_REPLACE = "values().all { it == null }" +internal const val IS_NOT_EMPTY_REPLACE = "values().any { it != null }" +internal const val GET_ROW_REPLACE = "df().getRow(index)" +internal const val GET_ROWS_ITERABLE_REPLACE = "df().getRows(indices)" +internal const val GET_ROWS_RANGE_REPLACE = "df().getRows(indices)" +internal const val GET_ROW_OR_NULL_REPLACE = "df().getRowOrNull(index)" +internal const val COPY_REPLACE = "columns().toDataFrame().cast()" + // endregion // region keep across releases diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt index a09b476532..351912ed47 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt @@ -87,6 +87,7 @@ class ConstructorsTests { col.type() shouldBe typeOf() col.kind() shouldBe ColumnKind.Group col[0] shouldBe row + @Suppress("DEPRECATION_ERROR") col[1].isEmpty() shouldBe true } @@ -103,7 +104,9 @@ class ConstructorsTests { col.type() shouldBe typeOf() col.kind() shouldBe ColumnKind.Group col[0] shouldBe row + @Suppress("DEPRECATION_ERROR") col[1]!!.isEmpty() shouldBe true + @Suppress("DEPRECATION_ERROR") col[2]!!.isEmpty() shouldBe true } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/statistics/sum.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/statistics/sum.kt index aaafae2412..f27395f910 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/statistics/sum.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/statistics/sum.kt @@ -117,6 +117,7 @@ class SumTests { } } + @Suppress("DEPRECATION_ERROR") @Test fun `unknown number type`() { columnOf(1.toBigDecimal(), 2.toBigDecimal()).toDataFrame() diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt index 68eba9ec32..25f157eb0b 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTests.kt @@ -2464,6 +2464,7 @@ class DataFrameTests : BaseTest() { df.comparableNothing.valuesAreComparable() shouldBe false } + @Suppress("DEPRECATION_ERROR") // https://github.com/Kotlin/dataframe/pull/1077#discussion_r1981352374 @Test fun `values are comparable difficult`() { diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTreeTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTreeTests.kt index e138f112ea..aa4d6e220d 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTreeTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/DataFrameTreeTests.kt @@ -266,7 +266,7 @@ class DataFrameTreeTests : BaseTest() { @Test fun `move`() { - val actual = typed2.move { nameAndCity.name }.into("name") + val actual = typed2.move { nameAndCity.name }.into { pathOf("name") } actual.columnNames() shouldBe listOf("nameAndCity", "name", "age", "weight") actual.getColumnGroup("nameAndCity").columnNames() shouldBe listOf("city") } @@ -368,6 +368,7 @@ class DataFrameTreeTests : BaseTest() { df2.pivot { it["nameAndCity"]["city"] }.groupBy { it["nameAndCity"]["name"] }.values("age").check() } + @Suppress("DEPRECATION_ERROR") @Test fun `pivot grouped column`() { val grouped = typed.group { age and weight }.into("info") diff --git a/dataframe-arrow/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/ArrowKtTest.kt b/dataframe-arrow/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/ArrowKtTest.kt index aa4f6bd652..679aabae49 100644 --- a/dataframe-arrow/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/ArrowKtTest.kt +++ b/dataframe-arrow/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/ArrowKtTest.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlinx.dataframe.api.NullabilityOptions import org.jetbrains.kotlinx.dataframe.api.add import org.jetbrains.kotlinx.dataframe.api.columnOf import org.jetbrains.kotlinx.dataframe.api.convertToBoolean -import org.jetbrains.kotlinx.dataframe.api.copy import org.jetbrains.kotlinx.dataframe.api.dataFrameOf import org.jetbrains.kotlinx.dataframe.api.map import org.jetbrains.kotlinx.dataframe.api.pathOf @@ -354,7 +353,7 @@ internal class ArrowKtTest { @Test fun testNarrowing() { - val frameWithoutRequiredField = citiesExampleFrame.copy().remove("settled") + val frameWithoutRequiredField = citiesExampleFrame.remove("settled") frameWithoutRequiredField.arrowWriter( targetSchema = Schema.fromJSON(citiesExampleSchema), @@ -381,7 +380,7 @@ internal class ArrowKtTest { @Test fun testStrictType() { - val frameRenaming = citiesExampleFrame.copy().remove("settled") + val frameRenaming = citiesExampleFrame.remove("settled") val frameWithIncompatibleField = frameRenaming.add( frameRenaming["is_capital"] @@ -424,7 +423,7 @@ internal class ArrowKtTest { @Test fun testStrictNullable() { - val frameRenaming = citiesExampleFrame.copy().remove("settled") + val frameRenaming = citiesExampleFrame.remove("settled") val frameWithNulls = frameRenaming.add( DataColumn.createValueColumn( "settled", diff --git a/plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/move.kt b/plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/move.kt index 0ae08cdfba..b2c5fea70b 100644 --- a/plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/move.kt +++ b/plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/move.kt @@ -4,6 +4,7 @@ import org.jetbrains.kotlinx.dataframe.api.after import org.jetbrains.kotlinx.dataframe.api.into import org.jetbrains.kotlinx.dataframe.api.move import org.jetbrains.kotlinx.dataframe.api.moveToStart +import org.jetbrains.kotlinx.dataframe.api.pathOf import org.jetbrains.kotlinx.dataframe.api.to import org.jetbrains.kotlinx.dataframe.api.toStart import org.jetbrains.kotlinx.dataframe.api.toEnd @@ -62,7 +63,7 @@ class MoveInto0 : AbstractSchemaModificationInterpreter() { override fun Arguments.interpret(): PluginDataFrameSchema { val columns = receiver.columns.resolve(receiver.df).map { it.path } - return receiver.df.asDataFrame().move { columns.toColumnSet() }.into(column).toPluginDataFrameSchema() + return receiver.df.asDataFrame().move { columns.toColumnSet() }.into{ pathOf(column) }.toPluginDataFrameSchema() } }