Skip to content

Commit 763f7b8

Browse files
Merge pull request #1406 from Kotlin/insert_kdocs
Insert kdocs
2 parents 3cc2006 + 551f276 commit 763f7b8

File tree

4 files changed

+279
-6
lines changed

4 files changed

+279
-6
lines changed

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/insert.kt

Lines changed: 271 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,230 @@ import org.jetbrains.kotlinx.dataframe.annotations.Interpretable
1010
import org.jetbrains.kotlinx.dataframe.annotations.Refine
1111
import org.jetbrains.kotlinx.dataframe.columns.ColumnAccessor
1212
import org.jetbrains.kotlinx.dataframe.columns.ColumnPath
13+
import org.jetbrains.kotlinx.dataframe.documentation.DocumentationUrls
14+
import org.jetbrains.kotlinx.dataframe.documentation.DslGrammarLink
15+
import org.jetbrains.kotlinx.dataframe.documentation.ExcludeFromSources
16+
import org.jetbrains.kotlinx.dataframe.documentation.Indent
17+
import org.jetbrains.kotlinx.dataframe.documentation.LineBreak
18+
import org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns
1319
import org.jetbrains.kotlinx.dataframe.impl.api.insertImpl
1420
import org.jetbrains.kotlinx.dataframe.impl.columnName
1521
import org.jetbrains.kotlinx.dataframe.impl.removeAt
1622
import org.jetbrains.kotlinx.dataframe.util.DEPRECATED_ACCESS_API
23+
import org.jetbrains.kotlinx.dataframe.util.INSERT_AFTER_COL_PATH
24+
import org.jetbrains.kotlinx.dataframe.util.INSERT_AFTER_COL_PATH_REPLACE
1725
import kotlin.reflect.KProperty
1826

1927
// region DataFrame
2028

2129
// region insert
2230

31+
/**
32+
* This function does not immediately insert the new column but instead specify a column to insert and
33+
* returns an [InsertClause],
34+
* which serves as an intermediate step.
35+
* The [InsertClause] object provides methods to insert a new column using:
36+
* - [under][InsertClause.under] - inserts a new column under the specified column group.
37+
* - [after][InsertClause.after] - inserts a new column after the specified column.
38+
* - [at][InsertClause.at]- inserts a new column at the specified position.
39+
*
40+
* Each method returns a new [DataFrame] with the inserted column.
41+
*
42+
* Check out [Grammar].
43+
*
44+
* @include [SelectingColumns.ColumnGroupsAndNestedColumnsMention]
45+
*
46+
* See [Selecting Columns][InsertSelectingOptions].
47+
*
48+
* For more information: {@include [DocumentationUrls.Insert]}
49+
*
50+
* See also:
51+
* - [move][DataFrame.move] - move columns to a new position within the [DataFrame].
52+
* - [add][DataFrame.add] - add new columns to the [DataFrame]
53+
* (without specifying a position, to the end of the [DataFrame]).
54+
*/
55+
internal interface InsertDocs {
56+
57+
/**
58+
* {@comment Version of [SelectingColumns] with correctly filled in examples}
59+
* @include [SelectingColumns] {@include [SetInsertOperationArg]}
60+
*/
61+
interface InsertSelectingOptions
62+
63+
/**
64+
* ## Insert Operation Grammar
65+
* {@include [LineBreak]}
66+
* {@include [DslGrammarLink]}
67+
* {@include [LineBreak]}
68+
*
69+
* [**`insert`**][insert]**`(`**`column: `[`DataColumn`][DataColumn]**`)`**` /`
70+
*
71+
* [**`insert`**][insert]**`(`**`name: `[`String`][String]**`, `**`infer: `[`Infer`][Infer]**`) { `**`rowExpression: `[`RowExpression`][RowExpression]**` }`**
72+
*
73+
* {@include [Indent]}
74+
* __`.`__[**`under`**][InsertClause.under]**` { `**`column: `[`ColumnSelector`][ColumnSelector]**` }`**
75+
*
76+
* {@include [Indent]}
77+
* `| `__`.`__[**`under`**][InsertClause.under]**`(`**` columnPath: `[`ColumnPath`][ColumnPath]**`)`**
78+
*
79+
* {@include [Indent]}
80+
* `| `__`.`__[**`after`**][InsertClause.after]**` { `**`column: `[`ColumnSelector`][ColumnSelector]**` }`**
81+
*
82+
* {@include [Indent]}
83+
* `| `__`.`__[**`at`**][InsertClause.at]**`(`**`position: `[`Int`][Int]**`)`**
84+
*/
85+
interface Grammar
86+
}
87+
88+
/** {@set [SelectingColumns.OPERATION] [insert][insert]} */
89+
@ExcludeFromSources
90+
private interface SetInsertOperationArg
91+
92+
/**
93+
* Inserts the given [column] into this [DataFrame].
94+
*
95+
* {@include [InsertDocs]}
96+
*
97+
* ### Examples:
98+
* ```kotlin
99+
* // Insert a new column "age" under the column group with path ("info", "personal").
100+
* df.insert(age).under(pathOf("info", "personal"))
101+
*
102+
* // Insert a new column "count" after the column "url".
103+
* df.insert(count).after { url }
104+
* ```
105+
*
106+
* @param [column] A single [DataColumn] to insert into the [DataFrame].
107+
* @return An [InsertClause] for specifying the placement of the new column.
108+
*/
23109
public fun <T, C> DataFrame<T>.insert(column: DataColumn<C>): InsertClause<T> = InsertClause(this, column)
24110

