diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt index db289d71ae..b32a594304 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt @@ -35,8 +35,20 @@ internal interface DocumentationUrls { /** [See Row Expressions on the documentation website.]({@include [Url]}/datarow.html#row-expressions) */ typealias RowExpressions = Nothing + /** [See RowExpression on the documentation website.]({@include [Url]}/datarow.html#rowexpression) */ + typealias RowExpression = Nothing + + /** [See RowValueExpression on the documentation website.]({@include [Url]}/datarow.html#rowvalueexpression) */ + typealias RowValueExpression = Nothing + /** [See Row Conditions on the documentation website.]({@include [Url]}/datarow.html#row-conditions) */ typealias RowConditions = Nothing + + /** [See RowFilter on the documentation website.]({@include [Url]}/datarow.html#rowfilter) */ + typealias RowFilter = Nothing + + /** [See RowValueFilter on the documentation website.]({@include [Url]}/datarow.html#rowvaluefilter) */ + typealias RowValueFilter = Nothing } /** [See `drop` on the documentation website.]({@include [Url]}/drop.html) */ diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt index 57c5f0f8ad..656f7d8e0f 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt @@ -57,7 +57,11 @@ internal interface ExpressionsGivenRow { @ExcludeFromSources typealias AddDataRowNote = Nothing - /** Provide a new value for every selected cell given its row using a [row expression][DfRowExpression]. */ + /** + * Provide a new value for every selected cell given its row using a [row expression][DfRowExpression]. + * + * Fore more information, {@include [DocumentationUrls.DataRow.RowExpression]} + */ interface RowExpression { /** @@ -79,6 +83,8 @@ internal interface ExpressionsGivenRow { /** Provide a new value for every selected cell given its row and its previous value using a * [row value expression][DfRowValueExpression]. + * + * Fore more information, {@include [DocumentationUrls.DataRow.RowValueExpression]} */ interface RowValueExpression { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt index 95152ba438..9cbc381db9 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt @@ -41,7 +41,7 @@ internal interface SelectingRows { * including through [extension properties][AccessApis.ExtensionPropertiesApi] * for convenient and type-safe access. * - * Fore more information, {@include [DocumentationUrls.DataRow.RowConditions]} + * Fore more information, {@include [DocumentationUrls.DataRow.RowFilter]} */ @ExcludeFromSources typealias RowFilterSnippet = Nothing @@ -60,7 +60,7 @@ internal interface SelectingRows { * including through [extension properties][AccessApis.ExtensionPropertiesApi] * for convenient and type-safe access. * - * Fore more information, {@include [DocumentationUrls.DataRow.RowConditions]} + * Fore more information, {@include [DocumentationUrls.DataRow.RowValueFilter]} */ @ExcludeFromSources typealias RowValueFilterSnippet = Nothing diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt index d62a56c370..1fe4a54921 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt @@ -265,7 +265,7 @@ internal fun AnyFrame.toHtmlData( } val nested = if (col is ColumnGroup<*>) { col.columns().map { - col.columnToJs(it.addParentPath(col.path), rowsLimit, configuration, renderRootDf) + renderRootDf.columnToJs(it.addParentPath(col.path), rowsLimit, configuration, renderRootDf) } } else { emptyList() diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt index aebf9b7f18..f700b833fd 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlinx.dataframe.samples.api.TestBase import org.jetbrains.kotlinx.dataframe.samples.api.age import org.jetbrains.kotlinx.dataframe.samples.api.firstName import org.jetbrains.kotlinx.dataframe.samples.api.isHappy +import org.jetbrains.kotlinx.dataframe.samples.api.lastName import org.jetbrains.kotlinx.dataframe.samples.api.name import org.jetbrains.kotlinx.dataframe.samples.api.weight import org.junit.Test @@ -314,6 +315,18 @@ class FormatTests : TestBase() { formatted::class.simpleName shouldBe "FormattedFrame" } + @Test + fun `format with where clause on df with a column group`() { + val formatted = df + .format { all() } + .where { age < 18 } + .with { background(red) and textColor(black) } + + val html = formatted.toHtml().toString() + + html.split("background-color:#ff0000").size - 1 shouldBe 7 + } + // Issue #982 @Suppress("ktlint:standard:argument-list-wrapping") @Test diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApi.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApi.kt deleted file mode 100644 index 7150512478..0000000000 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApi.kt +++ /dev/null @@ -1,49 +0,0 @@ -@file:Suppress("ktlint") - -package org.jetbrains.kotlinx.dataframe.samples.api - -import org.jetbrains.kotlinx.dataframe.api.add -import org.jetbrains.kotlinx.dataframe.api.at -import org.jetbrains.kotlinx.dataframe.api.diffOrNull -import org.jetbrains.kotlinx.dataframe.api.drop -import org.jetbrains.kotlinx.dataframe.api.filter -import org.jetbrains.kotlinx.dataframe.api.pivot -import org.jetbrains.kotlinx.dataframe.api.prev -import org.jetbrains.kotlinx.dataframe.api.update -import org.jetbrains.kotlinx.dataframe.api.where -import org.jetbrains.kotlinx.dataframe.api.with -import org.jetbrains.kotlinx.dataframe.explainer.TransformDataFrameExpressions -import org.junit.Test - -class DataRowApi : TestBase() { - - @Test - @TransformDataFrameExpressions - fun expressions() { - // SampleStart - // Row expression computes values for a new column - df.add("fullName") { name.firstName + " " + name.lastName } - - // Row expression computes updated values - df.update { weight }.at(1, 3, 4).with { prev()?.weight } - - // Row expression computes cell content for values of pivoted column - df.pivot { city }.with { name.lastName.uppercase() } - // SampleEnd - } - - @Test - @TransformDataFrameExpressions - fun conditions() { - // SampleStart - // Row condition is used to filter rows by index - df.filter { index() % 5 == 0 } - - // Row condition is used to drop rows where `age` is the same as in the previous row - df.drop { diffOrNull { age } == 0 } - - // Row condition is used to filter rows for value update - df.update { weight }.where { index() > 4 && city != "Paris" }.with { 50 } - // SampleEnd - } -} diff --git a/docs/StardustDocs/resources/api/dataRowApi/addWithExpression_properties.html b/docs/StardustDocs/resources/api/dataRowApi/addWithExpression_properties.html new file mode 100644 index 0000000000..6218b8407f --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/addWithExpression_properties.html @@ -0,0 +1,517 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/convertExpression_properties.html b/docs/StardustDocs/resources/api/dataRowApi/convertExpression_properties.html new file mode 100644 index 0000000000..b208982ae8 --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/convertExpression_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/dfDataRow.html b/docs/StardustDocs/resources/api/dataRowApi/dfDataRow.html new file mode 100644 index 0000000000..bf549bf28c --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/dfDataRow.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/dropWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/dropWithCondition_properties.html new file mode 100644 index 0000000000..fa066e9fdd --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/dropWithCondition_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/filterWithConditionDf.html b/docs/StardustDocs/resources/api/dataRowApi/filterWithConditionDf.html new file mode 100644 index 0000000000..29feda83f1 --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/filterWithConditionDf.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/filterWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/filterWithCondition_properties.html new file mode 100644 index 0000000000..ba45b38a2e --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/filterWithCondition_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/firstWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/firstWithCondition_properties.html new file mode 100644 index 0000000000..ef1235876d --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/firstWithCondition_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/formatWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/formatWithCondition_properties.html new file mode 100644 index 0000000000..ef126227e1 --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/formatWithCondition_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/gatherWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/gatherWithCondition_properties.html new file mode 100644 index 0000000000..3634a854ec --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/gatherWithCondition_properties.html @@ -0,0 +1,514 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/pivotWithExpression_properties.html b/docs/StardustDocs/resources/api/dataRowApi/pivotWithExpression_properties.html new file mode 100644 index 0000000000..0895649dc5 --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/pivotWithExpression_properties.html @@ -0,0 +1,515 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/updateWithCondition_properties.html b/docs/StardustDocs/resources/api/dataRowApi/updateWithCondition_properties.html new file mode 100644 index 0000000000..0f4440d85f --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/updateWithCondition_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/dataRowApi/updateWithExpression_properties.html b/docs/StardustDocs/resources/api/dataRowApi/updateWithExpression_properties.html new file mode 100644 index 0000000000..b32d21abb9 --- /dev/null +++ b/docs/StardustDocs/resources/api/dataRowApi/updateWithExpression_properties.html @@ -0,0 +1,516 @@ + + + + + +
+ +

+ + + diff --git a/docs/StardustDocs/resources/api/groupBy/pivotOnGroupBy_properties.html b/docs/StardustDocs/resources/api/groupBy/pivotOnGroupBy_properties.html index ee2e423717..04e742cb72 100644 --- a/docs/StardustDocs/resources/api/groupBy/pivotOnGroupBy_properties.html +++ b/docs/StardustDocs/resources/api/groupBy/pivotOnGroupBy_properties.html @@ -461,7 +461,7 @@ { name: "\">Alice", children: [], rightAlign: false, values: [{ frameId: 1, value: "DataFrame 1 x 5" },{ frameId: 2, value: "DataFrame 2 x 5" }] }, { name: "\">Bob", children: [], rightAlign: false, values: [{ frameId: 3, value: "DataFrame 2 x 5" },{ frameId: 4, value: "DataFrame 1 x 5" }] }, { name: "\">Charlie", children: [], rightAlign: false, values: [{ frameId: 5, value: "DataFrame 2 x 5" },{ frameId: 6, value: "DataFrame 2 x 5" }] }, -{ name: "\">firstName", children: [1, 2, 3], rightAlign: false, values: ["{ Alice: DataFrame [1 x 5], B..., C... }","{ Alice: DataFrame [2 x 5], B..., C... }"] }, +{ name: "\">firstName", children: [1, 2, 3], rightAlign: false, values: ["{ Alice: DataFrame [1 x 5], B..., C... }","{ Alice: DataFrame [2 x 5], B..., C... }"] }, { name: "\">name", children: [4], rightAlign: false, values: ["{ firstName: { Alice: DataFr..., ... } }","{ firstName: { Alice: DataFr..., ... } }"] }, ], id: 0, rootId: 0, totalRows: 2 } ) }); /*-->*/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotAnd_properties.html b/docs/StardustDocs/resources/api/pivot/pivotAnd_properties.html index 118973967c..119d8b05a0 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotAnd_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotAnd_properties.html @@ -459,11 +459,11 @@ /**/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotGroupByFrames_properties.html b/docs/StardustDocs/resources/api/pivot/pivotGroupByFrames_properties.html index c9d99d5091..161cbaeb5b 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotGroupByFrames_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotGroupByFrames_properties.html @@ -460,7 +460,7 @@ call_DataFrame(function() { DataFrame.addTable({ cols: [{ name: "firstName", children: [], rightAlign: false, values: ["Alice","Bob","Charlie"] }, { name: "\">true", children: [], rightAlign: false, values: [{ frameId: 1, value: "DataFrame 1 x 5" },{ frameId: 2, value: "DataFrame 2 x 5" },{ frameId: 3, value: "DataFrame 2 x 5" }] }, { name: "\">false", children: [], rightAlign: false, values: [{ frameId: 4, value: "DataFrame 2 x 5" },{ frameId: 5, value: "DataFrame 1 x 5" },{ frameId: 6, value: "DataFrame 2 x 5" }] }, -{ name: "\">isHappy", children: [1, 2], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }"] }, +{ name: "\">isHappy", children: [1, 2], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }"] }, ], id: 0, rootId: 0, totalRows: 3 } ) }); /*-->*/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotGroupByOther_properties.html b/docs/StardustDocs/resources/api/pivot/pivotGroupByOther_properties.html index 9d25492087..efd54781e5 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotGroupByOther_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotGroupByOther_properties.html @@ -465,7 +465,7 @@ { name: "weight", children: [], rightAlign: true, values: ["54","87","null","null","68","55","90","52","60","70"] }, { name: "\">true", children: [], rightAlign: false, values: [{ frameId: 1, value: "DataFrame 1 x 5" },{ frameId: 2, value: "DataFrame 1 x 5" },"",{ frameId: 3, value: "DataFrame 1 x 5" },{ frameId: 4, value: "DataFrame 1 x 5" },"",{ frameId: 5, value: "DataFrame 1 x 5" },"","",""] }, { name: "\">false", children: [], rightAlign: false, values: ["","",{ frameId: 6, value: "DataFrame 1 x 5" },"","",{ frameId: 7, value: "DataFrame 1 x 5" },"",{ frameId: 8, value: "DataFrame 1 x 5" },{ frameId: 9, value: "DataFrame 1 x 5" },{ frameId: 10, value: "DataFrame 1 x 5" }] }, -{ name: "\">isHappy", children: [6, 7], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [0 x 0], false: D... }"] }, +{ name: "\">isHappy", children: [6, 7], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [0 x 0], false: D... }","{ true: DataFrame [0 x 0], false: D... }"] }, ], id: 0, rootId: 0, totalRows: 10 } ) }); /*-->*/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotGroupBy_properties.html b/docs/StardustDocs/resources/api/pivot/pivotGroupBy_properties.html index c9d99d5091..161cbaeb5b 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotGroupBy_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotGroupBy_properties.html @@ -460,7 +460,7 @@ call_DataFrame(function() { DataFrame.addTable({ cols: [{ name: "firstName", children: [], rightAlign: false, values: ["Alice","Bob","Charlie"] }, { name: "\">true", children: [], rightAlign: false, values: [{ frameId: 1, value: "DataFrame 1 x 5" },{ frameId: 2, value: "DataFrame 2 x 5" },{ frameId: 3, value: "DataFrame 2 x 5" }] }, { name: "\">false", children: [], rightAlign: false, values: [{ frameId: 4, value: "DataFrame 2 x 5" },{ frameId: 5, value: "DataFrame 1 x 5" },{ frameId: 6, value: "DataFrame 2 x 5" }] }, -{ name: "\">isHappy", children: [1, 2], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }"] }, +{ name: "\">isHappy", children: [1, 2], rightAlign: false, values: ["{ true: DataFrame [1 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }","{ true: DataFrame [2 x 5], false: D... }"] }, ], id: 0, rootId: 0, totalRows: 3 } ) }); /*-->*/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotInward_properties.html b/docs/StardustDocs/resources/api/pivot/pivotInward_properties.html index 8ff674999f..274f7cc266 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotInward_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotInward_properties.html @@ -459,7 +459,7 @@ /**/ diff --git a/docs/StardustDocs/resources/api/pivot/pivotThen_properties.html b/docs/StardustDocs/resources/api/pivot/pivotThen_properties.html index 3fd5305149..c999da6594 100644 --- a/docs/StardustDocs/resources/api/pivot/pivotThen_properties.html +++ b/docs/StardustDocs/resources/api/pivot/pivotThen_properties.html @@ -460,11 +460,11 @@ call_DataFrame(function() { DataFrame.addTable({ cols: [{ name: "\">Alice", children: [], rightAlign: false, values: [{ frameId: 1, value: "DataFrame 1 x 5" }] }, { name: "\">Bob", children: [], rightAlign: false, values: [{ frameId: 2, value: "DataFrame 2 x 5" }] }, { name: "\">Charlie", children: [], rightAlign: false, values: [{ frameId: 3, value: "DataFrame 2 x 5" }] }, -{ name: "\">true", children: [0, 1, 2], rightAlign: false, values: ["{ Alice: DataFrame [1 x 5], B..., C... }"] }, +{ name: "\">true", children: [0, 1, 2], rightAlign: false, values: ["{ Alice: DataFrame [1 x 5], B..., C... }"] }, { name: "\">Charlie", children: [], rightAlign: false, values: [{ frameId: 4, value: "DataFrame 2 x 5" }] }, { name: "\">Alice", children: [], rightAlign: false, values: [{ frameId: 5, value: "DataFrame 2 x 5" }] }, { name: "\">Bob", children: [], rightAlign: false, values: [{ frameId: 6, value: "DataFrame 1 x 5" }] }, -{ name: "\">false", children: [4, 5, 6], rightAlign: false, values: ["{ Charlie: DataFrame [2 x 5], ... }"] }, +{ name: "\">false", children: [4, 5, 6], rightAlign: false, values: ["{ Charlie: DataFrame [2 x 5], ... }"] }, ], id: 0, rootId: 0, totalRows: 1 } ) }); /*-->*/ diff --git a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html b/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html deleted file mode 100644 index 46db5c8bd2..0000000000 --- a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html +++ /dev/null @@ -1,725 +0,0 @@ - - - - - -
- df.filter { index() % 5 == 0 } -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Output DataFrame: rowsCount = 2, columnsCount = 5 -
- -