111+
/**
112+
* Creates a new column using the provided [expression][AddExpression] and inserts it into this [DataFrame].
113+
*
114+
* {@include [AddExpressionDocs]}
115+
*
116+
* {@include [InsertDocs]}
117+
*
118+
* ## Examples
119+
*
120+
* ```kotlin
121+
* // Insert a new column "sum" that contains the sum of values from the "firstValue"
122+
* // and "secondValue" columns for each row after the "firstValue" column.
123+
* val dfWithSum = df.insert("sum") { firstValue + secondValue }.after { firstValue }
124+
*
125+
* // Insert a new "fibonacci" column with the Fibonacci sequence under a "math" column group:
126+
* // for the first two rows, the value is 1;
127+
* // for subsequent rows, it's the sum of the two previous Fibonacci values.
128+
* val dfWithFibonacci = df.insert("fibonacci") {
129+
* if (index() < 2) 1
130+
* else prev()!!.newValue<Int>() + prev()!!.prev()!!.newValue<Int>()
131+
* }.under("math")
132+
* ```
133+
*
134+
* @param [name] The name of the new column to be created and inserted.
135+
* @param [infer] Controls how values are inferred when building the new column. Defaults to [Infer.Nulls].
136+
* @param [expression] An [AddExpression] that computes the value for each row of the new column.
137+
* @return An [InsertClause] for specifying the placement of the newly created column.
138+
*/
25139
@Interpretable("Insert1")
26140
public inline fun <T, reified R> DataFrame<T>.insert(
27141
name: String,
28142
infer: Infer = Infer.Nulls,
29-
noinline expression: RowExpression<T, R>,
143+
noinline expression: AddExpression<T, R>,
30144
): InsertClause<T> = insert(mapToColumn(name, infer, expression))
31145

32146
@Deprecated(DEPRECATED_ACCESS_API)
33147
@AccessApiOverload
34148
public inline fun <T, reified R> DataFrame<T>.insert(
35149
column: ColumnAccessor<R>,
36150
infer: Infer = Infer.Nulls,
37-
noinline expression: RowExpression<T, R>,
151+
noinline expression: AddExpression<T, R>,
38152
): InsertClause<T> = insert(column.name(), infer, expression)
39153

40154
@Deprecated(DEPRECATED_ACCESS_API)
41155
@AccessApiOverload
42156
public inline fun <T, reified R> DataFrame<T>.insert(
43157
column: KProperty<R>,
44158
infer: Infer = Infer.Nulls,
45-
noinline expression: RowExpression<T, R>,
159+
noinline expression: AddExpression<T, R>,
46160
): InsertClause<T> = insert(column.columnName, infer, expression)
47161

48162
// endregion
49163

164+
/**
165+
* An intermediate class used in the [insert] operation.
166+
*
167+
* This class itself does not perform any insertions — it is a transitional step
168+
* before specifying how to insert the selected columns.
169+
* It must be followed by one of the inserting methods
170+
* to produce a new [DataFrame] with an inserted column.
171+
*
172+
* Use the following methods to perform the insertion:
173+
* - [under][InsertClause.under] - inserts a new column under the specified column group.
174+
* - [after][InsertClause.after] - inserts a new column after the specified column.
175+
* - [at][InsertClause.at]- inserts a new column at the specified position.
176+
*
177+
* See [Grammar][InsertDocs.Grammar] for more details.
178+
*/
50179
public class InsertClause<T>(internal val df: DataFrame<T>, internal val column: AnyCol) {
51180
override fun toString(): String = "InsertClause(df=$df, column=$column)"
52181
}
53182

54183
// region under
55184

185+
/**
186+
* Inserts the new column previously specified with [insert] under
187+
* the selected [column group][column].
188+
*
189+
* Works only with existing column groups.
190+
* To insert into a new column group, use the overloads:
191+
* `under(path: ColumnPath)` or `under(column: String)`.
192+
* [Should be fixed](https://github.com/Kotlin/dataframe/issues/1411).
193+
*
194+
* For more information: {@include [DocumentationUrls.Insert]}
195+
*
196+
* See [Grammar][InsertDocs.Grammar] for more details.
197+
*
198+
* See [SelectingColumns.Dsl].
199+
*
200+
* ### Examples
201+
* ```kotlin
202+
* // Insert a new column "age" under the column group with path ("info", "personal")
203+
* df.insert(age).under { info.personal }
204+
*
205+
* // Insert a new column "sum" under the only top-level column group
206+
* val dfWithSum = df.insert("sum") { a + b }.under { colGroups().single() }
207+
* ```
208+
*
209+
* @param column The [ColumnSelector] used to choose an existing column group in this [DataFrame]
210+
* under which the new column will be inserted.
211+
* @return A new [DataFrame] with the inserted column placed under the selected group.
212+
*/
56213
@Refine
57214
@Interpretable("Under0")
58215
public fun <T> InsertClause<T>.under(column: ColumnSelector<T, *>): DataFrame<T> = under(df.getColumnPath(column))
59216

217+
/**
218+
* Inserts the new column previously specified with [insert] under
219+
* the column group defined by the given [columnPath].
220+
*
221+
* {@include [org.jetbrains.kotlinx.dataframe.documentation.ColumnPathCreation]}
222+
*
223+
* See [Grammar][InsertDocs.Grammar] for more details.
224+
*
225+
* For more information: {@include [DocumentationUrls.Insert]}
226+
*
227+
* ### Example
228+
* ```kotlin
229+
* // Insert a new column "age" under the column group with path ("info", "personal")
230+
* df.insert(age).under(pathOf("info", "personal"))
231+
* ```
232+
*
233+
* @param [columnPath] The [ColumnPath] specifying the path to a column group in this [DataFrame]
234+
* under which the new column will be inserted.
235+
* @return A new [DataFrame] with the inserted column placed under the specified column group.
236+
*/
60237
@Refine
61238
@Interpretable("Under1")
62239
public fun <T> InsertClause<T>.under(columnPath: ColumnPath): DataFrame<T> =
@@ -70,6 +247,26 @@ public fun <T> InsertClause<T>.under(column: ColumnAccessor<*>): DataFrame<T> =
70247
@AccessApiOverload
71248
public fun <T> InsertClause<T>.under(column: KProperty<*>): DataFrame<T> = under(column.columnName)
72249