-
-
-
-
- df.drop { diffOrNull { age } == 0 } -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Output DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
-
-
- df.update { weight }.where { index() > 4 && city != "Paris" }.with { 5... -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Step 1: Update -
- -

DataFrame [7 x 5]

-
-
- Step 2: Update -
- -

DataFrame [7 x 5]

-
-
- Output DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
-
- - - diff --git a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.expressions.html b/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.expressions.html deleted file mode 100644 index 5bdff76103..0000000000 --- a/docs/StardustDocs/resources/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.expressions.html +++ /dev/null @@ -1,809 +0,0 @@ - - - - - -
- df.add("fullName") { name.firstName + " " + name.lastName } -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Output DataFrame: rowsCount = 7, columnsCount = 6 -
- -

-
-
-
-
- df.update { weight }.at(1, 3, 4).with { prev()?.weight } -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Step 1: Update -
- -

DataFrame [7 x 5]

-
-
- Step 2: Update -
- -

DataFrame [7 x 5]

-
-
- Output DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
-
-
- df.pivot { city }.with { name.lastName.uppercase() } -
- Input DataFrame: rowsCount = 7, columnsCount = 5 -
- -

-
-
- Step 1: Pivot -
- -

-
-
- Output DataRow -
- -

-
-
-
- - - diff --git a/docs/StardustDocs/topics/_shadow_resources.md b/docs/StardustDocs/topics/_shadow_resources.md index e000fc5e0e..c0d1767fb2 100644 --- a/docs/StardustDocs/topics/_shadow_resources.md +++ b/docs/StardustDocs/topics/_shadow_resources.md @@ -326,3 +326,15 @@ + + + + + + + + + + + + diff --git a/docs/StardustDocs/topics/concepts/DataRow.md b/docs/StardustDocs/topics/concepts/DataRow.md index 8249bba53b..ca4d36a9a3 100644 --- a/docs/StardustDocs/topics/concepts/DataRow.md +++ b/docs/StardustDocs/topics/concepts/DataRow.md @@ -1,5 +1,5 @@ [//]: # (title: DataRow) - + `DataRow` represents a single record, one piece of data within a [`DataFrame`](DataFrame.md) @@ -7,76 +7,375 @@ -* `index(): Int` — sequential row number in [`DataFrame`](DataFrame.md), starts from 0 -* `prev(): DataRow?` — previous row (`null` for the first row) -* `next(): DataRow?` — next row (`null` for the last row) -* `diff(T) { rowExpression }: T / diffOrNull { rowExpression }: T?` — difference between the results of a [row expression](DataRow.md#row-expressions) calculated for current and previous rows -* `explode(columns): DataFrame` — spread lists and [`DataFrame`](DataFrame.md) objects vertically into new rows -* `values(): List` — list of all cell values from the current row -* `valuesOf(): List` — list of values of the given type -* `columnsCount(): Int` — number of columns -* `columnNames(): List` — list of all column names -* `columnTypes(): List` — list of all column types -* `namedValues(): List>` — list of name-value pairs where `name` is a column name and `value` is cell value -* `namedValuesOf(): List>` — list of name-value pairs where value has given type -* `transpose(): DataFrame>` — [`DataFrame`](DataFrame.md) of two columns: `name: String` is column names and `value: Any?` is cell values -* `transposeTo(): DataFrame>`— [`DataFrame`](DataFrame.md) of two columns: `name: String` is column names and `value: T` is cell values -* `getRow(Int): DataRow` — row from [`DataFrame`](DataFrame.md) by row index -* `getRows(Iterable): DataFrame` — [`DataFrame`](DataFrame.md) with subset of rows selected by absolute row index. -* `relative(Iterable): DataFrame` — [`DataFrame`](DataFrame.md) with subset of rows selected by relative row index: `relative(-1..1)` will return previous, current and next row. Requested indices will be coerced to the valid range and invalid indices will be skipped -* `getValue(columnName)` — cell value of type `T` by this row and given `columnName` -* `getValueOrNull(columnName)` — cell value of type `T?` by this row and given `columnName` or `null` if there's no such column -* `get(column): T` — cell value by this row and given `column` -* `String.invoke(): T` — cell value of type `T` by this row and given `this` column name -* `ColumnPath.invoke(): T` — cell value of type `T` by this row and given `this` column path -* `ColumnReference.invoke(): T` — cell value of type `T` by this row and given `this` column -* `df()` — [`DataFrame`](DataFrame.md) that current row belongs to +* `index(): Int` — sequential row number in [`DataFrame`](DataFrame.md), starts from 0; +* `prev(): DataRow?` — previous row (`null` for the first row); +* `next(): DataRow?` — next row (`null` for the last row); +* `diff(T) { rowExpression }: T / diffOrNull { rowExpression }: T?` — difference between the results +of a [row expression](DataRow.md#row-expressions) calculated for the current and previous rows; +* `explode(columns): DataFrame` — spread lists and [`DataFrame`](DataFrame.md) objects vertically into new rows; +* `values(): List` — list of all cell values from the current row; +* `valuesOf(): List` — list of values of the given type ; +* `columnsCount(): Int` — number of columns; +* `columnNames(): List` — list of all column names; +* `columnTypes(): List` — list of all column types; +* `namedValues(): List>` — list of name-value pairs where `name` is a column name +and `value` is a cell value; +* `namedValuesOf(): List>` — list of name-value pairs where the value has the given type; +* `transpose(): DataFrame>` — [`DataFrame`](DataFrame.md) with two columns: `name: String` for column names +and `value: Any?` for cell values; +* `transposeTo(): DataFrame>` — [`DataFrame`](DataFrame.md) with two columns: `name: String` for column names +and `value: T` for cell values; +* `getRow(Int): DataRow` — row from the [`DataFrame`](DataFrame.md) by a row index; +* `getRows(Iterable): DataFrame` — [`DataFrame`](DataFrame.md) with a subset of rows selected by absolute row indices; +* `relative(Iterable): DataFrame` — [`DataFrame`](DataFrame.md) with a subset of rows selected by relative row indices: +`relative(-1..1)` will return the previous, current, and next row. Requested indices will be coerced to the valid range +and invalid indices will be skipped; +* `getValue(columnName)` — cell value of type `T` by this row and the given `columnName`; +* `getValueOrNull(columnName)` — cell value of type `T?` by this row +and the given `columnName` or `null` if there's no such column; +* `get(column): T` — cell value by this row and the given `column`; +* `String.invoke(): T` — cell value of type `T` by this row and the given `this` column name; +* `ColumnPath.invoke(): T` — cell value of type `T` by this row and the given `this` column path; +* `ColumnReference.invoke(): T` — cell value of type `T` by this row and the given `this` column; +* `df()` — [`DataFrame`](DataFrame.md) that the current row belongs to. +The following dataframe will be used in the examples below: + + + +```kotlin +df +``` + + + + ## Row expressions -Row expressions provide a value for every row of [`DataFrame`](DataFrame.md) and are used in [add](add.md), [filter](filter.md), [forEach](iterate.md), [update](update.md) and other operations. +Row expressions provide a value for every row of [`DataFrame`](DataFrame.md) +and are used in [add](add.md), [filter](filter.md), [forEach](iterate.md), [update](update.md), and other operations. +There are two types of row expressions, differing in what the `it` argument refers to: + +### `RowExpression` +`RowExpression` computes a new value for every selected cell given the [`DataRow`](DataRow.md) of that cell. +Both `this` and `it` keywords in `RowExpression` refer to the same [`DataRow`](DataRow.md). +Row values can be accessed with or without these keywords. - +`RowExpression` signature: ```DataRow.(DataRow) -> T```. + +#### `RowExpression` examples + +##### add {collapsible="true"} + + + + ```kotlin // Row expression computes values for a new column df.add("fullName") { name.firstName + " " + name.lastName } +``` + + + + +```kotlin +// Row expression computes values for a new column +df.add("fullName") { "name"["firstName"] + " " + "name"["lastName"] } +``` + + + + + +##### pivot {collapsible="true"} -// Row expression computes updated values -df.update { weight }.at(1, 3, 4).with { prev()?.weight } + + + +```kotlin // Row expression computes cell content for values of pivoted column df.pivot { city }.with { name.lastName.uppercase() } ``` - + + + +```kotlin +// Row expression computes cell content for values of pivoted column +df.pivot { city }.with { "name"["lastName"]().uppercase() } +``` + + + + + +### `RowValueExpression` +`RowValueExpression` computes a new value for every selected cell given the [`DataRow`](DataRow.md) of that cell +and the current value of that cell. `this` refers to the current [`DataRow`](DataRow.md), +and `it` refers to the current value of the cell. +`RowValueExpression` is used after selecting columns in functions such as [`update`](update.md) or [`convert`](convert.md). + +`RowValueExpression` signature: ```DataRow.(C) -> T```. + +#### `RowValueExpression` examples + +##### update (expression) {collapsible="true"} + + + + + +```kotlin +// "it" refers to the current "weight" cell, and "prev()" is called on the row "this" +df.update { weight }.at(2, 3, 5).with { it ?: prev()?.weight } +``` + + + + +```kotlin +// "it" refers to the current "weight" cell, and "prev()" is called on the row "this" +df.update("weight").at(2, 3, 5).with { it ?: prev()?.get("weight") } +``` + + + + +##### convert {collapsible="true"} + + + + +```kotlin +// "it" refers to the current "city" cell +df.convert { city }.notNull { it.uppercase() } +``` + + + + +```kotlin +// "it" refers to the current "city" cell +df.convert("city").notNull { (it as String).uppercase() } +``` -Row expression signature: ```DataRow.(DataRow) -> T```. Row values can be accessed with or without ```it``` keyword. Implicit and explicit argument represent the same `DataRow` object. + + + ## Row conditions -Row condition is a special case of [row expression](#row-expressions) that returns `Boolean`. +Row condition is a special case of [row expression](#row-expressions) that returns `Boolean`. +There are two types of row conditions: + +### `RowFilter` +`RowFilter` evaluates a [`DataRow`](DataRow.md) +and returns a `Boolean` indicating whether the row should be included in the result. +Both `this` and `it` in `RowFilter` refer to the same [`DataRow`](DataRow.md). +`RowFilter` is used in functions such as [`filter`](filter.md), [`drop`](drop.md), +[`first`](first.md), and [`count`](count.md). + +`RowFilter` signature: ```DataRow.(DataRow) -> Boolean```. + +#### `RowFilter` examples + +##### filter {collapsible="true"} + + + +```kotlin +df +``` + + + - + + + ```kotlin -// Row condition is used to filter rows by index -df.filter { index() % 5 == 0 } +// Row filter is used to filter rows +df.filter { name.firstName == "Alice" && age >= 18 } +``` -// Row condition is used to drop rows where `age` is the same as in the previous row -df.drop { diffOrNull { age } == 0 } + + -// Row condition is used to filter rows for value update -df.update { weight }.where { index() > 4 && city != "Paris" }.with { 50 } +```kotlin +// Row filter is used to filter rows +df.filter { "name"["firstName"]() == "Alice" && "age"() >= 18 } ``` - + + + +##### drop {collapsible="true"} -Row condition signature: ```DataRow.(DataRow) -> Boolean``` + + + +```kotlin +// Row filter is used to drop rows where `city` or `weight` is null +df.drop { city == null || weight == null } +``` + + + +```kotlin +// Row filter is used to drop rows where `city` or `weight` is null +df.drop { "city"() == null || "weight"() == null } +``` + + + + + +##### first {collapsible="true"} + + + + + +```kotlin +// Row filter is used to take the first row where `city` is Milan +df.first { city == "Milan" } +``` + + + + +```kotlin +// Row filter is used to take the first row where `city` is Milan +df.first { "city"() == "Milan" } +``` + + + + + +##### count {collapsible="true"} + + + + + +```kotlin +// Row filter is used to count happy people +df.count { isHappy } // the result is 5 +``` + + + + +```kotlin +// Row filter is used to count happy people +df.count { "isHappy"() } // the result is 5 +``` + + + + +### `RowValueFilter` +`RowValueFilter` is used after selecting columns in functions +such as [`update`](update.md), [`gather`](gather.md), and [`format`](format.md). +Like `RowFilter`, it returns a `Boolean` indicating whether the row should be included in the result. +However, unlike `RowFilter`, where both `this` and `it` refer to the current [`DataRow`](DataRow.md), +`RowValueFilter` uses the current row as `this` and can also access the selected column value from this row as `it`. + +`RowValueFilter` signature: ```DataRow.(C) -> Boolean```. + +#### `RowValueFilter` examples + +##### update (condition) {collapsible="true"} + + + + + +```kotlin +// Row value filter is used to filter rows for value update +df.update { age }.where { name.firstName == "Alice" && name.lastName == "Cooper" }.with { 16 } +``` + + + + +```kotlin +// Row value filter is used to filter rows for value update +df.update("age") + .where { + "name"["firstName"]() == "Alice" && + "name"["lastName"]() == "Cooper" + } + .with { 16 } +``` + + + + + +##### gather {collapsible="true"} + + + + + +```kotlin +// Row value filter is used to gather only unfilled profile fields +df.gather { age and city and weight and isHappy } + .where { it == null } + .into("field", "value") +``` + + + + +```kotlin +// Row value filter is used to gather only unfilled profile fields +df.gather("age", "city", "weight", "isHappy") + .where { it == null } + .into("field", "value") +``` + + + + + +##### format {collapsible="true"} + + + + + +```kotlin +// Row value filter is used to format only rows with minors +df + .format() + .where { age < 18 } + .with { background(RgbColor(242, 210, 189)) and textColor(black) } +``` + + + + +```kotlin +// Row value filter is used to format only rows with minors +df + .format() + .where { "age"() < 18 } + .with { background(RgbColor(242, 210, 189)) and textColor(black) } +``` + + + + ## Row statistics @@ -91,7 +390,7 @@ These statistics will be applied only to values of appropriate types, and incomp For example, if a [dataframe](DataFrame.md) has columns of types `String` and `Int`, `rowSum()` will compute the sum of the `Int` values in the row and ignore `String` values. -To apply statistics only to values of a particular type use `-Of` versions: +To apply statistics only to values of a particular type, use `-Of` versions: * `rowSumOf` * `rowMeanOf` * `rowStdOf` diff --git a/samples/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApiSamples.kt b/samples/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApiSamples.kt new file mode 100644 index 0000000000..6cf4916f7d --- /dev/null +++ b/samples/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/DataRowApiSamples.kt @@ -0,0 +1,292 @@ +package org.jetbrains.kotlinx.dataframe.samples.api + +import org.jetbrains.kotlinx.dataframe.api.RgbColor +import org.jetbrains.kotlinx.dataframe.api.add +import org.jetbrains.kotlinx.dataframe.api.and +import org.jetbrains.kotlinx.dataframe.api.at +import org.jetbrains.kotlinx.dataframe.api.convert +import org.jetbrains.kotlinx.dataframe.api.count +import org.jetbrains.kotlinx.dataframe.api.drop +import org.jetbrains.kotlinx.dataframe.api.filter +import org.jetbrains.kotlinx.dataframe.api.first +import org.jetbrains.kotlinx.dataframe.api.format +import org.jetbrains.kotlinx.dataframe.api.gather +import org.jetbrains.kotlinx.dataframe.api.into +import org.jetbrains.kotlinx.dataframe.api.notNull +import org.jetbrains.kotlinx.dataframe.api.perRowCol +import org.jetbrains.kotlinx.dataframe.api.pivot +import org.jetbrains.kotlinx.dataframe.api.prev +import org.jetbrains.kotlinx.dataframe.api.toDataFrame +import org.jetbrains.kotlinx.dataframe.api.update +import org.jetbrains.kotlinx.dataframe.api.where +import org.jetbrains.kotlinx.dataframe.api.with +import org.jetbrains.kotlinx.dataframe.samples.DataFrameSampleHelper +import org.jetbrains.kotlinx.dataframe.util.defaultHeaderFormatting +import org.junit.Test + +class DataRowApiSamples : DataFrameSampleHelper("dataRowApi", "api") { + private val df = peopleDf + + private fun lastNameToColorUpdateCondition(name: String): RgbColor? = + when (name) { + "Cooper" -> RgbColor(189, 206, 233) + else -> null + } + + private fun lastNameToColorUpdateExpression(name: String): RgbColor? = + when (name) { + "Daniels" -> RgbColor(189, 206, 233) + "Chaplin" -> RgbColor(242, 210, 189) + "Wolf" -> RgbColor(233, 199, 220) + else -> null + } + + private fun lastNameToColorFilter(name: String): RgbColor? = + when (name) { + "Wolf" -> RgbColor(242, 210, 189) + "Smith" -> RgbColor(233, 199, 220) + else -> null + } + + @Test + fun dfDataRow() { + // SampleStart + df + // SampleEnd + .saveDfHtmlSample() + } + + @Test + fun addWithExpression_properties() { + // SampleStart + // Row expression computes values for a new column + df.add("fullName") { name.firstName + " " + name.lastName } + // SampleEnd + .defaultHeaderFormatting { fullName } + .saveDfHtmlSample() + } + + @Test + fun addWithExpression_strings() { + // SampleStart + // Row expression computes values for a new column + df.add("fullName") { "name"["firstName"] + " " + "name"["lastName"] } + // SampleEnd + } + + @Test + fun updateWithExpression_properties() { + // SampleStart + // "it" refers to the current "weight" cell, and "prev()" is called on the row "this" + df.update { weight }.at(2, 3, 5).with { it ?: prev()?.weight } + // SampleEnd + .format().perRowCol { row, _ -> + val lastName = df[row.index()].name.lastName + lastNameToColorUpdateExpression(lastName)?.let { color -> + background(color) and textColor(black) + } + } + .saveDfHtmlSample() + } + + @Test + fun updateWithExpression_strings() { + // SampleStart + // "it" refers to the current "weight" cell, and "prev()" is called on the row "this" + df.update("weight").at(2, 3, 5).with { it ?: prev()?.get("weight") } + // SampleEnd + } + + @Test + fun convertExpression_properties() { + // SampleStart + // "it" refers to the current "city" cell + df.convert { city }.notNull { it.uppercase() } + // SampleEnd + .saveDfHtmlSample() + } + + @Test + fun convertExpression_strings() { + // SampleStart + // "it" refers to the current "city" cell + df.convert("city").notNull { (it as String).uppercase() } + // SampleEnd + } + + @Test + fun pivotWithExpression_properties() { + // SampleStart + // Row expression computes cell content for values of pivoted column + df.pivot { city }.with { name.lastName.uppercase() } + // SampleEnd + .toDataFrame() + .saveDfHtmlSample() + } + + @Test + fun pivotWithExpression_strings() { + // SampleStart + // Row expression computes cell content for values of pivoted column + df.pivot { city }.with { "name"["lastName"]().uppercase() } + // SampleEnd + } + + @Test + fun filterWithConditionDf() { + df.format().perRowCol { row, _ -> + val lastName = row.name.lastName + lastNameToColorFilter(lastName)?.let { color -> + background(color) and textColor(black) + } + } + .saveDfHtmlSample() + } + + @Test + fun filterWithCondition_properties() { + // SampleStart + // Row filter is used to filter rows + df.filter { name.firstName == "Alice" && age >= 18 } + // SampleEnd + .format().perRowCol { row, _ -> + val lastName = row.name.lastName + lastNameToColorFilter(lastName)?.let { color -> + background(color) and textColor(black) + } + } + .saveDfHtmlSample() + } + + @Test + fun filterWithCondition_strings() { + // SampleStart + // Row filter is used to filter rows + df.filter { "name"["firstName"]() == "Alice" && "age"() >= 18 } + // SampleEnd + } + + @Test + fun dropWithCondition_properties() { + // SampleStart + // Row filter is used to drop rows where `city` or `weight` is null + df.drop { city == null || weight == null } + // SampleEnd + .saveDfHtmlSample() + } + + @Test + fun dropWithCondition_strings() { + // SampleStart + // Row filter is used to drop rows where `city` or `weight` is null + df.drop { "city"() == null || "weight"() == null } + // SampleEnd + } + + @Test + fun firstWithCondition_properties() { + // SampleStart + // Row filter is used to take the first row where `city` is Milan + df.first { city == "Milan" } + // SampleEnd + .toDataFrame() + .saveDfHtmlSample() + } + + @Test + fun firstWithCondition_strings() { + // SampleStart + // Row filter is used to take the first row where `city` is Milan + df.first { "city"() == "Milan" } + // SampleEnd + } + + @Test + fun countWithCondition_properties() { + // SampleStart + // Row filter is used to count happy people + df.count { isHappy } // the result is 5 + // SampleEnd + } + + @Test + fun countWithCondition_strings() { + // SampleStart + // Row filter is used to count happy people + df.count { "isHappy"() } // the result is 5 + // SampleEnd + } + + @Test + fun updateWithCondition_properties() { + // SampleStart + // Row value filter is used to filter rows for value update + df.update { age }.where { name.firstName == "Alice" && name.lastName == "Cooper" }.with { 16 } + // SampleEnd + .format().perRowCol { row, _ -> + val lastName = df[row.index()].name.lastName + lastNameToColorUpdateCondition(lastName)?.let { color -> + background(color) and textColor(black) + } + } + .saveDfHtmlSample() + } + + @Test + fun updateWithCondition_strings() { + // SampleStart + // Row value filter is used to filter rows for value update + df.update("age") + .where { + "name"["firstName"]() == "Alice" && + "name"["lastName"]() == "Cooper" + } + .with { 16 } + // SampleEnd + } + + @Test + fun gatherWithCondition_properties() { + // SampleStart + // Row value filter is used to gather only unfilled profile fields + df.gather { age and city and weight and isHappy } + .where { it == null } + .into("field", "value") + // SampleEnd + .defaultHeaderFormatting { "field"() and "value"() } + .saveDfHtmlSample() + } + + @Test + fun gatherWithCondition_strings() { + // SampleStart + // Row value filter is used to gather only unfilled profile fields + df.gather("age", "city", "weight", "isHappy") + .where { it == null } + .into("field", "value") + // SampleEnd + } + + @Test + fun formatWithCondition_properties() { + // SampleStart + // Row value filter is used to format only rows with minors + df + .format() + .where { age < 18 } + .with { background(RgbColor(242, 210, 189)) and textColor(black) } + // SampleEnd + .saveDfHtmlSample() + } + + @Test + fun formatWithCondition_strings() { + // SampleStart + // Row value filter is used to format only rows with minors + df + .format() + .where { "age"() < 18 } + .with { background(RgbColor(242, 210, 189)) and textColor(black) } + // SampleEnd + } +}