250+
/**
251+
* Inserts the new column previously specified with [insert] under
252+
* the given column group by its [name][column].
253+
*
254+
* If the column group with the provided [name][column] does not exist, it will be created automatically.
255+
*
256+
* For more information: {@include [DocumentationUrls.Insert]}
257+
*
258+
* See [Grammar][InsertDocs.Grammar] for more details.
259+
*
260+
* ### Example
261+
* ```kotlin
262+
* // Insert a new column "age" under the "info" column group.
263+
* df.insert(age).under("info")
264+
* ```
265+
*
266+
* @param [column] The [name][String] of the column group in this [DataFrame].
267+
* If the group does not exist, it will be created.
268+
* @return A new [DataFrame] with the inserted column placed under the specified column group.
269+
*/
73270
@Refine
74271
@Interpretable("Under4")
75272
public fun <T> InsertClause<T>.under(column: String): DataFrame<T> = under(pathOf(column))
@@ -78,29 +275,98 @@ public fun <T> InsertClause<T>.under(column: String): DataFrame<T> = under(pathO
78275

79276
// region after
80277

278+
/**
279+
* Inserts the new column previously specified with [insert]
280+
* at the position immediately after the selected [column] (on the same level).
281+
*
282+
* For more information: {@include [DocumentationUrls.Insert]}
283+
*
284+
* See [Grammar][InsertDocs.Grammar] for more details.
285+
*
286+
* See also: [SelectingColumns.Dsl].
287+
*
288+
* ### Examples:
289+
* ```kotlin
290+
* // Insert a new column "age" after the "name" column
291+
* df.insert(age).after { name }
292+
*
293+
* // Insert a new column "sum" after the nested "min" column (inside the "stats" column group)
294+
* val dfWithSum = df.insert("sum") { a + b }.after { stats.min }
295+
* ```
296+
*
297+
* @param [column] The [ColumnSelector] used to choose an existing column in this [DataFrame],
298+
* after which the new column will be inserted.
299+
* @return A new [DataFrame] with the inserted column placed after the selected column.
300+
*/
81301
@Refine
82302
@Interpretable("InsertAfter0")
83-
public fun <T> InsertClause<T>.after(column: ColumnSelector<T, *>): DataFrame<T> = after(df.getColumnPath(column))
303+
public fun <T> InsertClause<T>.after(column: ColumnSelector<T, *>): DataFrame<T> = afterImpl(df.getColumnPath(column))
84304

305+
/**
306+
* Inserts the new column previously specified with [insert]
307+
* at the position immediately after the column with the given [name][column].
308+
*
309+
* For more information: {@include [DocumentationUrls.Insert]}
310+
*
311+
* See [Grammar][InsertDocs.Grammar] for more details.
312+
*
313+
* See also: [SelectingColumns.ColumnNames].
314+
*
315+
* ### Example
316+
* ```kotlin
317+
* // Insert a new column "age" after the "name" column
318+
* df.insert(age).after("name")
319+
* ```
320+
*
321+
* @param [column] The [String] name of the column in this [DataFrame]
322+
* after which the new column will be inserted.
323+
* @return A new [DataFrame] with the inserted column placed after the specified column.
324+
*/
85325
public fun <T> InsertClause<T>.after(column: String): DataFrame<T> = df.add(this.column).move(this.column).after(column)
86326

87327
@Deprecated(DEPRECATED_ACCESS_API)
88328
@AccessApiOverload
89-
public fun <T> InsertClause<T>.after(column: ColumnAccessor<*>): DataFrame<T> = after(column.path())
329+
public fun <T> InsertClause<T>.after(column: ColumnAccessor<*>): DataFrame<T> = afterImpl(column.path())
90330

91331
@Deprecated(DEPRECATED_ACCESS_API)
92332
@AccessApiOverload
93333
public fun <T> InsertClause<T>.after(column: KProperty<*>): DataFrame<T> = after(column.columnName)
94334

335+
@Deprecated(INSERT_AFTER_COL_PATH, ReplaceWith(INSERT_AFTER_COL_PATH_REPLACE), DeprecationLevel.ERROR)
95336
public fun <T> InsertClause<T>.after(columnPath: ColumnPath): DataFrame<T> {
96337
val dstPath = ColumnPath(columnPath.removeAt(columnPath.size - 1) + column.name())
97338
return df.insertImpl(dstPath, column).move { dstPath }.after { columnPath }
98339
}
99340

341+
internal fun <T> InsertClause<T>.afterImpl(columnPath: ColumnPath): DataFrame<T> {
342+
val dstPath = ColumnPath(columnPath.removeAt(columnPath.size - 1) + column.name())
343+
return df.insertImpl(dstPath, column).move { dstPath }.after { columnPath }
344+
}
345+
100346
// endregion
101347

102348
// region at
103349

350+
/**
351+
* Inserts the new column previously specified with [insert]
352+
* at the given [position] in the [DataFrame].
353+
*
354+
* The new column will be placed at the specified index, shifting existing columns to the right.
355+
*
356+
* For more information: {@include [DocumentationUrls.Insert]}
357+
*
358+
* See [Grammar][InsertDocs.Grammar] for more details.
359+
*
360+
* ### Example
361+
* ```kotlin
362+
* // Insert a new column "age" at index 3
363+
* df.insert(age).at(3)
364+
* ```
365+
*
366+
* @param [position] The [Int] index where the new column should be inserted.
367+
* Columns currently at this index and after will be shifted right.
368+
* @return A new [DataFrame] with the inserted column placed at the specified position.
369+
*/
104370
@Refine
105371
@Interpretable("InsertAt")
106372
public fun <T> InsertClause<T>.at(position: Int): DataFrame<T> = df.add(column).move(column).to(position)

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ internal interface DocumentationUrls {
135135
/** [See `format` on the documentation website.]({@include [Url]}/format.html) */
136136
interface Format
137137

138+
/** [See `insert` on the documentation website.]({@include [Url]}/insert.html) */
139+
interface Insert
140+
138141
/** [See `rename` on the documentation website.]({@include [Url]}/rename.html) */
139142
interface Rename
140143
}

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ internal const val COL_TYPE_INSTANT =
139139
"kotlinx.datetime.Instant is deprecated in favor of kotlin.time.Instant. Either migrate to kotlin.time.Instant and use ColType.StdlibInstant or use ColType.DeprecatedInstant. $MESSAGE_1_0 and migrated to kotlin.time.Instant in 1.1."
140140
internal const val COL_TYPE_INSTANT_REPLACE = "ColType.DeprecatedInstant"
141141

142+
internal const val INSERT_AFTER_COL_PATH =
143+
"This `after()` overload will be removed in favor of `after { }` with Column Selection DSL. $MESSAGE_1_0"
144+
internal const val INSERT_AFTER_COL_PATH_REPLACE = "this.after { columnPath }"
145+
142146
// endregion
143147

144148
// region WARNING in 1.0, ERROR in 1.1

plugins/kotlin-dataframe/src/org/jetbrains/kotlinx/dataframe/plugin/impl/api/insert.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ internal class InsertAfter0 : AbstractInterpreter<PluginDataFrameSchema>() {
8383

8484
override fun Arguments.interpret(): PluginDataFrameSchema {
8585
return receiver.df.asDataFrame()
86-
.insert(receiver.column.asDataColumn()).after(column.col.path)
86+
.insert(receiver.column.asDataColumn()).after { column.col.path }
8787
.toPluginDataFrameSchema()
8888
}
8989
}

0 commit comments

Comments
 (0)