diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d2c17f7509..7858c07580 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,8 +1,10 @@ name: Build docs on: + # Specify to run a workflow manually from the Actions tab on GitHub. workflow_dispatch: +# Gives the workflow permissions to clone the repo and create a page deployment permissions: id-token: write pages: write @@ -20,15 +22,31 @@ env: jobs: build-job: runs-on: ubuntu-latest + container: registry.jetbrains.team/p/writerside/builder/writerside-builder:2.1.1481-p3872-df + outputs: + artifact: ${{ steps.generate-artifact.outputs.artifact }} steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Build Writerside docs using Docker - uses: ./.github/writerside-build - - name: Upload artifact + - name: Prepare for build + run: | + apt-get update + apt-get install -y zip + mkdir -p artifacts + - name: Build docs and include sitemap.xml + run: | + export DISPLAY=:99 + Xvfb :99 & + /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product $PRODUCT --runner github -output-dir artifacts/ || true + test -e artifacts/$ARTIFACT && echo $ARTIFACT exists + cp docs/StardustDocs/sitemap.xml artifacts/sitemap.xml + cd artifacts + zip -r $ARTIFACT sitemap.xml + working-directory: ${{ github.workspace }} + - name: Upload modified documentation artifact uses: actions/upload-artifact@v3 with: - name: artifact + name: help path: artifacts/${{ env.ARTIFACT }} retention-days: 7 - name: Upload algolia-indexes @@ -42,13 +60,14 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} + # Requires the build-job results needs: build-job runs-on: ubuntu-latest steps: - name: Download artifact uses: actions/download-artifact@v3 with: - name: artifact + name: help - name: Unzip artifact uses: montudor/action-zip@v1 with: diff --git a/.github/writerside-build/Dockerfile b/.github/writerside-build/Dockerfile deleted file mode 100644 index d48e3f09b9..0000000000 --- a/.github/writerside-build/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -# Container image that runs your code -FROM registry.jetbrains.team/p/writerside/builder/writerside-builder:2.1.1256-p3333 - -# Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -RUN chmod +x /entrypoint.sh - -# Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/writerside-build/action.yml b/.github/writerside-build/action.yml deleted file mode 100644 index 80367d1b13..0000000000 --- a/.github/writerside-build/action.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Build Writerside docs using Docker -description: Build Writerside documentation artifacts - -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/writerside-build/entrypoint.sh b/.github/writerside-build/entrypoint.sh deleted file mode 100644 index 33eb48a7f8..0000000000 --- a/.github/writerside-build/entrypoint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -set -e -/opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product $PRODUCT --runner github -output-dir artifacts/ || true -echo "Test existing of $ARTIFACT artifact" -test -e artifacts/$ARTIFACT diff --git a/README.md b/README.md index e53d03e95b..c47fef495b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Kotlin](https://img.shields.io/badge/kotlin-1.8.20-blue.svg?logo=kotlin)](http://kotlinlang.org) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/dataframe?color=blue&label=Maven%20Central)](https://search.maven.org/artifact/org.jetbrains.kotlinx/dataframe) [![GitHub License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/Kotlin/dataframe/HEAD) Kotlin Dataframe aims to reconcile Kotlin's static typing with the dynamic nature of data by utilizing both the full power of the Kotlin language and the opportunities provided by intermittent code execution in Jupyter notebooks and REPL. @@ -30,7 +31,7 @@ Explore [**documentation**](https://kotlin.github.io/dataframe/overview.html) fo plugins { // Optional Gradle plugin for enhanced type safety and schema generation // https://kotlin.github.io/dataframe/gradle.html - id 'org.jetbrains.kotlinx.dataframe' version '0.11.0' + id 'org.jetbrains.kotlinx.dataframe' version '0.11.1' } repositories { @@ -38,7 +39,7 @@ repositories { } dependencies { - implementation 'org.jetbrains.kotlinx:dataframe:0.11.0' + implementation 'org.jetbrains.kotlinx:dataframe:0.11.1' } ``` @@ -48,7 +49,7 @@ dependencies { plugins { // Optional Gradle plugin for enhanced type safety and schema generation // https://kotlin.github.io/dataframe/gradle.html - id("org.jetbrains.kotlinx.dataframe") version "0.11.0" + id("org.jetbrains.kotlinx.dataframe") version "0.11.1" } repositories { @@ -56,7 +57,7 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlinx:dataframe:0.11.0") + implementation("org.jetbrains.kotlinx:dataframe:0.11.1") } ``` @@ -67,11 +68,11 @@ dependencies { plugins { // Optional Gradle plugin for enhanced type safety and schema generation // https://kotlin.github.io/dataframe/gradle.html - id 'org.jetbrains.kotlinx.dataframe' version '0.11.0' + id 'org.jetbrains.kotlinx.dataframe' version '0.11.1' } dependencies { - implementation 'org.jetbrains.kotlinx:dataframe:0.11.0' + implementation 'org.jetbrains.kotlinx:dataframe:0.11.1' } android { @@ -115,11 +116,11 @@ tasks.withType(KotlinCompile).configureEach { plugins { // Optional Gradle plugin for enhanced type safety and schema generation // https://kotlin.github.io/dataframe/gradle.html - id("org.jetbrains.kotlinx.dataframe") version "0.11.0" + id("org.jetbrains.kotlinx.dataframe") version "0.11.1" } dependencies { - implementation("org.jetbrains.kotlinx:dataframe:0.11.0") + implementation("org.jetbrains.kotlinx:dataframe:0.11.1") } android { @@ -189,6 +190,7 @@ This table shows the mapping between main library component versions and minimum | 0.10.0 | 8 | 1.8.20 | 0.11.0-358 | 3.0.0 | 11.0.0 | | 0.10.1 | 8 | 1.8.20 | 0.11.0-358 | 3.0.0 | 11.0.0 | | 0.11.0 | 8 | 1.8.20 | 0.11.0-358 | 3.0.0 | 11.0.0 | +| 0.11.1 | 8 | 1.8.20 | 0.11.0-358 | 3.0.0 | 11.0.0 | ## Usage example **Create:** diff --git a/RELEASE_CHECK_LIST.md b/RELEASE_CHECK_LIST.md index 0665b64cb1..2b3c8e1e24 100644 --- a/RELEASE_CHECK_LIST.md +++ b/RELEASE_CHECK_LIST.md @@ -29,3 +29,8 @@ 17. Create Release from the release tag on GitHub 18. Update a KDF version in the [Kotlin Jupyter Descriptor](https://github.com/Kotlin/kotlin-jupyter-libraries/blob/master/dataframe.json). Now the Renovate bot doing this 19. Update DataFrame version in gradle.properties file for next release cycle (i.e. 0.10.0 -> 0.11.0) +20. Update deprecated functions in [deprecationMessages.kt](/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt) + such that + - `Level.WARNING` messages are changed to `Level.ERROR` + - `Level.ERROR` messages and their functions are removed. + - Update regions in the file accordingly. diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5b3a57cfb7..8d3d548133 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -154,6 +154,28 @@ tasks.withType { dependsOn(copySamplesOutputs) } +// This task installs the pre-commit hook to the local machine the first time the project is built +// The pre-commit hook contains the command to run processKDocsMain before each commit +val installGitPreCommitHook by tasks.creating(Copy::class) { + doNotTrackState(/* reasonNotToTrackState = */ "Fails on TeamCity otherwise.") + + val gitHooksDir = File(rootProject.rootDir, ".git/hooks") + if (gitHooksDir.exists()) { + from(File(rootProject.rootDir, "gradle/scripts/pre-commit")) + into(gitHooksDir) + fileMode = 755 + } else { + logger.lifecycle("'.git/hooks' directory not found. Skipping installation of pre-commit hook.") + } + +} +tasks.named("assemble") { + dependsOn(installGitPreCommitHook) +} + +// region docPreprocessor + +// This task is used to add all generated sources (from processKDocsMain) to git val generatedSourcesFolderName = "generated-sources" val addGeneratedSourcesToGit by tasks.creating(GitTask::class) { directory.set(file(".")) @@ -162,8 +184,8 @@ val addGeneratedSourcesToGit by tasks.creating(GitTask::class) { } // Backup the kotlin source files location -val kotlinMainSources = kotlin.sourceSets.main.get().kotlin.sourceDirectories -val kotlinTestSources = kotlin.sourceSets.test.get().kotlin.sourceDirectories +val kotlinMainSources: FileCollection = kotlin.sourceSets.main.get().kotlin.sourceDirectories +val kotlinTestSources: FileCollection = kotlin.sourceSets.test.get().kotlin.sourceDirectories fun pathOf(vararg parts: String) = parts.joinToString(File.separator) @@ -176,12 +198,15 @@ val processKDocsMain by creatingProcessDocTask( processors = listOf( INCLUDE_DOC_PROCESSOR, INCLUDE_FILE_DOC_PROCESSOR, - INCLUDE_ARG_DOC_PROCESSOR, + ARG_DOC_PROCESSOR, COMMENT_DOC_PROCESSOR, SAMPLE_DOC_PROCESSOR, ) + arguments += ARG_DOC_PROCESSOR_LOG_NOT_FOUND to false + task { + group = "KDocs" doLast { // ensure generated sources are added to git addGeneratedSourcesToGit.executeCommand() @@ -224,6 +249,18 @@ tasks.withType { } } +// If we want to use Dokka, make sure to use the preprocessed sources +tasks.withType { + dependsOn(processKDocsMain) + dokkaSourceSets { + all { + sourceRoot(processKDocsMain.target.get()) + } + } +} + +// endregion + korro { docs = fileTree(rootProject.rootDir) { include("docs/StardustDocs/topics/*.md") @@ -370,13 +407,3 @@ dataframes { name = "org.jetbrains.kotlinx.dataframe.samples.api.Repository" } } - -// If we want to use Dokka, make sure to use the preprocessed sources -tasks.withType { - dependsOn(processKDocsMain) - dokkaSourceSets { - all { - sourceRoot(processKDocsMain.target.get()) - } - } -} diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt index 6fbcbe8c8d..794b2797cd 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt @@ -238,7 +238,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the first column that adheres to the given [condition\]. @@ -327,7 +327,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the last column that adheres to the given [condition\]. @@ -416,7 +416,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the single column that adheres to the given [condition\]. @@ -505,7 +505,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * `df.`[select][DataFrame.select]` { `{@includeArg [CommonSubsetOfColumnsDocs.Example]}` }` + * `df.`[select][DataFrame.select]` { `{@getArg [CommonSubsetOfColumnsDocs.Example]}` }` * * @param [endInclusive\] The last column in the subset. * @receiver The first column in the subset. @@ -717,9 +717,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * `df.`[select][select]` { `[col][col]`({@includeArg [CommonColDocs.Arg]}) }` + * `df.`[select][select]` { `[col][col]`({@getArg [CommonColDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[col][col]`({@includeArg [CommonColDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[col][col]`({@getArg [CommonColDocs.Arg]}) }` * * @return A [ColumnAccessor] for the column with the given argument. * @see [column\] @@ -1020,9 +1020,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * `df.`[select][select]` { `[colGroup][colGroup]`({@includeArg [CommonColGroupDocs.Arg]}) }` + * `df.`[select][select]` { `[colGroup][colGroup]`({@getArg [CommonColGroupDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[colGroup][colGroup]`({@includeArg [CommonColGroupDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[colGroup][colGroup]`({@getArg [CommonColGroupDocs.Arg]}) }` * * @return A [ColumnAccessor] for the column group with the given argument. * @see [columnGroup\] @@ -1327,9 +1327,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * an accessor for a frame column inside a [ColumnGroup]. * * #### For example: - * `df.`[select][select]` { `[frameCol][frameCol]`({@includeArg [CommonFrameColDocs.Arg]}) }` + * `df.`[select][select]` { `[frameCol][frameCol]`({@getArg [CommonFrameColDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[frameCol][frameCol]`({@includeArg [CommonFrameColDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[frameCol][frameCol]`({@getArg [CommonFrameColDocs.Arg]}) }` * * @return A [ColumnAccessor] for the frame column with the given argument. * @see [frameColumn\] @@ -1636,7 +1636,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonColsDocs.Examples]} + * {@getArg [CommonColsDocs.Examples]} * */ private interface CommonColsDocs { @@ -1664,7 +1664,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonColsDocs.Examples][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.CommonColsDocs.Examples]} + * {@getArg [CommonColsDocs.Examples][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.CommonColsDocs.Examples]} * * * @param [predicate\] A [ColumnFilter function][ColumnFilter] that takes a [ColumnReference] and returns a [Boolean]. @@ -1695,11 +1695,11 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonColsDocs.Examples][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.CommonColsDocs.Examples]} + * {@getArg [CommonColsDocs.Examples][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.CommonColsDocs.Examples]} * * - * @param [firstCol\] A {@includeArg [AccessorType]} that points to a column. - * @param [otherCols\] Optional additional {@includeArg [AccessorType]}s that point to columns. + * @param [firstCol\] A {@getArg [AccessorType]} that points to a column. + * @param [otherCols\] Optional additional {@getArg [AccessorType]}s that point to columns. * @return A [ColumnSet] containing the columns that [firstCol\] and [otherCols\] point to. */ interface Vararg { @@ -3777,7 +3777,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonAllDocs.Examples]} + * {@getArg [CommonAllDocs.Examples]} * * @see [cols\] */ @@ -3804,7 +3804,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * #### Examples for this overload: * * `df.`[select][select]` { `[cols][cols]` { "a" in `[name][ColumnWithPath.name]` }.`[all][all]`() }` - * ## ‎ + * + *      + * * NOTE: This is an identity call and can be omitted in most cases. However, it can still prove useful * for readability or in combination with [recursively]. * @@ -3938,7 +3940,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonRecursivelyDocs.Examples]} + * {@getArg [CommonRecursivelyDocs.Examples]} * * @param [includeTopLevel\] Whether to include the top-level columns in the result. `true` by default. */ @@ -4440,7 +4442,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4448,7 +4452,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4479,7 +4485,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4487,7 +4495,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4640,7 +4650,9 @@ public inline fun SingleColumn<*>.dfsOf(noinline filter: (ColumnWith * `df.`[select][DataFrame.select]` { `[colsOf][colsOf]`<`[Int][Int]`>() }` * * `df.`[select][DataFrame.select]` { `[colsOf][colsOf]`<`[Int][Int]`> { it.`[size][DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf] can also be called on existing columns: * * `df.`[select][DataFrame.select]` { myColumnGroup.`[colsOf][colsOf]`<`[Int][Int]`>() }` @@ -4648,7 +4660,9 @@ public inline fun SingleColumn<*>.dfsOf(noinline filter: (ColumnWith * `df.`[select][DataFrame.select]` { "myColumnGroup"().`[colsOf][colsOf]`<`[Int][Int]`> { it.`[size][DataColumn.size]` > 10 } }` * * `df.`[select][DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4668,7 +4682,9 @@ internal interface ColsOf * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4676,7 +4692,9 @@ internal interface ColsOf * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4704,7 +4722,9 @@ private interface CommonColsOfDocs { * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4712,7 +4732,9 @@ private interface CommonColsOfDocs { * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4748,7 +4770,9 @@ public fun ColumnSet<*>.colsOf( * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4756,7 +4780,9 @@ public fun ColumnSet<*>.colsOf( * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4789,7 +4815,9 @@ public inline fun ColumnSet<*>.colsOf(noinline filter: (DataColumn() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4797,7 +4825,9 @@ public inline fun ColumnSet<*>.colsOf(noinline filter: (DataColumn().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * @@ -4833,7 +4863,9 @@ public fun SingleColumn<*>.colsOf( * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { `[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` - * ## ‎ + * + *      + * * Alternatively, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also be called on existing columns: * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { myColumnGroup.`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`>() }` @@ -4841,7 +4873,9 @@ public fun SingleColumn<*>.colsOf( * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { "myColumnGroup"().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Int][Int]`> { it.`[size][org.jetbrains.kotlinx.dataframe.DataColumn.size]` > 10 } }` * * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` { (Type::myColumnGroup)().`[colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf]`<`[Double][Double]`>() }` - * ## ‎ + * + *      + * * Finally, [colsOf][org.jetbrains.kotlinx.dataframe.api.colsOf] can also take a [KType] argument instead of a reified type. * This is useful when the type is not known at compile time or when the API function cannot be inlined. * diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt index cc7bc35fc7..8828763626 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt @@ -53,7 +53,9 @@ private interface SetFillNullsOperationArg * Check out the [`fillNulls` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNulls.Usage]. * * For more information: [See `fillNulls` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnulls) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * * ## This Fill Nulls Overload @@ -70,7 +72,9 @@ private interface CommonFillNullsFunctionDoc * Check out the [`fillNulls` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNulls.Usage]. * * For more information: [See `fillNulls` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnulls) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * * ## This Fill Nulls Overload @@ -104,7 +108,9 @@ public fun DataFrame.fillNulls(columns: ColumnsSelector): Updat * Check out the [`fillNulls` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNulls.Usage]. * * For more information: [See `fillNulls` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnulls) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * * ## This Fill Nulls Overload @@ -130,7 +136,9 @@ public fun DataFrame.fillNulls(vararg columns: String): Update = * Check out the [`fillNulls` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNulls.Usage]. * * For more information: [See `fillNulls` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnulls) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * * ## This Fill Nulls Overload @@ -158,7 +166,9 @@ public fun DataFrame.fillNulls(vararg columns: KProperty): Update DataFrame.fillNaNs(columns: ColumnsSelector): Update< * Check out the [`fillNaNs` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNaNs.Usage]. * * For more information: [See `fillNaNs` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnans) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Fill NaNs Overload * Select columns using their [column names][String] @@ -327,7 +343,9 @@ public fun DataFrame.fillNaNs(vararg columns: String): Update = * Check out the [`fillNaNs` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNaNs.Usage]. * * For more information: [See `fillNaNs` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillnans) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Fill NaNs Overload * Select columns using [KProperties][KProperty] ([KProperties API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.KPropertiesApi]). @@ -353,7 +371,9 @@ public fun DataFrame.fillNaNs(vararg columns: KProperty): Update DataFrame.fillNA(columns: ColumnsSelector): Update DataFrame.fillNA(vararg columns: String): Update = * Check out the [`fillNA` Operation Usage][org.jetbrains.kotlinx.dataframe.api.FillNA.Usage]. * * For more information: [See `fillNA` on the documentation website.](https://kotlin.github.io/dataframe/fill.html#fillna) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Fill NA Overload * Select columns using [KProperties][KProperty] ([KProperties API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.KPropertiesApi]). @@ -526,7 +554,9 @@ public fun DataFrame.fillNA(vararg columns: KProperty): Update) { public fun randomBoolean(nrow: Int): AnyFrame = fillNotNull(nrow) { Random.nextBoolean() } } +/** + * Helper class for implementing operations when column names can be potentially duplicated. + * For example, operations involving multiple dataframes, computed columns or parsing some third-party data + */ +public class DynamicDataFrameBuilder { + private var cols: MutableList = mutableListOf() + private val generator = ColumnNameGenerator() + + public fun add(col: AnyCol): String { + val uniqueName = if (col.name().isEmpty()) { + generator.addUnique(unnamedColumnPrefix) + } else { + generator.addUnique(col.name()) + } + val renamed = if (uniqueName != col.name()) { + col.rename(uniqueName) + } else { + col + } + cols.add(renamed) + return uniqueName + } + + public fun toDataFrame(): AnyFrame { + return dataFrameOf(cols) + } +} + /** * Returns [DataFrame] with no rows and no columns. * diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt index 99f64b4d75..cea811b37e 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt @@ -17,7 +17,7 @@ import kotlin.reflect.KType import kotlin.reflect.typeOf /** - * Specifies how to handle columns in original dataframe that were not mathced to any column in destination dataframe schema. + * Specifies how to handle columns in original dataframe that were not matched to any column in destination dataframe schema. */ public enum class ExcessiveColumns { /** diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt index 6fd14236b4..c985766b28 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt @@ -10,7 +10,9 @@ public fun DataFrame.join( other: DataFrame, type: JoinType = JoinType.Inner, selector: JoinColumnsSelector? = null -): DataFrame = joinImpl(other, type, true, selector) +): DataFrame { + return joinImpl(other, type, addNewColumns = type.addNewColumns, selector) +} public fun DataFrame.join( other: DataFrame, @@ -116,10 +118,17 @@ public enum class JoinType { Left, // all data from left data frame, nulls for mismatches in right data frame Right, // all data from right data frame, nulls for mismatches in left data frame Inner, // only matched data from right and left data frame + Filter, // only matched data from left data frame Full, // all data from left and from right data frame, nulls for any mismatches Exclude // mismatched rows from left data frame } +internal val JoinType.addNewColumns: Boolean + get() = when (this) { + JoinType.Filter, JoinType.Exclude -> false + JoinType.Left, JoinType.Right, JoinType.Inner, JoinType.Full -> true + } + public val JoinType.allowLeftNulls: Boolean get() = this == JoinType.Right || this == JoinType.Full public val JoinType.allowRightNulls: Boolean get() = this == JoinType.Left || this == JoinType.Full || this == JoinType.Exclude diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt new file mode 100644 index 0000000000..b5d4ef8f4c --- /dev/null +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt @@ -0,0 +1,50 @@ +package org.jetbrains.kotlinx.dataframe.api + +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.DataRow +import org.jetbrains.kotlinx.dataframe.Selector +import org.jetbrains.kotlinx.dataframe.impl.api.joinWithImpl + +public interface JoinedDataRow : DataRow { + public val right: DataRow +} + +public typealias JoinExpression = Selector, Boolean> + +public fun DataFrame.joinWith( + right: DataFrame, + type: JoinType = JoinType.Inner, + joinExpression: JoinExpression +): DataFrame { + return joinWithImpl(right, type, addNewColumns = type.addNewColumns, joinExpression) +} + +public fun DataFrame.innerJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Inner, joinExpression) + +public fun DataFrame.leftJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Left, joinExpression) + +public fun DataFrame.rightJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Right, joinExpression) + +public fun DataFrame.fullJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Full, joinExpression) + +public fun DataFrame.filterJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWithImpl(right, JoinType.Inner, addNewColumns = false, joinExpression) + +public fun DataFrame.excludeJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWithImpl(right, JoinType.Exclude, addNewColumns = false, joinExpression) diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt index 795006d42d..895c14f619 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt @@ -5,11 +5,15 @@ import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.DataFrameExpression import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.Selector +import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.columns.ColumnSet +import org.jetbrains.kotlinx.dataframe.columns.FrameColumn import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.ValueColumn import org.jetbrains.kotlinx.dataframe.columns.toColumnSet +import org.jetbrains.kotlinx.dataframe.documentation.Indent +import org.jetbrains.kotlinx.dataframe.documentation.LineBreak import org.jetbrains.kotlinx.dataframe.impl.api.SortFlag import org.jetbrains.kotlinx.dataframe.impl.api.addFlag import org.jetbrains.kotlinx.dataframe.impl.api.sortByImpl @@ -56,6 +60,88 @@ public fun > DataColumn.sort(): ValueColumn = public fun > DataColumn.sortDesc(): ValueColumn = DataColumn.createValueColumn(name, values().sortedDescending(), type, defaultValue = defaultValue()) +/** + * ## Sort [DataColumn] With + * + * This function returns the sorted version of the current [ValueColumn], [FrameColumn], or [ColumnGroup] based + * on the given [Comparator]. The [comparator\] can either be given as an instance of [Comparator], or directly + * as a lambda. + * + * #### For example + * + * `df`[`[`][DataFrame.get]`"price"`[`]`][DataFrame.get]`.`[sortWith][sortWith]` { a, b -> a - b }` + * + * + *      + * + * `df.`[select][DataFrame.select]` {` + * + *     `name.`[sortWith][sortWith]`(myComparator) `[and][ColumnsSelectionDsl.and]` `[allAfter][ColumnsSelectionDsl.allAfter]`(name)` + * + * `}` + * + * @receiver The [DataColumn] to sort. This can be either a [ValueColumn], [FrameColumn], or [ColumnGroup] and will + * dictate the return type of the function. + * @param [comparator\] The [Comparator] to use for sorting the [DataColumn]. This can either be a [Comparator]<[T\]> or + * a lambda of type `(`[T][T\]`, `[T][T\]`) -> `[Int][Int]. + * @return The sorted [DataColumn] [this\] of the same type as the receiver. + */ +private interface CommonDataColumnSortWithDocs + +/** ## Sort [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] With + * + * This function returns the sorted version of the current [ValueColumn][org.jetbrains.kotlinx.dataframe.columns.ValueColumn], [FrameColumn][org.jetbrains.kotlinx.dataframe.columns.FrameColumn], or [ColumnGroup][org.jetbrains.kotlinx.dataframe.columns.ColumnGroup] based + * on the given [Comparator]. The [comparator] can either be given as an instance of [Comparator], or directly + * as a lambda. + * + * #### For example + * + * `df`[`[`][org.jetbrains.kotlinx.dataframe.DataFrame.get]`"price"`[`]`][org.jetbrains.kotlinx.dataframe.DataFrame.get]`.`[sortWith][org.jetbrains.kotlinx.dataframe.api.sortWith]` { a, b -> a - b }` + * + * + *      + * + * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` {` + * + *     `name.`[sortWith][org.jetbrains.kotlinx.dataframe.api.sortWith]`(myComparator) `[and][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.and]` `[allAfter][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.allAfter]`(name)` + * + * `}` + * + * @receiver The [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] to sort. This can be either a [ValueColumn][org.jetbrains.kotlinx.dataframe.columns.ValueColumn], [FrameColumn][org.jetbrains.kotlinx.dataframe.columns.FrameColumn], or [ColumnGroup][org.jetbrains.kotlinx.dataframe.columns.ColumnGroup] and will + * dictate the return type of the function. + * @param [comparator] The [Comparator] to use for sorting the [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn]. This can either be a [Comparator]<[T]> or + * a lambda of type `(`[T][T]`, `[T][T]`) -> `[Int][Int]. + * @return The sorted [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] [this] of the same type as the receiver. */ +public fun > C.sortWith(comparator: Comparator): C = + DataColumn.create(name, values().sortedWith(comparator), type) as C + +/** ## Sort [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] With + * + * This function returns the sorted version of the current [ValueColumn][org.jetbrains.kotlinx.dataframe.columns.ValueColumn], [FrameColumn][org.jetbrains.kotlinx.dataframe.columns.FrameColumn], or [ColumnGroup][org.jetbrains.kotlinx.dataframe.columns.ColumnGroup] based + * on the given [Comparator]. The [comparator] can either be given as an instance of [Comparator], or directly + * as a lambda. + * + * #### For example + * + * `df`[`[`][org.jetbrains.kotlinx.dataframe.DataFrame.get]`"price"`[`]`][org.jetbrains.kotlinx.dataframe.DataFrame.get]`.`[sortWith][org.jetbrains.kotlinx.dataframe.api.sortWith]` { a, b -> a - b }` + * + * + *      + * + * `df.`[select][org.jetbrains.kotlinx.dataframe.DataFrame.select]` {` + * + *     `name.`[sortWith][org.jetbrains.kotlinx.dataframe.api.sortWith]`(myComparator) `[and][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.and]` `[allAfter][org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl.allAfter]`(name)` + * + * `}` + * + * @receiver The [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] to sort. This can be either a [ValueColumn][org.jetbrains.kotlinx.dataframe.columns.ValueColumn], [FrameColumn][org.jetbrains.kotlinx.dataframe.columns.FrameColumn], or [ColumnGroup][org.jetbrains.kotlinx.dataframe.columns.ColumnGroup] and will + * dictate the return type of the function. + * @param [comparator] The [Comparator] to use for sorting the [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn]. This can either be a [Comparator]<[T]> or + * a lambda of type `(`[T][T]`, `[T][T]`) -> `[Int][Int]. + * @return The sorted [DataColumn][org.jetbrains.kotlinx.dataframe.DataColumn] [this] of the same type as the receiver. */ +public fun > C.sortWith(comparator: (T, T) -> Int): C = + sortWith(Comparator(comparator)) + // endregion // region DataFrame diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt index dbed59ac10..010d4276e0 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt @@ -12,6 +12,10 @@ import org.jetbrains.kotlinx.dataframe.impl.api.updateImpl import org.jetbrains.kotlinx.dataframe.impl.api.updateWithValuePerColumnImpl import org.jetbrains.kotlinx.dataframe.impl.headPlusArray import org.jetbrains.kotlinx.dataframe.util.ITERABLE_COLUMNS_DEPRECATION_MESSAGE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_AS_NULLABLE_MESSAGE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_AS_NULLABLE_REPLACE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_WITH_VALUE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_WITH_VALUE_REPLACE import kotlin.reflect.KProperty /** @@ -86,7 +90,9 @@ private interface SetSelectingColumnsOperationArg * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload */ @@ -108,7 +114,9 @@ private interface UpdateWithNote * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload * Select or express columns using the Column(s) Selection DSL. @@ -140,7 +148,9 @@ public fun DataFrame.update(columns: ColumnsSelector): Update DataFrame.update(vararg columns: String): Update = up * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload * Select columns using [KProperties][KProperty] ([KProperties API][org.jetbrains.kotlinx.dataframe.documentation.AccessApi.KPropertiesApi]). @@ -194,7 +206,9 @@ public fun DataFrame.update(vararg columns: KProperty): Update Update>.asFrame(expression: DataFrameExpressi asFrameImpl(expression) @Deprecated( - "Useless unless in combination with `withValue(null)`, but then users can just use `with { null }`...", - ReplaceWith("this as Update") + UPDATE_AS_NULLABLE_MESSAGE, + ReplaceWith(UPDATE_AS_NULLABLE_REPLACE), + DeprecationLevel.WARNING, ) public fun Update.asNullable(): Update = this as Update @@ -443,7 +458,7 @@ private interface UpdatePerColMap * * For example: * - * `val defaults = {@includeArg [CommonUpdatePerColMapDoc]}` + * `val defaults = {@getArg [CommonUpdatePerColMapDoc]}` * * `df.`[update][update]` { name and age }.`[where][Update.where]` { ... }.`[perCol][perCol]`(defaults)` * @@ -608,7 +623,9 @@ public fun Update.notNull(expression: UpdateExpression): * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload * ### This overload is a combination of [update] and [with][Update.with]. @@ -646,7 +663,9 @@ public fun DataFrame.update( * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload * ### This overload is a combination of [update] and [with][Update.with]. @@ -683,7 +702,9 @@ public fun DataFrame.update( * Check out the [`update` Operation Usage][org.jetbrains.kotlinx.dataframe.api.Update.Usage]. * * For more information: [See `update` on the documentation website.](https://kotlin.github.io/dataframe/update.html) - * ## ‎ + * + *      + * * The columns to update need to be selected. See [Selecting Columns][org.jetbrains.kotlinx.dataframe.documentation.SelectingColumns] for all the selecting options. * ## This Update Overload * ### This overload is a combination of [update] and [with][Update.with]. @@ -713,11 +734,11 @@ public fun DataFrame.update( update(*headPlusArray(firstCol, cols)).with(expression) /** - * Specific version of [with] that simply sets the value of each selected row to {@includeArg [CommonSpecificWithDocFirstArg]}. + * Specific version of [with] that simply sets the value of each selected row to {@getArg [CommonSpecificWithDocFirstArg]}. * * For example: * - * `df.`[update][update]` { id }.`[where][Update.where]` { it < 0 }.`{@includeArg [CommonSpecificWithDocSecondArg]}` + * `df.`[update][update]` { id }.`[where][Update.where]` { it < 0 }.`{@getArg [CommonSpecificWithDocSecondArg]}` */ private interface CommonSpecificWithDoc @@ -752,7 +773,7 @@ public fun Update.withNull(): DataFrame = with { null } public fun Update.withZero(): DataFrame = updateWithValuePerColumnImpl { 0 as C } /** - * ## With Value + * ## With Value (Deprecated) * Specific version of [with][org.jetbrains.kotlinx.dataframe.api.with] that simply sets the value of each selected row to [value]. * * For example: @@ -763,5 +784,5 @@ public fun Update.withZero(): DataFrame = updateWithValuePerColu * * @param [value] The value to set the selected rows to. In contrast to [with][Update.with], this must be the same exact type. */ -@Deprecated("Use with { value } instead", ReplaceWith("this.with { value }")) +@Deprecated(UPDATE_WITH_VALUE, ReplaceWith(UPDATE_WITH_VALUE_REPLACE), DeprecationLevel.WARNING) public fun Update.withValue(value: C): DataFrame = with { value } diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt index 2227500253..d6e3592290 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/DocumentationUrls.kt @@ -4,7 +4,7 @@ internal interface DocumentationUrls { interface NameArg - /** See {@includeArg [NameArg]} on the documentation website. */ + /** See {@getArg [NameArg]} on the documentation website. */ interface Text /** https://kotlin.github.io/dataframe */ diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt index ac95e39c2f..b8f9a5e7a2 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlinx.dataframe.ColumnExpression as DfColumnExpression internal interface ExpressionsGivenColumn { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt index ab2995e74b..c2e249195b 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt @@ -22,7 +22,7 @@ internal interface ExpressionsGivenDataFrame { * * For example: * - * {@includeArg [OperationArg]}` { `[select][DataFrame.select]` { lastName } }` + * {@getArg [OperationArg]}` { `[select][DataFrame.select]` { lastName } }` */ interface WithExample } diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt index ab7131e39c..aada644ec8 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRow.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlinx.dataframe.RowValueExpression as DfRowValueExpressio internal interface ExpressionsGivenRow { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt index a175729c52..c239ed431d 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlinx.dataframe.RowColumnExpression as DfRowColumnExpress internal interface ExpressionsGivenRowAndColumn { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt index 85dc436dca..553290b13e 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt @@ -64,7 +64,7 @@ internal interface SelectingColumnsLink internal interface SelectingColumns { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt index 6d65f010a9..ce3c71f6fb 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingRows.kt @@ -22,13 +22,13 @@ import org.jetbrains.kotlinx.dataframe.index internal interface SelectingRows { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface FirstOperationArg /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface SecondOperationArg diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt index 3660dd5397..8b49d557d7 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt @@ -1,4 +1,14 @@ package org.jetbrains.kotlinx.dataframe.documentation -/** ## ‎ */ +/** + * + *      + * + */ internal interface LineBreak + +/**      */ +internal interface Indent + +/**          */ +internal interface DoubleIndent diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt index 1881b97314..93fe387f83 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt @@ -27,7 +27,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.resolveSingle import org.jetbrains.kotlinx.dataframe.io.renderToString import kotlin.reflect.KProperty -private const val unnamedColumnPrefix = "untitled" +internal const val unnamedColumnPrefix = "untitled" internal open class DataFrameImpl(cols: List, val nrow: Int) : DataFrame, AggregatableInternal { diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt index daad1cde21..37a63906c4 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt @@ -29,12 +29,12 @@ import org.jetbrains.kotlinx.dataframe.type import kotlin.reflect.full.withNullability internal fun defaultJoinColumns(left: DataFrame, right: DataFrame): JoinColumnsSelector = - { left.columnNames().intersect(right.columnNames()).map { it.toColumnAccessor() }.let { ColumnsList(it) } } + { left.columnNames().intersect(right.columnNames().toSet()).map { it.toColumnAccessor() }.let { ColumnsList(it) } } internal fun defaultJoinColumns(dataFrames: Iterable>): JoinColumnsSelector = { dataFrames.map { it.columnNames() }.fold, Set?>(null) { set, names -> - set?.intersect(names) ?: names.toSet() + set?.intersect(names.toSet()) ?: names.toSet() }.orEmpty().map { it.toColumnAccessor() }.let { ColumnsList(it) } } @@ -114,7 +114,7 @@ internal fun DataFrame.joinImpl( // group row indices by key from right data frame val groupedRight = when (joinType) { - JoinType.Exclude -> rightJoinKeyToIndex.map { it.first to emptyList() }.toMap() + JoinType.Exclude -> rightJoinKeyToIndex.associate { it.first to emptyList() } else -> rightJoinKeyToIndex.groupBy({ it.first }) { it.second } } @@ -129,7 +129,7 @@ internal fun DataFrame.joinImpl( } // for every row index in right data frame store a flag indicating whether this row was matched by some row in left data frame - val rightMatched = Array(other.nrow) { false } + val rightMatched = BooleanArray(other.nrow) { false } // number of rows in right data frame that were not matched by any row in left data frame. Used for correct allocation of an output array var rightUnmatchedCount = other.nrow @@ -162,8 +162,8 @@ internal fun DataFrame.joinImpl( val newRightColumnsCount = newRightColumns.size val outputColumnsCount = leftColumnsCount + newRightColumnsCount - val outputData = Array>(outputColumnsCount) { arrayOfNulls(outputRowsCount) } - val hasNulls = Array(outputColumnsCount) { false } + val outputData = List>(outputColumnsCount) { arrayOfNulls(outputRowsCount) } + val hasNulls = BooleanArray(outputColumnsCount) { false } var row = 0 diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt new file mode 100644 index 0000000000..306e4678bb --- /dev/null +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt @@ -0,0 +1,98 @@ +package org.jetbrains.kotlinx.dataframe.impl.api + +import org.jetbrains.kotlinx.dataframe.DataColumn +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.DataRow +import org.jetbrains.kotlinx.dataframe.api.JoinExpression +import org.jetbrains.kotlinx.dataframe.api.JoinType +import org.jetbrains.kotlinx.dataframe.api.JoinedDataRow +import org.jetbrains.kotlinx.dataframe.api.allowLeftNulls +import org.jetbrains.kotlinx.dataframe.api.allowRightNulls +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.count +import org.jetbrains.kotlinx.dataframe.api.indices +import org.jetbrains.kotlinx.dataframe.api.toDataFrame +import org.jetbrains.kotlinx.dataframe.impl.ColumnNameGenerator +import org.jetbrains.kotlinx.dataframe.impl.DataRowImpl + +internal class JoinedDataRowImpl( + leftOwner: DataFrame, + val index: Int, + rightOwner: DataFrame, + index1: Int +) : JoinedDataRow, DataRowImpl(index, leftOwner) { + override val right: DataRow = DataRowImpl(index1, rightOwner) +} + +internal fun DataFrame.joinWithImpl( + right: DataFrame, + type: JoinType = JoinType.Inner, + addNewColumns: Boolean, + joinExpression: JoinExpression +): DataFrame { + val generator = ColumnNameGenerator(columnNames()) + if (addNewColumns) { + right.columnNames().forEach { generator.addUnique(it) } + } + val rightColumnsCount = if (addNewColumns) right.columnsCount() else 0 + val outputData = List(columnsCount() + rightColumnsCount) { mutableListOf() } + val rightMatched = BooleanArray(right.count()) { false } + for (l in indices()) { + var leftMatched = false + for (r in right.indices()) { + val joined = JoinedDataRowImpl(this, l, right, r) + val matched = joinExpression(joined, joined) + if (matched && type == JoinType.Exclude) { + leftMatched = true + break + } + if (matched) { + rightMatched[r] = true + leftMatched = true + val left = get(l).values() + for (col in left.indices) { + outputData[col].add(left[col]) + } + if (addNewColumns) { + val offset = left.size + val row = right.get(r).values() + for (col in row.indices) { + outputData[col + offset].add(row[col]) + } + } + } + } + if (!leftMatched && type.allowRightNulls) { + val left = get(l).values() + for (col in left.indices) { + outputData[col].add(left[col]) + } + if (addNewColumns) { + for (col in left.size..outputData.lastIndex) { + outputData[col].add(null) + } + } + } + } + + if (type.allowLeftNulls) { + rightMatched.forEachIndexed { row, matched -> + if (!matched) { + repeat(columnsCount()) { col -> + outputData[col].add(null) + } + val offset = columnsCount() + val rowData = right[row].values() + for (col in rowData.indices) { + outputData[offset + col].add(rowData[col]) + } + } + } + } + + val df: DataFrame<*> = outputData.mapIndexed { index, values -> + DataColumn.createWithTypeInference(generator.names[index], values) + }.toDataFrame() + + return df.cast() +} diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt index 229c13d0dc..cfbc6a73d0 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt @@ -183,7 +183,7 @@ public fun DataFrame.html(): String = toStandaloneHTML().toString() public fun DataFrame.toStandaloneHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, - getFooter: (DataFrame) -> String = { "DataFrame [${it.size}]" }, + getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions() /** @@ -192,25 +192,31 @@ public fun DataFrame.toStandaloneHTML( public fun DataFrame.toHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, - getFooter: (DataFrame) -> String = { "DataFrame [${it.size}]" }, + getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData { val limit = configuration.rowsLimit ?: Int.MAX_VALUE val footer = getFooter(this) - val bodyFooter = buildString { - val openPTag = "

" - if (limit < nrow) { + val bodyFooter = footer?.let { + buildString { + val openPTag = "

" + if (limit < nrow) { + append(openPTag) + append("... showing only top $limit of $nrow rows

") + } append(openPTag) - append("... showing only top $limit of $nrow rows

") + append(footer) + append("

") } - append(openPTag) - append(footer) - append("

") } - val tableHtml = toHtmlData(configuration, cellRenderer) + var tableHtml = toHtmlData(configuration, cellRenderer) + + if (bodyFooter != null) { + tableHtml += DataFrameHtmlData("", bodyFooter, "") + } - return tableHtml + DataFrameHtmlData("", bodyFooter, "") + return tableHtml } /** diff --git a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt index aabe39112a..67e3c38f56 100644 --- a/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt +++ b/core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/util/deprecationMessages.kt @@ -1,13 +1,41 @@ package org.jetbrains.kotlinx.dataframe.util -internal const val DF_READ_DEPRECATION_MESSAGE = "Replaced with `unfold` operation." +/* + * This file contains deprecation messages for the whole core module. + * After each release, all messages should be reviewed and updated. + * Level.WARNING -> Level.ERROR + * Level.ERROR -> Remove + */ +// region WARNING in 0.11.0, ERROR in 0.12.0 + +private const val message_0_12_0 = "Was removed in 0.12.0." + +internal const val DF_READ_DEPRECATION_MESSAGE = "Replaced with `unfold` operation. $message_0_12_0" internal const val DF_READ_REPLACE_MESSAGE = "this.unfold(*columns)" -internal const val ITERABLE_COLUMNS_DEPRECATION_MESSAGE = "Replaced with `toColumnSet()` operation." +internal const val ITERABLE_COLUMNS_DEPRECATION_MESSAGE = "Replaced with `toColumnSet()` operation. $message_0_12_0" -internal const val DIFF_DEPRECATION_MESSAGE = "Replaced to explicitly indicate nullable return value; added a new non-null overload." +// endregion -internal const val DIFF_REPLACE_MESSAGE = "this.diffOrNull(expression)" +// region WARNING in 0.12.0, ERROR in 0.13.0 +private const val message_0_13_0 = "Will be removed in 0.13.0." + +internal const val DIFF_DEPRECATION_MESSAGE = "Replaced to explicitly indicate nullable return value; added a new non-null overload. $message_0_13_0" +internal const val DIFF_REPLACE_MESSAGE = "this.diffOrNull(expression)" internal const val DIFF_OR_NULL_IMPORT = "org.jetbrains.kotlinx.dataframe.api.diffOrNull" + +internal const val UPDATE_AS_NULLABLE_MESSAGE = "This function is useless unless in combination with `withValue(null)`, but then you can just use `with { null }`. $message_0_13_0" +internal const val UPDATE_AS_NULLABLE_REPLACE = "this as Update" + +internal const val UPDATE_WITH_VALUE = "Replaced in favor of `with { value }`. $message_0_13_0" +internal const val UPDATE_WITH_VALUE_REPLACE = "this.with { value }" + +// endregion + +// region WARNING in 0.13.0, ERROR in 0.14.0 + +private const val message_0_14_0 = "Will be removed in 0.14.0." + +// endregion diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt new file mode 100644 index 0000000000..53f76daa45 --- /dev/null +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt @@ -0,0 +1,27 @@ +package org.jetbrains.kotlinx.dataframe.api + +import io.kotest.matchers.shouldBe +import org.junit.Test + +class ConstructorsTests { + + @Test + fun `untitled column naming`() { + val builder = DynamicDataFrameBuilder() + repeat(5) { + builder.add(columnOf(1, 2, 3)) + } + builder.toDataFrame() shouldBe dataFrameOf(List(5) { columnOf(1, 2, 3) }) + } + + @Test + fun `duplicated name`() { + val builder = DynamicDataFrameBuilder() + val column by columnOf(1, 2, 3) + builder.add(column) + builder.add(column) + val df = builder.toDataFrame() + df.columnsCount() shouldBe 2 + df.columnNames() shouldBe listOf(column.name(), "${column.name()}1") + } +} diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt new file mode 100644 index 0000000000..8ac9ebc5f0 --- /dev/null +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt @@ -0,0 +1,70 @@ +package org.jetbrains.kotlinx.dataframe.api + +import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.DataColumn +import org.jetbrains.kotlinx.dataframe.nrow +import org.junit.Test + +class SortDataColumn { + + @Test + fun `value column sort with`() { + val col = DataColumn.createValueColumn("", listOf(1, 6, 8, 4, 2, 9)) + val sortedCol = col.sort() + val descSortedCol = col.sortDesc() + + col.sortWith { a, b -> a - b } shouldBe sortedCol + col.sortWith { a, b -> b - a } shouldBe descSortedCol + + col.sortWith(Int::compareTo) shouldBe sortedCol + col.sortWith(compareBy { it }) shouldBe sortedCol + } + + @Test + fun `frame column sort with`() { + val col = DataColumn.createFrameColumn( + "", + listOf( + dataFrameOf("a")(1, 2), + dataFrameOf("a")(1), + dataFrameOf("a")(1, 2, 3) + ) + ) + val sortedCol = DataColumn.createFrameColumn( + "", + listOf( + dataFrameOf("a")(1), + dataFrameOf("a")(1, 2), + dataFrameOf("a")(1, 2, 3) + ) + ) + + col.sortWith { df1, df2 -> df1.nrow - df2.nrow } shouldBe sortedCol + col.sortWith(compareBy { it.nrow }) shouldBe sortedCol + } + + @Test + fun `column group sort with`() { + val a by column() + val b by column() + + val col = DataColumn.createColumnGroup( + "", + dataFrameOf( + columnOf(1, 3, 2) named a, + columnOf("hello", "world", "!") named b, + ), + ) + + val sortedCol = DataColumn.createColumnGroup( + "", + dataFrameOf( + columnOf(1, 2, 3) named a, + columnOf("hello", "!", "world") named b, + ), + ) + + col.sortWith { df1, df2 -> df1[a] - df2[a] } shouldBe sortedCol + col.sortWith(compareBy { it[a] }) shouldBe sortedCol + } +} diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt index b9da8c9402..c3d34c30ed 100644 --- a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt @@ -52,6 +52,11 @@ object PluginCallbackProxy : PluginCallback { val names = mutableMapOf>() val expressionsByStatement = mutableMapOf>() + private var manualOutput: DataFrameHtmlData? = null + fun overrideHtmlOutput(manualOutput: DataFrameHtmlData) { + this.manualOutput = manualOutput + } + data class Expression( val source: String, val containingClassFqName: String?, @@ -61,69 +66,76 @@ object PluginCallbackProxy : PluginCallback { fun start() { expressionsByStatement.clear() + manualOutput = null } fun save() { // ensure stable table ids across test invocation sessionId = 0 tableInSessionId = 0 - var output = DataFrameHtmlData.tableDefinitions() + DataFrameHtmlData( - // copy writerside stlyles - style = """ - body { - font-family: "JetBrains Mono",SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace; - } - - :root { - color: #19191C; - background-color: #fff; - } - - :root[theme="dark"] { - background-color: #19191C; - color: #FFFFFFCC - } - - details details { - margin-left: 20px; - } - - summary { - padding: 6px; - } - """.trimIndent() - ) - - // make copy to avoid concurrent modification exception - val statements = expressionsByStatement.toMap() - when (statements.size) { - 0 -> error("function doesn't have any dataframe expression") - 1 -> { - output += statementOutput(statements.values.single()) - } - else -> { - statements.forEach { (index, expressions) -> - var details: DataFrameHtmlData = statementOutput(expressions) + var output: DataFrameHtmlData + val manualOutput = this.manualOutput + if (manualOutput == null) { + output = DataFrameHtmlData.tableDefinitions() + DataFrameHtmlData( + // copy writerside stlyles + style = """ + body { + font-family: "JetBrains Mono",SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + details details { + margin-left: 20px; + } + + summary { + padding: 6px; + } + """.trimIndent() + ) - details = details.copy( - body = - """ -
- ${expressions.joinToString(".") { it.source } - .also { - if (it.length > 95) TODO("expression is too long ${it.length}. better to split sample in multiple snippets") - } - .escapeHtmlForIFrame()} - ${details.body} -
-
- """.trimIndent() - ) + // make copy to avoid concurrent modification exception + val statements = expressionsByStatement.toMap() + when (statements.size) { + 0 -> error("function doesn't have any dataframe expression") + 1 -> { + output += statementOutput(statements.values.single()) + } + else -> { + statements.forEach { (index, expressions) -> + var details: DataFrameHtmlData = statementOutput(expressions) - output += details + details = details.copy( + body = + """ +
+ ${expressions.joinToString(".") { it.source } + .also { + if (it.length > 95) TODO("expression is too long ${it.length}. better to split sample in multiple snippets") + } + .escapeHtmlForIFrame()} + ${details.body} +
+
+ """.trimIndent() + ) + output += details + } } } + } else { + output = manualOutput } + val input = expressionsByStatement.values.first().first() val name = "${input.containingClassFqName}.${input.containingFunName}" val destination = File("build/dataframes").also { @@ -143,50 +155,67 @@ object PluginCallbackProxy : PluginCallback { ) } + private fun List.joinToSource(): String = + joinToString(".") { it.source } + private fun statementOutput( expressions: List, ): DataFrameHtmlData { var data = DataFrameHtmlData() - if (expressions.size < 2) error("Sample without output or input (i.e. function returns some value)") - for ((i, expression) in expressions.withIndex()) { - when (i) { - 0 -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + val allow = setOf( + "toDataFrame", "peek(dataFrameOf(col), dataFrameOf(col))" + ) + if (expressions.isEmpty()) { + error("No dataframe expressions in sample") + } + if (expressions.size == 1) { + if (allow.any { expressions[0].source.contains(it) }) { + val expression = expressions[0] + data += convertToHTML(expression.df) + } else { + error("${expressions.joinToSource()} Sample without output or input (i.e. function returns some value)") + } + } else { + for ((i, expression) in expressions.withIndex()) { + when (i) { + 0 -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Input ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description - } + """.trimIndent() + ) + data += description + } - expressions.lastIndex -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + expressions.lastIndex -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Output ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description - } + """.trimIndent() + ) + data += description + } - else -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + else -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Step $i: ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description + """.trimIndent() + ) + data += description + } } } } diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt index 13a0943580..8584a85608 100644 --- a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt @@ -1,7 +1,9 @@ package org.jetbrains.kotlinx.dataframe.samples.api import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.api.DynamicDataFrameBuilder import org.jetbrains.kotlinx.dataframe.api.Infer import org.jetbrains.kotlinx.dataframe.api.ValueProperty import org.jetbrains.kotlinx.dataframe.api.add @@ -431,4 +433,21 @@ class Create : TestBase() { df["scores"].kind shouldBe ColumnKind.Frame df["summary"]["min score"].values() shouldBe listOf(3, 5) } + + @Test + @TransformDataFrameExpressions + fun duplicatedColumns() { + // SampleStart + fun peek(vararg dataframes: AnyFrame): AnyFrame { + val builder = DynamicDataFrameBuilder() + for (df in dataframes) { + df.columns().firstOrNull()?.let { builder.add(it) } + } + return builder.toDataFrame() + } + + val col by columnOf(1, 2, 3) + peek(dataFrameOf(col), dataFrameOf(col)) + // SampleEnd + } } diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt new file mode 100644 index 0000000000..b9636e97b0 --- /dev/null +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt @@ -0,0 +1,660 @@ +package org.jetbrains.kotlinx.dataframe.samples.api + +import io.kotest.matchers.shouldBe +import kotlinx.datetime.LocalDate +import kotlinx.datetime.toJavaLocalDate +import org.jetbrains.kotlinx.dataframe.AnyFrame +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.FormattingDSL +import org.jetbrains.kotlinx.dataframe.api.Infer +import org.jetbrains.kotlinx.dataframe.api.JoinedDataRow +import org.jetbrains.kotlinx.dataframe.api.RGBColor +import org.jetbrains.kotlinx.dataframe.api.and +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.colsOf +import org.jetbrains.kotlinx.dataframe.api.column +import org.jetbrains.kotlinx.dataframe.api.convert +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf +import org.jetbrains.kotlinx.dataframe.api.excludeJoinWith +import org.jetbrains.kotlinx.dataframe.api.filterJoinWith +import org.jetbrains.kotlinx.dataframe.api.fullJoinWith +import org.jetbrains.kotlinx.dataframe.api.getValue +import org.jetbrains.kotlinx.dataframe.api.innerJoin +import org.jetbrains.kotlinx.dataframe.api.innerJoinWith +import org.jetbrains.kotlinx.dataframe.api.joinWith +import org.jetbrains.kotlinx.dataframe.api.leftJoin +import org.jetbrains.kotlinx.dataframe.api.leftJoinWith +import org.jetbrains.kotlinx.dataframe.api.rightJoin +import org.jetbrains.kotlinx.dataframe.api.rightJoinWith +import org.jetbrains.kotlinx.dataframe.api.with +import org.jetbrains.kotlinx.dataframe.explainer.PluginCallbackProxy +import org.jetbrains.kotlinx.dataframe.explainer.TransformDataFrameExpressions +import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData +import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration +import org.jetbrains.kotlinx.dataframe.io.renderValueForHtml +import org.jetbrains.kotlinx.dataframe.io.toHTML +import org.jetbrains.kotlinx.dataframe.jupyter.ChainedCellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.RenderedContent +import org.junit.Test +import java.time.format.DateTimeFormatter + +class JoinWith : TestBase() { + + @DataSchema + interface Campaigns { + val name: String + val startDate: LocalDate + val endDate: LocalDate + } + + @DataSchema + interface Visits { + val date: LocalDate + val userId: Int + } + + private val campaigns = dataFrameOf("name", "startDate", "endDate")( + "Winter Sale", LocalDate(2023, 1, 1), LocalDate(2023, 1, 31), + "Spring Sale", LocalDate(2023, 4, 1), LocalDate(2023, 4, 30), + "Summer Sale", LocalDate(2023, 7, 1), LocalDate(2023, 7, 31), + "Autumn Sale", LocalDate(2023, 10, 1), LocalDate(2023, 10, 31), + ).cast() + + private val visits = dataFrameOf("date", "usedId")( + LocalDate(2023, 1, 10), 1, + LocalDate(2023, 1, 20), 2, + LocalDate(2023, 4, 15), 1, + LocalDate(2023, 5, 1), 3, + LocalDate(2023, 7, 10), 2, + ).cast() + + class ColoredValue(val value: T, val backgroundColor: RGBColor, val textColor: RGBColor) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ColoredValue<*> + + return value == other.value + } + + override fun hashCode(): Int { + return value?.hashCode() ?: 0 + } + } + + private val renderer = object : ChainedCellRenderer(DefaultCellRenderer) { + override fun maybeContent(value: Any?, configuration: DisplayConfiguration): RenderedContent? { + return if (value is ColoredValue<*>) { + if (value.value is LocalDate) { + RenderedContent.text(DateTimeFormatter.ofPattern("dd MMMM yyyy").format(value.value.toJavaLocalDate())) + } else { + renderValueForHtml(value.value, configuration.cellContentLimit, configuration.decimalFormat) + } + } else { + null + } + } + + override fun maybeTooltip(value: Any?, configuration: DisplayConfiguration): String? { + return null + } + } + + private fun AnyFrame.uwrapColoredValues(): AnyFrame { + return convert { colsOf?>().rec() }.with(Infer.Type) { it?.value } + } + + private fun T.colored(background: RGBColor, text: RGBColor) = ColoredValue(this, background, text) + private fun T.winter(background: RGBColor = RGBColor(179, 205, 224), text: RGBColor = RGBColor(0, 0, 51)) = ColoredValue(this, background, text) + private fun T.spring(background: RGBColor = RGBColor(204, 235, 197), text: RGBColor = RGBColor(0, 51, 0)) = ColoredValue(this, background, text) + private fun T.summer(background: RGBColor = RGBColor(176, 224, 230), text: RGBColor = RGBColor(25, 25, 112)) = ColoredValue(this, background, text) + private fun T.autumn(background: RGBColor = RGBColor(221, 160, 221), text: RGBColor = RGBColor(85, 26, 139)) = ColoredValue(this, background, text) + + private val coloredCampaigns = dataFrameOf("name", "startDate", "endDate")( + "Winter Sale".winter(), LocalDate(2023, 1, 1).winter(), LocalDate(2023, 1, 31).winter(), + "Spring Sale".spring(), LocalDate(2023, 4, 1).spring(), LocalDate(2023, 4, 30).spring(), + "Summer Sale".summer(), LocalDate(2023, 7, 1).summer(), LocalDate(2023, 7, 31).summer(), + "Autumn Sale".autumn(), LocalDate(2023, 10, 1).autumn(), LocalDate(2023, 10, 31).autumn(), + ) + + private val coloredVisits = dataFrameOf("date", "usedId")( + LocalDate(2023, 1, 10).winter(), 1.winter(), + LocalDate(2023, 1, 20).winter(), 2.winter(), + LocalDate(2023, 4, 15).spring(), 1.spring(), + LocalDate(2023, 5, 1).colored(FormattingDSL.white, FormattingDSL.black), 3.colored(FormattingDSL.white, FormattingDSL.black), + LocalDate(2023, 7, 10).summer(), 2.summer(), + ) + + private fun AnyFrame.toColoredHTML() = toHTML( + getFooter = { null }, + cellRenderer = renderer, + configuration = DisplayConfiguration.DEFAULT.copy( + cellFormatter = { row, col -> + val value = row[col] + if (value is ColoredValue<*>) { + background(value.backgroundColor) and textColor(value.textColor) + } else { + background(white) + } + } + ) + ) + + private val joinExpression: JoinedDataRow.(it: JoinedDataRow) -> Boolean = { + right[{ "date">() }].value in + "startDate">().value.."endDate">().value + } + + private fun DataFrameHtmlData.wrap(title: String): DataFrameHtmlData { + return copy( + body = """ +
+ $title + $body +
+ """.trimIndent() + ) + } + + private fun DataFrameHtmlData.wrap(): DataFrameHtmlData { + return copy( + body = """ +
+ $body +
+ """.trimIndent() + ) + } + + private fun snippetOutput(coloredResult: DataFrame, result: DataFrame) { + coloredCampaigns.uwrapColoredValues() shouldBe campaigns + coloredVisits.uwrapColoredValues() shouldBe visits + coloredResult.uwrapColoredValues() shouldBe result + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus(coloredCampaigns.toColoredHTML().wrap("campaigns")) + .plus(coloredVisits.toColoredHTML().wrap("visits")) + .plus(coloredResult.toColoredHTML().wrap("result")) + .plus( + DataFrameHtmlData( + style = """ + body { + display: flex; + align-items: flex-start; + overflow-x: auto; + font-family: "JetBrains Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 14px; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + .table-container { + margin-right: 20px; + } + + .table-container:not(:last-child) { + margin-right: 20px; + } + + td { + white-space: nowrap; + } + """.trimIndent() + ) + ) + ) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.innerJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.filterJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.leftJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.rightJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.fullJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.excludeJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_strings() { + val result = + // SampleStart + campaigns.innerJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_strings() { + val result = + // SampleStart + campaigns.filterJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_strings() { + val result = + // SampleStart + campaigns.leftJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_strings() { + val result = + // SampleStart + campaigns.rightJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_strings() { + val result = + // SampleStart + campaigns.fullJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_strings() { + val result = + // SampleStart + campaigns.excludeJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_properties() { + val result = + // SampleStart + campaigns.innerJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_properties() { + val result = + // SampleStart + campaigns.filterJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_properties() { + val result = + // SampleStart + campaigns.leftJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_properties() { + val result = + // SampleStart + campaigns.rightJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_properties() { + val result = + // SampleStart + campaigns.fullJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_properties() { + val result = + // SampleStart + campaigns.excludeJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun crossProduct() { + val result = + // SampleStart + campaigns.joinWith(visits) { true } + // SampleEnd + val coloredResult = coloredCampaigns.joinWith(coloredVisits) { true } + snippetOutput(coloredResult, result) + } + + val df1 = dataFrameOf("index", "age", "name")( + 1.spring(), 15.spring(), "BOB".spring(), + 2.summer(), 19.summer(), "ALICE".summer(), + 3.autumn(), 20.autumn(), "CHARLIE".autumn() + ) + + val df2 = dataFrameOf("index", "age", "name")( + 1.spring(), 15.spring(), "Bob".spring(), + 2.summer(), 19.summer(), "Alice".summer(), + 4.winter(), 21.winter(), "John".winter() + ) + + @TransformDataFrameExpressions + @Test + fun compareInnerColumns() { + // SampleStart + df1.innerJoin(df2, "index", "age") + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus(df1.innerJoin(df2, "index", "age").toColoredHTML().wrap("result")) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareInnerValues() { + // SampleStart + df1.innerJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.innerJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareLeft() { + // SampleStart + df1.leftJoin(df2, "index", "age") + df1.leftJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.leftJoin(df2, "index", "age").toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(DataFrameHtmlData(body = "

")) + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap()) + .plus(df2.toColoredHTML().wrap()) + .plus( + df1.leftJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap() + ) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareRight() { + // SampleStart + df1.rightJoin(df2, "index", "age") + df1.rightJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.rightJoin(df2, "index", "age").toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(DataFrameHtmlData(body = "

")) + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap()) + .plus(df2.toColoredHTML().wrap()) + .plus( + df1.rightJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap() + ) + .wrapRow() + ) + .plus(other) + ) + } + + private fun DataFrameHtmlData.wrapRow(): DataFrameHtmlData { + return copy( + body = """ +
+ $body +
+ """.trimIndent() + ) + } + + private val other = DataFrameHtmlData( + style = """ + body { + font-family: "JetBrains Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 14px; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + .table-row { + display: flex; + align-items: flex-start; + overflow-x: auto; + } + + .table-container:not(:last-child) { + margin-right: 20px; + } + + td { + white-space: nowrap; + } + """.trimIndent() + ) +} diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt index 1a8bf5c505..82d0a081b6 100644 --- a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt @@ -469,7 +469,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitInplace_properties() { // SampleStart - df.split { name.firstName }.by { it.chars().toList() }.inplace() + df.split { name.firstName }.by { it.asIterable() }.inplace() // SampleEnd } @@ -480,7 +480,7 @@ class Modify : TestBase() { val name by columnGroup() val firstName by name.column() - df.split { firstName }.by { it.chars().toList() }.inplace() + df.split { firstName }.by { it.asIterable() }.inplace() // SampleEnd } @@ -488,7 +488,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitInplace_strings() { // SampleStart - df.split { "name"["firstName"]() }.by { it.chars().toList() }.inplace() + df.split { "name"["firstName"]() }.by { it.asIterable() }.inplace() // SampleEnd } @@ -496,9 +496,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun split_properties() { // SampleStart - df.split { name }.by { it.values() }.into("nameParts") - - df.split { name.lastName }.by(" ").default("").inward { "word$it" } + df.split { name.lastName }.by { it.asIterable() }.into("char1", "char2") // SampleEnd } @@ -509,9 +507,7 @@ class Modify : TestBase() { val name by columnGroup() val lastName by name.column() - df.split { name }.by { it.values() }.into("nameParts") - - df.split { lastName }.by(" ").default("").inward { "word$it" } + df.split { lastName }.by { it.asIterable() }.into("char1", "char2") // SampleEnd } @@ -519,18 +515,61 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun split_strings() { // SampleStart - df.split { name }.by { it.values() }.into("nameParts") + df.split { "name"["lastName"]() }.by { it.asIterable() }.into("char1", "char2") + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_properties() { + // SampleStart + df.split { name.lastName } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_accessors() { + // SampleStart + val name by columnGroup() + val lastName by name.column() - df.split { "name"["lastName"] }.by(" ").default("").inward { "word$it" } + df.split { lastName } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_strings() { + // SampleStart + df.split { "name"["lastName"]() } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } // SampleEnd } @Test @TransformDataFrameExpressions fun splitRegex() { - val merged = df.merge { name.lastName and name.firstName }.by { it[0] + " (" + it[1] + ")" }.into("name") - val name by column() // SampleStart + val merged = df.merge { name.lastName and name.firstName } + .by { it[0] + " (" + it[1] + ")" } + .into("name") + // SampleEnd + } + + private val merged = df.merge { name.lastName and name.firstName }.by { it[0] + " (" + it[1] + ")" }.into("name") + + @Test + @TransformDataFrameExpressions + fun splitRegex1() { + // SampleStart + val name by column() + merged.split { name } .match("""(.*) \((.*)\)""") .inward("firstName", "lastName") @@ -562,7 +601,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitIntoRows_properties() { // SampleStart - df.split { name.firstName }.by { it.chars().toList() }.intoRows() + df.split { name.firstName }.by { it.asIterable() }.intoRows() df.split { name }.by { it.values() }.intoRows() // SampleEnd @@ -575,7 +614,7 @@ class Modify : TestBase() { val name by columnGroup() val firstName by name.column() - df.split { firstName }.by { it.chars().toList() }.intoRows() + df.split { firstName }.by { it.asIterable() }.intoRows() df.split { name }.by { it.values() }.intoRows() // SampleEnd @@ -585,7 +624,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitIntoRows_strings() { // SampleStart - df.split { "name"["firstName"]() }.by { it.chars().toList() }.intoRows() + df.split { "name"["firstName"]() }.by { it.asIterable() }.intoRows() df.split { colGroup("name") }.by { it.values() }.intoRows() // SampleEnd diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt new file mode 100644 index 0000000000..8e0f2e790c --- /dev/null +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt @@ -0,0 +1,26 @@ +package org.jetbrains.kotlinx.dataframe.testSets.person + +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf + +open class BaseJoinTest : BaseTest() { + val df2 = dataFrameOf("name", "origin", "grade", "age")( + "Alice", "London", 3, "young", + "Alice", "London", 5, "old", + "Bob", "Tokyo", 4, "young", + "Bob", "Paris", 5, "old", + "Charlie", "Moscow", 1, "young", + "Charlie", "Moscow", 2, "old", + "Bob", "Paris", 4, null + ) + val typed2: DataFrame = df2.cast() + + @DataSchema + interface Person2 { + val name: String + val origin: String? + val grade: Int + } +} diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt index 47fa300d4c..68962157d8 100644 --- a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt @@ -1,49 +1,26 @@ package org.jetbrains.kotlinx.dataframe.testSets.person import io.kotest.matchers.shouldBe -import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.JoinType import org.jetbrains.kotlinx.dataframe.api.addId import org.jetbrains.kotlinx.dataframe.api.all import org.jetbrains.kotlinx.dataframe.api.append -import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.column import org.jetbrains.kotlinx.dataframe.api.count -import org.jetbrains.kotlinx.dataframe.api.dataFrameOf import org.jetbrains.kotlinx.dataframe.api.distinct import org.jetbrains.kotlinx.dataframe.api.excludeJoin import org.jetbrains.kotlinx.dataframe.api.filter import org.jetbrains.kotlinx.dataframe.api.filterJoin import org.jetbrains.kotlinx.dataframe.api.fullJoin import org.jetbrains.kotlinx.dataframe.api.innerJoin +import org.jetbrains.kotlinx.dataframe.api.join import org.jetbrains.kotlinx.dataframe.api.leftJoin import org.jetbrains.kotlinx.dataframe.api.remove import org.jetbrains.kotlinx.dataframe.api.rightJoin import org.jetbrains.kotlinx.dataframe.api.select import org.junit.Test -class JoinTests : BaseTest() { - - val df2 = dataFrameOf("name", "origin", "grade", "age")( - "Alice", "London", 3, "young", - "Alice", "London", 5, "old", - "Bob", "Tokyo", 4, "young", - "Bob", "Paris", 5, "old", - "Charlie", "Moscow", 1, "young", - "Charlie", "Moscow", 2, "old", - "Bob", "Paris", 4, null - ) - -// Generated Code - - @DataSchema - interface Person2 { - val name: String - val origin: String? - val grade: Int - } - - val typed2: DataFrame = df2.cast() +class JoinTests : BaseJoinTest() { @Test fun `inner join`() { @@ -118,4 +95,14 @@ class JoinTests : BaseTest() { res shouldBe expected } + + @Test + fun `test overloads contract`() { + typed.innerJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Inner) { name and it.city.match(right.origin) } + typed.leftJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Left) { name and it.city.match(right.origin) } + typed.rightJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Right) { name and it.city.match(right.origin) } + typed.fullJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Full) { name and it.city.match(right.origin) } + typed.excludeJoin(typed2) { city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Exclude) { city.match(right.origin) } + typed.filterJoin(typed2) { city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Filter) { city.match(right.origin) } + } } diff --git a/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt new file mode 100644 index 0000000000..39a5e9886c --- /dev/null +++ b/core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt @@ -0,0 +1,130 @@ +package org.jetbrains.kotlinx.dataframe.testSets.person + +import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.api.JoinType +import org.jetbrains.kotlinx.dataframe.api.addId +import org.jetbrains.kotlinx.dataframe.api.all +import org.jetbrains.kotlinx.dataframe.api.append +import org.jetbrains.kotlinx.dataframe.api.column +import org.jetbrains.kotlinx.dataframe.api.count +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf +import org.jetbrains.kotlinx.dataframe.api.distinct +import org.jetbrains.kotlinx.dataframe.api.excludeJoin +import org.jetbrains.kotlinx.dataframe.api.excludeJoinWith +import org.jetbrains.kotlinx.dataframe.api.filter +import org.jetbrains.kotlinx.dataframe.api.filterJoinWith +import org.jetbrains.kotlinx.dataframe.api.fullJoinWith +import org.jetbrains.kotlinx.dataframe.api.innerJoinWith +import org.jetbrains.kotlinx.dataframe.api.joinWith +import org.jetbrains.kotlinx.dataframe.api.leftJoinWith +import org.jetbrains.kotlinx.dataframe.api.print +import org.jetbrains.kotlinx.dataframe.api.remove +import org.jetbrains.kotlinx.dataframe.api.rightJoinWith +import org.jetbrains.kotlinx.dataframe.api.select +import org.junit.Test + +class JoinWithTests : BaseJoinTest() { + + @Test + fun `inner join`() { + val res = typed.joinWith(typed2) { + name == right.name && city == right.origin + } + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 7 + res["age1"].hasNulls() shouldBe false + res.count { name == "Charlie" && city == "Moscow" } shouldBe 4 + res.select { city and name }.distinct().rowsCount() shouldBe 3 + res[Person2::grade].hasNulls() shouldBe false + } + + @Test + fun `left join`() { + val res = typed.leftJoinWith(typed2) { name == right.name && city == right.origin } + + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 10 + res["age1"].hasNulls() shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 6 + res.count { it["grade"] == null } shouldBe 3 + res.age.hasNulls() shouldBe false + } + + @Test + fun `right join`() { + val res = typed.rightJoinWith(typed2) { + name == right.name && city == right.origin + } + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 9 + res["age1"].hasNulls() shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 4 + res[Person2::grade].hasNulls() shouldBe false + res.age.hasNulls() shouldBe true + val newEntries = res.filter { it["age"] == null } + newEntries.rowsCount() shouldBe 2 + newEntries.all { it["name1"] == "Bob" && it["origin"] == "Paris" && weight == null } shouldBe true + } + + @Test + fun `outer join`() { + val res = typed.fullJoinWith(typed2) { name == right.name && city == right.origin } + println(res) + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 12 + res.columns().all { it.hasNulls() } shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 7 + val distinct = res.select { name and age and city and weight }.distinct() + val expected = typed.append(null, null, null, null) + distinct shouldBe expected + } + + @Test + fun `filter join`() { + val res = typed.filterJoinWith(typed2) { city == right.origin } + val expected = typed.innerJoinWith(typed2.select { origin }) { city == right.origin }.remove("origin") + res shouldBe expected + } + + @Test + fun `filter not join`() { + val res = typed.excludeJoinWith(typed2) { city == right.origin } + res.rowsCount() shouldBe 3 + res.city.toSet() shouldBe typed.city.toSet() - typed2.origin.toSet() + + val indexColumn = column("__index__") + val withIndex = typed.addId(indexColumn) + val joined = withIndex.filterJoinWith(typed2) { city == right.origin } + val joinedIndices = joined[indexColumn].toSet() + val expected = withIndex.filter { !joinedIndices.contains(it[indexColumn]) }.remove(indexColumn) + + res shouldBe expected + } + + @Test + fun rightJoin() { + val df = dataFrameOf("a", "b")( + 1, "a", + 2, "b", + 3, "c", + ) + + val df1 = dataFrameOf("a", "c")( + 5, "V", + 1, "I", + 2, "II", + 3, "III", + ) + df.append(4, "e").excludeJoin(df1).print() + } + + @Test + fun `test overloads contract`() { + typed.innerJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Inner) { name == right.name && city == right.origin } + typed.leftJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Left) { name == right.name && city == right.origin } + typed.rightJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Right) { name == right.name && city == right.origin } + typed.fullJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Full) { name == right.name && city == right.origin } + typed.excludeJoinWith(typed2) { city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Exclude) { city == right.origin } + typed.filterJoinWith(typed2) { city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Filter) { city == right.origin } + } +} diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt index 221d249c63..f40357ebc5 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/ColumnsSelectionDsl.kt @@ -179,7 +179,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the first column that adheres to the given [condition\]. @@ -194,7 +194,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonFirstDocs] - * @arg [CommonFirstDocs.Examples] + * @setArg [CommonFirstDocs.Examples] * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[first][first]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { `[colsOf][colsOf]`<`[Int][Int]`>().`[first][first]`() }` @@ -204,7 +204,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonFirstDocs] - * @arg [CommonFirstDocs.Examples] + * @setArg [CommonFirstDocs.Examples] * `df.`[select][select]` { `[first][first]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { myColumnGroup.`[first][first]`() }` @@ -216,7 +216,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonFirstDocs] - * @arg [CommonFirstDocs.Examples] + * @setArg [CommonFirstDocs.Examples] * `df.`[select][select]` { "myColumnGroup".`[first][first]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun String.first(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -224,7 +224,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonFirstDocs] - * @arg [CommonFirstDocs.Examples] + * @setArg [CommonFirstDocs.Examples] * `df.`[select][select]` { Type::myColumnGroup.`[first][first]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun KProperty<*>.first(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -236,7 +236,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the last column that adheres to the given [condition\]. @@ -251,7 +251,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonLastDocs] - * @arg [CommonLastDocs.Examples] + * @setArg [CommonLastDocs.Examples] * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[last][last]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { `[colsOf][colsOf]`<`[Int][Int]`>().`[first][last]`() }` @@ -261,7 +261,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonLastDocs] - * @arg [CommonLastDocs.Examples] + * @setArg [CommonLastDocs.Examples] * `df.`[select][select]` { `[last][last]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { myColumnGroup.`[last][last]`() }` @@ -273,7 +273,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonLastDocs] - * @arg [CommonLastDocs.Examples] + * @setArg [CommonLastDocs.Examples] * `df.`[select][select]` { "myColumnGroup".`[last][last]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun String.last(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -281,7 +281,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonLastDocs] - * @arg [CommonLastDocs.Examples] + * @setArg [CommonLastDocs.Examples] * `df.`[select][select]` { Type::myColumnGroup.`[last][last]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun KProperty<*>.last(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -293,7 +293,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * {@includeArg [Examples]} + * {@getArg [Examples]} * * @param [condition\] The optional [ColumnFilter] condition that the column must adhere to. * @return A [SingleColumn] containing the single column that adheres to the given [condition\]. @@ -308,7 +308,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonSingleDocs] - * @arg [CommonSingleDocs.Examples] + * @setArg [CommonSingleDocs.Examples] * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[single][single]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { `[colsOf][colsOf]`<`[Int][Int]`>().`[single][single]`() }` @@ -318,7 +318,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonSingleDocs] - * @arg [CommonSingleDocs.Examples] + * @setArg [CommonSingleDocs.Examples] * `df.`[select][select]` { `[single][single]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` * * `df.`[select][select]` { myColumnGroup.`[single][single]`() }` @@ -330,7 +330,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonSingleDocs] - * @arg [CommonSingleDocs.Examples] + * @setArg [CommonSingleDocs.Examples] * `df.`[select][select]` { "myColumnGroup".`[single][single]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun String.single(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -338,7 +338,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonSingleDocs] - * @arg [CommonSingleDocs.Examples] + * @setArg [CommonSingleDocs.Examples] * `df.`[select][select]` { Type::myColumnGroup.`[single][single]` { it.`[name][ColumnReference.name]`().`[startsWith][String.startsWith]`("year") } }` */ public fun KProperty<*>.single(condition: ColumnFilter<*> = { true }): TransformableSingleColumn<*> = @@ -350,7 +350,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * `df.`[select][DataFrame.select]` { `{@includeArg [CommonSubsetOfColumnsDocs.Example]}` }` + * `df.`[select][DataFrame.select]` { `{@getArg [CommonSubsetOfColumnsDocs.Example]}` }` * * @param [endInclusive\] The last column in the subset. * @receiver The first column in the subset. @@ -365,63 +365,63 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] "fromColumn".."toColumn"} + * {@setArg [CommonSubsetOfColumnsDocs.Example] "fromColumn".."toColumn"} */ public operator fun String.rangeTo(endInclusive: String): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] "fromColumn"..Type::toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] "fromColumn"..Type::toColumn} */ public operator fun String.rangeTo(endInclusive: KProperty<*>): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] "fromColumn"..toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] "fromColumn"..toColumn} */ public operator fun String.rangeTo(endInclusive: AnyColumnReference): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn.."toColumn"} + * {@setArg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn.."toColumn"} */ public operator fun KProperty<*>.rangeTo(endInclusive: String): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn..Type::toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn..Type::toColumn} */ public operator fun KProperty<*>.rangeTo(endInclusive: KProperty<*>): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn..toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] Type::fromColumn..toColumn} */ public operator fun KProperty<*>.rangeTo(endInclusive: AnyColumnReference): ColumnSet<*> = toColumnAccessor().rangeTo(endInclusive) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] fromColumn.."toColumn"} + * {@setArg [CommonSubsetOfColumnsDocs.Example] fromColumn.."toColumn"} */ public operator fun AnyColumnReference.rangeTo(endInclusive: String): ColumnSet<*> = rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] fromColumn..Type::toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] fromColumn..Type::toColumn} */ public operator fun AnyColumnReference.rangeTo(endInclusive: KProperty<*>): ColumnSet<*> = rangeTo(endInclusive.toColumnAccessor()) /** * @include [CommonSubsetOfColumnsDocs] - * {@arg [CommonSubsetOfColumnsDocs.Example] fromColumn..toColumn} + * {@setArg [CommonSubsetOfColumnsDocs.Example] fromColumn..toColumn} */ public operator fun AnyColumnReference.rangeTo(endInclusive: AnyColumnReference): ColumnSet<*> = object : ColumnSet { @@ -468,19 +468,19 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * The function can also be called on [ColumnGroupReferences][ColumnGroupReference] to create * an accessor for a column inside a [ColumnGroup]. * - * {@includeArg [CommonColDocs.Note]} + * {@getArg [CommonColDocs.Note]} * * #### For example: * - * `df.`[select][select]` { `[col][col]`({@includeArg [CommonColDocs.Arg]}) }` + * `df.`[select][select]` { `[col][col]`({@getArg [CommonColDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[col][col]`({@includeArg [CommonColDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[col][col]`({@getArg [CommonColDocs.Arg]}) }` * * @return A [ColumnAccessor] for the column with the given argument. * @see [column\] * @see [colGroup\] * @see [frameCol\] - * {@arg [CommonColDocs.Note]} + * {@setArg [CommonColDocs.Note]} */ private interface CommonColDocs { @@ -492,7 +492,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum } /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnName"} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnName"} * @param [name] The name of the column. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -500,15 +500,15 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun col(name: String): ColumnAccessor<*> = column(name) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnName"} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnName"} * @param [name] The name of the column. * @param [C] The type of the column. */ public fun col(name: String): ColumnAccessor = column(name) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnGroup"["columnName"]} - * {@arg [CommonColDocs.Note] NOTE: For column paths, this is an identity function and can be removed.} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnGroup"["columnName"]} + * {@setArg [CommonColDocs.Note] NOTE: For column paths, this is an identity function and can be removed.} * @param [path] The [ColumnPath] pointing to the column. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -516,20 +516,20 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun col(path: ColumnPath): ColumnAccessor<*> = column(path) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the column. * @param [C] The type of the column. */ public fun col(path: ColumnPath): ColumnAccessor = column(path) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] Type::columnName} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the column. */ public fun col(property: KProperty): ColumnAccessor = column(property) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnName"} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnName"} * @param [name] The name of the column. * @receiver The [ColumnGroupReference] to get the column from. */ @@ -538,7 +538,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.col(name: String): ColumnAccessor<*> = column(name) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnName"} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnName"} * @param [name] The name of the column. * @receiver The [ColumnGroupReference] to get the column from. * @param [C] The type of the column. @@ -546,7 +546,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.col(name: String): ColumnAccessor = column(name) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the column. * @receiver The [ColumnGroupReference] to get the column from. */ @@ -555,7 +555,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.col(path: ColumnPath): ColumnAccessor<*> = column(path) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the column. * @receiver The [ColumnGroupReference] to get the column from. * @param [C] The type of the column. @@ -563,7 +563,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.col(path: ColumnPath): ColumnAccessor = column(path) /** - * @include [CommonColDocs] {@arg [CommonColDocs.Arg] Type::columnName} + * @include [CommonColDocs] {@setArg [CommonColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the column. * @receiver The [ColumnGroupReference] to get the column from. */ @@ -587,9 +587,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### For example: * - * `df.`[select][select]` { `[colGroup][colGroup]`({@includeArg [CommonColGroupDocs.Arg]}) }` + * `df.`[select][select]` { `[colGroup][colGroup]`({@getArg [CommonColGroupDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[colGroup][colGroup]`({@includeArg [CommonColGroupDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[colGroup][colGroup]`({@getArg [CommonColGroupDocs.Arg]}) }` * * @return A [ColumnAccessor] for the column group with the given argument. * @see [columnGroup\] @@ -606,7 +606,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnsContainer<*>.group(name: String): ColumnGroupReference = name.toColumnOf() /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroupName"} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroupName"} * @param [name] The name of the column group. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -614,14 +614,14 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun colGroup(name: String): ColumnAccessor> = columnGroup(name) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroupName"} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroupName"} * @param [name] The name of the column group. * @param [C] The type of the column group. */ public fun colGroup(name: String): ColumnAccessor> = columnGroup(name) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} * @param [path] The [ColumnPath] pointing to the column group. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -629,14 +629,14 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun colGroup(path: ColumnPath): ColumnAccessor> = columnGroup(path) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} * @param [path] The [ColumnPath] pointing to the column group. * @param [C] The type of the column group. */ public fun colGroup(path: ColumnPath): ColumnAccessor> = columnGroup(path) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] Type::columnGroupName} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] Type::columnGroupName} * @param [property] The [KProperty] pointing to the column group. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -644,13 +644,13 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun colGroup(property: KProperty>): ColumnAccessor> = columnGroup(property) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] Type::columnGroupName} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] Type::columnGroupName} * @param [property] The [KProperty] pointing to the column group. */ public fun colGroup(property: KProperty): ColumnAccessor> = columnGroup(property) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroupName"} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroupName"} * @param [name] The name of the column group. * @receiver The [ColumnGroupReference] to get the column group from. */ @@ -659,7 +659,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.colGroup(name: String): ColumnAccessor> = columnGroup(name) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroupName"} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroupName"} * @param [name] The name of the column group. * @receiver The [ColumnGroupReference] to get the column group from. * @param [C] The type of the column group. @@ -667,7 +667,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.colGroup(name: String): ColumnAccessor> = columnGroup(name) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} * @param [path] The [ColumnPath] pointing to the column group. * @receiver The [ColumnGroupReference] to get the column group from. */ @@ -677,7 +677,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum columnGroup(path) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] "columnGroup"["columnGroupName"]} * @param [path] The [ColumnPath] pointing to the column group. * @receiver The [ColumnGroupReference] to get the column group from. * @param [C] The type of the column group. @@ -686,7 +686,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum columnGroup(path) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] Type::columnGroupName} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] Type::columnGroupName} * @param [property] The [KProperty] pointing to the column group. * @receiver The [ColumnGroupReference] to get the column group from. */ @@ -696,7 +696,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum columnGroup(property) /** - * @include [CommonColGroupDocs] {@arg [CommonColGroupDocs.Arg] Type::columnGroupName} + * @include [CommonColGroupDocs] {@setArg [CommonColGroupDocs.Arg] Type::columnGroupName} * @param [property] The [KProperty] pointing to the column group. * @receiver The [ColumnGroupReference] to get the column group from. */ @@ -714,9 +714,9 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * an accessor for a frame column inside a [ColumnGroup]. * * #### For example: - * `df.`[select][select]` { `[frameCol][frameCol]`({@includeArg [CommonFrameColDocs.Arg]}) }` + * `df.`[select][select]` { `[frameCol][frameCol]`({@getArg [CommonFrameColDocs.Arg]}) }` * - * `df.`[select][select]` { myColGroup.`[frameCol][frameCol]`({@includeArg [CommonFrameColDocs.Arg]}) }` + * `df.`[select][select]` { myColGroup.`[frameCol][frameCol]`({@getArg [CommonFrameColDocs.Arg]}) }` * * @return A [ColumnAccessor] for the frame column with the given argument. * @see [frameColumn\] @@ -730,7 +730,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum } /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnName"} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnName"} * @param [name] The name of the frame column. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -738,14 +738,14 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun frameCol(name: String): ColumnAccessor> = frameColumn(name) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnName"} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnName"} * @param [name] The name of the frame column. * @param [C] The type of the frame column. */ public fun frameCol(name: String): ColumnAccessor> = frameColumn(name) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the frame column. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -753,14 +753,14 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun frameCol(path: ColumnPath): ColumnAccessor> = frameColumn(path) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the frame column. * @param [C] The type of the frame column. */ public fun frameCol(path: ColumnPath): ColumnAccessor> = frameColumn(path) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] Type::columnName} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the frame column. */ @Suppress("INAPPLICABLE_JVM_NAME") @@ -768,13 +768,13 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun frameCol(property: KProperty>): ColumnAccessor> = frameColumn(property) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] Type::columnName} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the frame column. */ public fun frameCol(property: KProperty>): ColumnAccessor> = frameColumn(property) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnName"} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnName"} * @param [name] The name of the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. */ @@ -783,7 +783,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.frameCol(name: String): ColumnAccessor> = frameColumn(name) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnName"} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnName"} * @param [name] The name of the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. * @param [C] The type of the frame column. @@ -791,7 +791,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum public fun ColumnGroupReference.frameCol(name: String): ColumnAccessor> = frameColumn(name) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. */ @@ -801,7 +801,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum frameColumn(path) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] "columnGroup"["columnName"]} * @param [path] The [ColumnPath] pointing to the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. * @param [C] The type of the frame column. @@ -810,7 +810,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum frameColumn(path) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] Type::columnName} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. */ @@ -820,7 +820,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum frameColumn(property) /** - * @include [CommonFrameColDocs] {@arg [CommonFrameColDocs.Arg] Type::columnName} + * @include [CommonFrameColDocs] {@setArg [CommonFrameColDocs.Arg] Type::columnName} * @param [property] The [KProperty] pointing to the frame column. * @receiver The [ColumnGroupReference] to get the frame column from. */ @@ -855,7 +855,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonColsDocs.Examples]} + * {@getArg [CommonColsDocs.Examples]} * */ private interface CommonColsDocs { @@ -871,8 +871,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonColsDocs] * - * @param [firstCol\] A {@includeArg [AccessorType]} that points to a column. - * @param [otherCols\] Optional additional {@includeArg [AccessorType]}s that point to columns. + * @param [firstCol\] A {@getArg [AccessorType]} that points to a column. + * @param [otherCols\] Optional additional {@getArg [AccessorType]}s that point to columns. * @return A [ColumnSet] containing the columns that [firstCol\] and [otherCols\] point to. */ interface Vararg { @@ -888,7 +888,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonColsDocs.Predicate] - * @arg [CommonColsDocs.Examples] + * @setArg [CommonColsDocs.Examples] * * `// although these can be shortened to just the `[colsOf][colsOf]` call` * @@ -917,7 +917,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonColsDocs.Predicate] - * @arg [CommonColsDocs.Examples] + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[cols][cols]` { "e" `[in\][String.contains\]` it.`[name][ColumnPath.name]`() } }` * @@ -956,7 +956,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonColsDocs.Predicate] - * @arg [CommonColsDocs.Examples] + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { "myGroupCol".`[cols][cols]` { "e" `[in\][String.contains\]` it.`[name][ColumnPath.name]`() } }` * @@ -980,7 +980,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonColsDocs.Predicate] - * @arg [CommonColsDocs.Examples] + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { Type::columnGroup.`[cols][cols]` { "e" `[in\][String.contains\]` it.`[name][ColumnPath.name]`() } }` * @@ -1009,8 +1009,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum // region references /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[cols][cols]`(columnA, columnB) }` * @@ -1038,8 +1038,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[cols][cols]`(columnA, columnB) }` * @@ -1081,8 +1081,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { "myColumnGroup".`[cols][cols]`(columnA, columnB) }` * @@ -1105,8 +1105,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [ColumnReference]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { Type::myColumnGroup.`[cols][cols]`(columnA, columnB) }` * @@ -1133,8 +1133,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum // region names /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [String]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [String]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[cols][cols]`("columnA", "columnB") }` * @@ -1159,8 +1159,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [String]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [String]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[cols][cols]`("columnA", "columnB") }` * @@ -1196,8 +1196,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet<*> = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [String]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [String]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { "columnGroup".`[cols][cols]`("columnA", "columnB") }` * @@ -1218,8 +1218,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet<*> = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [String]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [String]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { Type::myColumnGroup.`[cols][cols]`("columnA", "columnB") }` * @@ -1244,8 +1244,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum // region properties /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [KProperty]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [KProperty]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[colsOf][colsOf]`<`[String][String]`>().`[cols][cols]`(Type::colA, Type::colB) }` * @@ -1268,8 +1268,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [KProperty]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [KProperty]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { `[cols][cols]`(Type::colA, Type::colB) }` * @@ -1300,8 +1300,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [KProperty]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [KProperty]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { "myColumnGroup".`[cols][cols]`(Type::colA, Type::colB) }` * @@ -1322,8 +1322,8 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum ): ColumnSet = cols(firstCol, *otherCols) /** - * @include [CommonColsDocs.Vararg] {@arg [CommonColsDocs.Vararg.AccessorType] [KProperty]} - * @arg [CommonColsDocs.Examples] + * @include [CommonColsDocs.Vararg] {@setArg [CommonColsDocs.Vararg.AccessorType] [KProperty]} + * @setArg [CommonColsDocs.Examples] * * `df.`[select][select]` { Type::myColumnGroup.`[cols][cols]`(Type::colA, Type::colB) }` * @@ -1489,7 +1489,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonAllDocs.Examples]} + * {@getArg [CommonAllDocs.Examples]} * * @see [cols\] */ @@ -1501,7 +1501,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonAllDocs] - * @arg [CommonAllDocs.Examples] + * @setArg [CommonAllDocs.Examples] * * `df.`[select][select]` { `[cols][cols]` { "a" in `[name][ColumnWithPath.name]` }.`[all][all]`() }` * {@include [LineBreak]} @@ -1512,7 +1512,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonAllDocs] - * @arg [CommonAllDocs.Examples] + * @setArg [CommonAllDocs.Examples] * * `df.`[select][select]` { `[all][all]`() }` * @@ -1524,7 +1524,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonAllDocs] - * @arg [CommonAllDocs.Examples] + * @setArg [CommonAllDocs.Examples] * * `df.`[select][select]` { "myGroupCol".`[all][all]`() }` */ @@ -1532,7 +1532,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonAllDocs] - * @arg [CommonAllDocs.Examples] + * @setArg [CommonAllDocs.Examples] * * `df.`[select][select]` { Type::columnGroup.`[all][all]`() }` */ @@ -1594,7 +1594,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum * * #### Examples for this overload: * - * {@includeArg [CommonRecursivelyDocs.Examples]} + * {@getArg [CommonRecursivelyDocs.Examples]} * * @param [includeTopLevel\] Whether to include the top-level columns in the result. `true` by default. */ @@ -1606,7 +1606,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonRecursivelyDocs] - * @arg [CommonRecursivelyDocs.Examples] + * @setArg [CommonRecursivelyDocs.Examples] * * `df.`[select][DataFrame.select]` { `[colsOf][ColumnSet.colsOf]`<`[String][String]`>().`[recursively][recursively]`() }` * @@ -1622,7 +1622,7 @@ public interface ColumnsSelectionDsl : ColumnSelectionDsl, SingleColum /** * @include [CommonRecursivelyDocs] - * @arg [CommonRecursivelyDocs.Examples] + * @setArg [CommonRecursivelyDocs.Examples] * * `df.`[select][DataFrame.select]` { `[first][ColumnSet.first]` { col -> col.`[any][DataColumn.any]` { it == "Alice" } }.`[recursively][recursively]`() }` * diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt index 562ab973a4..bf02f92efc 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/Nulls.kt @@ -23,11 +23,11 @@ import kotlin.reflect.KProperty */ internal interface FillNulls { - /** @include [Update.Usage] {@arg [UpdateOperationArg] [fillNulls][fillNulls]} */ + /** @include [Update.Usage] {@setArg [UpdateOperationArg] [fillNulls][fillNulls]} */ interface Usage } -/** {@arg [SelectingColumns.OperationArg] [fillNulls][fillNulls]} */ +/** {@setArg [SelectingColumns.OperationArg] [fillNulls][fillNulls]} */ private interface SetFillNullsOperationArg /** @@ -119,11 +119,11 @@ internal inline val Float?.isNA: Boolean get() = this == null || this.isNaN() */ internal interface FillNaNs { - /** @include [Update.Usage] {@arg [Update.UpdateOperationArg] [fillNaNs][fillNaNs]} */ + /** @include [Update.Usage] {@setArg [Update.UpdateOperationArg] [fillNaNs][fillNaNs]} */ interface Usage } -/** {@arg [SelectingColumns.OperationArg] [fillNaNs][fillNaNs]} */ +/** {@setArg [SelectingColumns.OperationArg] [fillNaNs][fillNaNs]} */ internal interface SetFillNaNsOperationArg /** @@ -193,11 +193,11 @@ public fun DataFrame.fillNaNs(columns: Iterable>): */ internal interface FillNA { - /** @include [Update.Usage] {@arg [Update.UpdateOperationArg] [fillNA][fillNA]} */ + /** @include [Update.Usage] {@setArg [Update.UpdateOperationArg] [fillNA][fillNA]} */ interface Usage } -/** {@arg [SelectingColumns.OperationArg] [fillNA][fillNA]} */ +/** {@setArg [SelectingColumns.OperationArg] [fillNA][fillNA]} */ internal interface SetFillNAOperationArg /** @@ -288,7 +288,7 @@ internal interface DropNulls { interface WhereAllNullParam } -/** {@arg [SelectingColumns.OperationArg] [dropNulls][dropNulls]} */ +/** {@setArg [SelectingColumns.OperationArg] [dropNulls][dropNulls]} */ private interface SetDropNullsOperationArg /** @@ -395,7 +395,7 @@ internal interface DropNA { interface WhereAllNAParam } -/** {@arg [SelectingColumns.OperationArg] [dropNA][dropNA]} */ +/** {@setArg [SelectingColumns.OperationArg] [dropNA][dropNA]} */ private interface SetDropNAOperationArg /** @@ -502,7 +502,7 @@ internal interface DropNaNs { interface WhereAllNaNParam } -/** {@arg [SelectingColumns.OperationArg] [dropNaNs][dropNaNs]} */ +/** {@setArg [SelectingColumns.OperationArg] [dropNaNs][dropNaNs]} */ private interface SetDropNaNsOperationArg /** diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt index 10f1ebb536..7854580d33 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.columns.FrameColumn import org.jetbrains.kotlinx.dataframe.exceptions.DuplicateColumnNamesException import org.jetbrains.kotlinx.dataframe.exceptions.UnequalColumnSizesException +import org.jetbrains.kotlinx.dataframe.impl.ColumnNameGenerator import org.jetbrains.kotlinx.dataframe.impl.DataFrameImpl import org.jetbrains.kotlinx.dataframe.impl.asList import org.jetbrains.kotlinx.dataframe.impl.columnName @@ -23,6 +24,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.createColumn import org.jetbrains.kotlinx.dataframe.impl.columns.createComputedColumnReference import org.jetbrains.kotlinx.dataframe.impl.columns.forceResolve import org.jetbrains.kotlinx.dataframe.impl.columns.unbox +import org.jetbrains.kotlinx.dataframe.impl.unnamedColumnPrefix import org.jetbrains.kotlinx.dataframe.size import kotlin.random.Random import kotlin.random.nextInt @@ -348,6 +350,34 @@ public class DataFrameBuilder(private val header: List) { public fun randomBoolean(nrow: Int): AnyFrame = fillNotNull(nrow) { Random.nextBoolean() } } +/** + * Helper class for implementing operations when column names can be potentially duplicated. + * For example, operations involving multiple dataframes, computed columns or parsing some third-party data + */ +public class DynamicDataFrameBuilder { + private var cols: MutableList = mutableListOf() + private val generator = ColumnNameGenerator() + + public fun add(col: AnyCol): String { + val uniqueName = if (col.name().isEmpty()) { + generator.addUnique(unnamedColumnPrefix) + } else { + generator.addUnique(col.name()) + } + val renamed = if (uniqueName != col.name()) { + col.rename(uniqueName) + } else { + col + } + cols.add(renamed) + return uniqueName + } + + public fun toDataFrame(): AnyFrame { + return dataFrameOf(cols) + } +} + /** * Returns [DataFrame] with no rows and no columns. * diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt index 99f64b4d75..cea811b37e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt @@ -17,7 +17,7 @@ import kotlin.reflect.KType import kotlin.reflect.typeOf /** - * Specifies how to handle columns in original dataframe that were not mathced to any column in destination dataframe schema. + * Specifies how to handle columns in original dataframe that were not matched to any column in destination dataframe schema. */ public enum class ExcessiveColumns { /** diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt index 6fd14236b4..c985766b28 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/join.kt @@ -10,7 +10,9 @@ public fun DataFrame
.join( other: DataFrame, type: JoinType = JoinType.Inner, selector: JoinColumnsSelector? = null -): DataFrame = joinImpl(other, type, true, selector) +): DataFrame { + return joinImpl(other, type, addNewColumns = type.addNewColumns, selector) +} public fun DataFrame.join( other: DataFrame, @@ -116,10 +118,17 @@ public enum class JoinType { Left, // all data from left data frame, nulls for mismatches in right data frame Right, // all data from right data frame, nulls for mismatches in left data frame Inner, // only matched data from right and left data frame + Filter, // only matched data from left data frame Full, // all data from left and from right data frame, nulls for any mismatches Exclude // mismatched rows from left data frame } +internal val JoinType.addNewColumns: Boolean + get() = when (this) { + JoinType.Filter, JoinType.Exclude -> false + JoinType.Left, JoinType.Right, JoinType.Inner, JoinType.Full -> true + } + public val JoinType.allowLeftNulls: Boolean get() = this == JoinType.Right || this == JoinType.Full public val JoinType.allowRightNulls: Boolean get() = this == JoinType.Left || this == JoinType.Full || this == JoinType.Exclude diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt new file mode 100644 index 0000000000..b5d4ef8f4c --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/joinWith.kt @@ -0,0 +1,50 @@ +package org.jetbrains.kotlinx.dataframe.api + +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.DataRow +import org.jetbrains.kotlinx.dataframe.Selector +import org.jetbrains.kotlinx.dataframe.impl.api.joinWithImpl + +public interface JoinedDataRow : DataRow { + public val right: DataRow +} + +public typealias JoinExpression = Selector, Boolean> + +public fun DataFrame.joinWith( + right: DataFrame, + type: JoinType = JoinType.Inner, + joinExpression: JoinExpression +): DataFrame { + return joinWithImpl(right, type, addNewColumns = type.addNewColumns, joinExpression) +} + +public fun DataFrame.innerJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Inner, joinExpression) + +public fun DataFrame.leftJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Left, joinExpression) + +public fun DataFrame.rightJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Right, joinExpression) + +public fun DataFrame.fullJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWith(right, JoinType.Full, joinExpression) + +public fun DataFrame.filterJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWithImpl(right, JoinType.Inner, addNewColumns = false, joinExpression) + +public fun DataFrame.excludeJoinWith( + right: DataFrame, + joinExpression: JoinExpression +): DataFrame = joinWithImpl(right, JoinType.Exclude, addNewColumns = false, joinExpression) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt index 795006d42d..fc949c1e6d 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt @@ -5,11 +5,15 @@ import org.jetbrains.kotlinx.dataframe.DataFrame import org.jetbrains.kotlinx.dataframe.DataFrameExpression import org.jetbrains.kotlinx.dataframe.DataRow import org.jetbrains.kotlinx.dataframe.Selector +import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup import org.jetbrains.kotlinx.dataframe.columns.ColumnReference import org.jetbrains.kotlinx.dataframe.columns.ColumnSet +import org.jetbrains.kotlinx.dataframe.columns.FrameColumn import org.jetbrains.kotlinx.dataframe.columns.UnresolvedColumnsPolicy import org.jetbrains.kotlinx.dataframe.columns.ValueColumn import org.jetbrains.kotlinx.dataframe.columns.toColumnSet +import org.jetbrains.kotlinx.dataframe.documentation.Indent +import org.jetbrains.kotlinx.dataframe.documentation.LineBreak import org.jetbrains.kotlinx.dataframe.impl.api.SortFlag import org.jetbrains.kotlinx.dataframe.impl.api.addFlag import org.jetbrains.kotlinx.dataframe.impl.api.sortByImpl @@ -56,6 +60,40 @@ public fun > DataColumn.sort(): ValueColumn = public fun > DataColumn.sortDesc(): ValueColumn = DataColumn.createValueColumn(name, values().sortedDescending(), type, defaultValue = defaultValue()) +/** + * ## Sort [DataColumn] With + * + * This function returns the sorted version of the current [ValueColumn], [FrameColumn], or [ColumnGroup] based + * on the given [Comparator]. The [comparator\] can either be given as an instance of [Comparator], or directly + * as a lambda. + * + * #### For example + * + * `df`[`[`][DataFrame.get]`"price"`[`]`][DataFrame.get]`.`[sortWith][sortWith]` { a, b -> a - b }` + * + * {@include [LineBreak]} + * `df.`[select][DataFrame.select]` {` + * + * {@include [Indent]}`name.`[sortWith][sortWith]`(myComparator) `[and][ColumnsSelectionDsl.and]` `[allAfter][ColumnsSelectionDsl.allAfter]`(name)` + * + * `}` + * + * @receiver The [DataColumn] to sort. This can be either a [ValueColumn], [FrameColumn], or [ColumnGroup] and will + * dictate the return type of the function. + * @param [comparator\] The [Comparator] to use for sorting the [DataColumn]. This can either be a [Comparator]<[T\]> or + * a lambda of type `(`[T][T\]`, `[T][T\]`) -> `[Int][Int]. + * @return The sorted [DataColumn] [this\] of the same type as the receiver. + */ +private interface CommonDataColumnSortWithDocs + +/** @include [CommonDataColumnSortWithDocs] */ +public fun > C.sortWith(comparator: Comparator): C = + DataColumn.create(name, values().sortedWith(comparator), type) as C + +/** @include [CommonDataColumnSortWithDocs] */ +public fun > C.sortWith(comparator: (T, T) -> Int): C = + sortWith(Comparator(comparator)) + // endregion // region DataFrame diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt index 8cddfeaf0a..57d956fcee 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/update.kt @@ -12,6 +12,10 @@ import org.jetbrains.kotlinx.dataframe.impl.api.updateImpl import org.jetbrains.kotlinx.dataframe.impl.api.updateWithValuePerColumnImpl import org.jetbrains.kotlinx.dataframe.impl.headPlusArray import org.jetbrains.kotlinx.dataframe.util.ITERABLE_COLUMNS_DEPRECATION_MESSAGE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_AS_NULLABLE_MESSAGE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_AS_NULLABLE_REPLACE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_WITH_VALUE +import org.jetbrains.kotlinx.dataframe.util.UPDATE_WITH_VALUE_REPLACE import kotlin.reflect.KProperty /** @@ -38,9 +42,9 @@ public data class Update( internal interface UpdateOperationArg /** - * ## {@includeArg [UpdateOperationArg]} Operation Usage + * ## {@getArg [UpdateOperationArg]} Operation Usage * - * {@includeArg [UpdateOperationArg]} `{ `[columns][SelectingColumns]` }` + * {@getArg [UpdateOperationArg]} `{ `[columns][SelectingColumns]` }` * * - `[.`[where][Update.where]` { `[rowValueCondition][SelectingRows.RowValueCondition.WithExample]` } ]` * @@ -53,7 +57,7 @@ public data class Update( * | .`[withNull][Update.withNull]`() * | .`[withZero][Update.withZero]`() * | .`[asFrame][Update.asFrame]` { `[dataFrameExpression][ExpressionsGivenDataFrame.DataFrameExpression.WithExample]` }` - * {@arg [UpdateOperationArg] [update][update]}{@comment The default name of the `update` operation function name.} + * {@setArg [UpdateOperationArg] [update][update]}{@comment The default name of the `update` operation function name.} */ public interface Usage @@ -75,7 +79,7 @@ public data class Update( // region update -/** {@arg [SelectingColumns.OperationArg] [update][update]} */ +/** {@setArg [SelectingColumns.OperationArg] [update][update]} */ private interface SetSelectingColumnsOperationArg /** @@ -141,8 +145,8 @@ public fun DataFrame.update(columns: Iterable>): Up /** ## Where * @include [SelectingRows.RowValueCondition.WithExample] - * {@arg [SelectingRows.FirstOperationArg] [update][update]} - * {@arg [SelectingRows.SecondOperationArg] [where][where]} + * {@setArg [SelectingRows.FirstOperationArg] [update][update]} + * {@setArg [SelectingRows.SecondOperationArg] [where][where]} * * @param [predicate] The [row value filter][RowValueFilter] to select the rows to update. */ @@ -197,7 +201,7 @@ public fun Update.at(rowRange: IntRange): Update = where { in /** ## Per Row Col * @include [ExpressionsGivenRowAndColumn.RowColumnExpression.WithExample] - * {@arg [ExpressionsGivenRowAndColumn.OperationArg] [update][update]` { age \\\\}.`[perRowCol][perRowCol]} + * {@setArg [ExpressionsGivenRowAndColumn.OperationArg] [update][update]` { age \\\\}.`[perRowCol][perRowCol]} * * ## See Also * - {@include [SeeAlsoWith]} @@ -218,7 +222,7 @@ public typealias UpdateExpression = AddDataRow.(C) -> R /** ## With * {@include [ExpressionsGivenRow.RowValueExpression.WithExample]} - * {@arg [ExpressionsGivenRow.OperationArg] [update][update]` { city \}.`[with][with]} + * {@setArg [ExpressionsGivenRow.OperationArg] [update][update]` { city \}.`[with][with]} * * ## Note * @include [ExpressionsGivenRow.AddDataRowNote] @@ -240,15 +244,16 @@ private interface SeeAlsoWith * Updates selected [column group][ColumnGroup] as a [DataFrame] with the given [expression]. * * {@include [ExpressionsGivenDataFrame.DataFrameExpression.WithExample]} - * {@arg [ExpressionsGivenDataFrame.OperationArg] `df.`[update][update]` { name \}.`[asFrame][asFrame]} + * {@setArg [ExpressionsGivenDataFrame.OperationArg] `df.`[update][update]` { name \}.`[asFrame][asFrame]} * @param [expression] The {@include [ExpressionsGivenDataFrame.DataFrameExpressionLink]} to replace the selected column group with. */ public fun Update>.asFrame(expression: DataFrameExpression>): DataFrame = asFrameImpl(expression) @Deprecated( - "Useless unless in combination with `withValue(null)`, but then users can just use `with { null }`...", - ReplaceWith("this as Update") + UPDATE_AS_NULLABLE_MESSAGE, + ReplaceWith(UPDATE_AS_NULLABLE_REPLACE), + DeprecationLevel.WARNING, ) public fun Update.asNullable(): Update = this as Update @@ -275,7 +280,7 @@ private interface UpdatePerColMap * * For example: * - * `val defaults = {@includeArg [CommonUpdatePerColMapDoc]}` + * `val defaults = {@getArg [CommonUpdatePerColMapDoc]}` * * `df.`[update][update]` { name and age }.`[where][Update.where]` { ... }.`[perCol][perCol]`(defaults)` * @@ -285,7 +290,7 @@ private interface CommonUpdatePerColMapDoc /** * @include [CommonUpdatePerColMapDoc] - * {@arg [CommonUpdatePerColMapDoc] `[mapOf][mapOf]`("name" to "Empty", "age" to 0)} + * {@setArg [CommonUpdatePerColMapDoc] `[mapOf][mapOf]`("name" to "Empty", "age" to 0)} * * @param [values] The [Map]<[String], Value> to provide a new value for every selected cell. * For each selected column, there must be a value in the map with the same name. @@ -296,7 +301,7 @@ public fun Update.perCol(values: Map): DataFrame = up /** * {@include [CommonUpdatePerColMapDoc]} - * {@arg [CommonUpdatePerColMapDoc] df.`[getRows][DataFrame.getRows]`(`[listOf][listOf]`(0))` + * {@setArg [CommonUpdatePerColMapDoc] df.`[getRows][DataFrame.getRows]`(`[listOf][listOf]`(0))` * * `.`[update][update]` { name \}.`[with][Update.with]` { "Empty" \}` * @@ -311,7 +316,7 @@ public fun Update.perCol(values: AnyRow): DataFrame = perCol(val /** * @include [CommonUpdatePerColDoc] * @include [ExpressionsGivenColumn.ColumnExpression.WithExample] - * {@arg [ExpressionsGivenColumn.OperationArg] [update][update]` { age \}.`[perCol][perCol]} + * {@setArg [ExpressionsGivenColumn.OperationArg] [update][update]` { age \}.`[perCol][perCol]} * * @param [valueSelector] The {@include [ExpressionsGivenColumn.ColumnExpressionLink]} to provide a new value for every selected cell giving its column. */ @@ -363,7 +368,7 @@ public fun Update.notNull(expression: UpdateExpression): * @include [SelectingColumns.ColumnAccessors] * * {@include [ExpressionsGivenRow.RowValueExpression.WithExample]} - * {@arg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } + * {@setArg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } * * @include [Update.ColumnAccessorsParam] * @param [expression] The {@include [ExpressionsGivenRow.RowValueExpressionLink]} to update the rows with. @@ -382,7 +387,7 @@ public fun DataFrame.update( * @include [SelectingColumns.KProperties] * * {@include [ExpressionsGivenRow.RowValueExpression.WithExample]} - * {@arg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } + * {@setArg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } * * @include [Update.KPropertiesParam] * @param [expression] The {@include [ExpressionsGivenRow.RowValueExpressionLink]} to update the rows with. @@ -401,7 +406,7 @@ public fun DataFrame.update( * @include [SelectingColumns.ColumnNames] * * {@include [ExpressionsGivenRow.RowValueExpression.WithExample]} - * {@arg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } + * {@setArg [ExpressionsGivenRow.OperationArg] [update][update]`("city")` } * * @include [Update.ColumnNamesParam] * @param [expression] The {@include [ExpressionsGivenRow.RowValueExpressionLink]} to update the rows with. @@ -414,11 +419,11 @@ public fun DataFrame.update( update(*headPlusArray(firstCol, cols)).with(expression) /** - * Specific version of [with] that simply sets the value of each selected row to {@includeArg [CommonSpecificWithDocFirstArg]}. + * Specific version of [with] that simply sets the value of each selected row to {@getArg [CommonSpecificWithDocFirstArg]}. * * For example: * - * `df.`[update][update]` { id }.`[where][Update.where]` { it < 0 }.`{@includeArg [CommonSpecificWithDocSecondArg]}` + * `df.`[update][update]` { id }.`[where][Update.where]` { it < 0 }.`{@getArg [CommonSpecificWithDocSecondArg]}` */ private interface CommonSpecificWithDoc @@ -431,26 +436,26 @@ private interface CommonSpecificWithDocSecondArg /** * ## With Null * @include [CommonSpecificWithDoc] - * {@arg [CommonSpecificWithDocFirstArg] `null`} - * {@arg [CommonSpecificWithDocSecondArg] [withNull][withNull]`()} + * {@setArg [CommonSpecificWithDocFirstArg] `null`} + * {@setArg [CommonSpecificWithDocSecondArg] [withNull][withNull]`()} */ public fun Update.withNull(): DataFrame = with { null } /** * ## With Zero * @include [CommonSpecificWithDoc] - * {@arg [CommonSpecificWithDocFirstArg] `0`} - * {@arg [CommonSpecificWithDocSecondArg] [withZero][withZero]`()} + * {@setArg [CommonSpecificWithDocFirstArg] `0`} + * {@setArg [CommonSpecificWithDocSecondArg] [withZero][withZero]`()} */ public fun Update.withZero(): DataFrame = updateWithValuePerColumnImpl { 0 as C } /** - * ## With Value + * ## With Value (Deprecated) * @include [CommonSpecificWithDoc] - * {@arg [CommonSpecificWithDocFirstArg] [value]} - * {@arg [CommonSpecificWithDocSecondArg] [withValue][withValue]`(-1)} + * {@setArg [CommonSpecificWithDocFirstArg] [value]} + * {@setArg [CommonSpecificWithDocSecondArg] [withValue][withValue]`(-1)} * * @param [value] The value to set the selected rows to. In contrast to [with][Update.with], this must be the same exact type. */ -@Deprecated("Use with { value } instead", ReplaceWith("this.with { value }")) +@Deprecated(UPDATE_WITH_VALUE, ReplaceWith(UPDATE_WITH_VALUE_REPLACE), DeprecationLevel.WARNING) public fun Update.withValue(value: C): DataFrame = with { value } 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 edca077c6b..428569c630 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 @@ -4,7 +4,7 @@ internal interface DocumentationUrls { interface NameArg - /** See {@includeArg [NameArg]} on the documentation website. */ + /** See {@getArg [NameArg]} on the documentation website. */ interface Text /** https://kotlin.github.io/dataframe */ @@ -12,68 +12,68 @@ internal interface DocumentationUrls { interface DataRow { - /** [{@include [Text]}{@arg [NameArg] Row Expressions}]({@include [Url]}/datarow.html#row-expressions) */ + /** [{@include [Text]}{@setArg [NameArg] Row Expressions}]({@include [Url]}/datarow.html#row-expressions) */ interface RowExpressions - /** [{@include [Text]}{@arg [NameArg] Row Conditions}]({@include [Url]}/datarow.html#row-conditions) */ + /** [{@include [Text]}{@setArg [NameArg] Row Conditions}]({@include [Url]}/datarow.html#row-conditions) */ interface RowConditions } - /** [{@include [Text]}{@arg [NameArg] `NaN` and `NA`}]({@include [Url]}/nanAndNa.html) */ + /** [{@include [Text]}{@setArg [NameArg] `NaN` and `NA`}]({@include [Url]}/nanAndNa.html) */ interface NanAndNa { - /** [{@include [Text]}{@arg [NameArg] `NaN`}]({@include [Url]}/nanAndNa.html#nan) */ + /** [{@include [Text]}{@setArg [NameArg] `NaN`}]({@include [Url]}/nanAndNa.html#nan) */ interface NaN - /** [{@include [Text]}{@arg [NameArg] `NA`}]({@include [Url]}/nanAndNa.html#na) */ + /** [{@include [Text]}{@setArg [NameArg] `NA`}]({@include [Url]}/nanAndNa.html#na) */ interface NA } - /** [{@include [Text]}{@arg [NameArg] `update`}]({@include [Url]}/update.html) */ + /** [{@include [Text]}{@setArg [NameArg] `update`}]({@include [Url]}/update.html) */ interface Update - /** [{@include [Text]}{@arg [NameArg] `fill`}]({@include [Url]}/fill.html) */ + /** [{@include [Text]}{@setArg [NameArg] `fill`}]({@include [Url]}/fill.html) */ interface Fill { - /** [{@include [Text]}{@arg [NameArg] `fillNulls`}]({@include [Url]}/fill.html#fillnulls) */ + /** [{@include [Text]}{@setArg [NameArg] `fillNulls`}]({@include [Url]}/fill.html#fillnulls) */ interface FillNulls - /** [{@include [Text]}{@arg [NameArg] `fillNaNs`}]({@include [Url]}/fill.html#fillnans) */ + /** [{@include [Text]}{@setArg [NameArg] `fillNaNs`}]({@include [Url]}/fill.html#fillnans) */ interface FillNaNs - /** [{@include [Text]}{@arg [NameArg] `fillNA`}]({@include [Url]}/fill.html#fillna) */ + /** [{@include [Text]}{@setArg [NameArg] `fillNA`}]({@include [Url]}/fill.html#fillna) */ interface FillNA } - /** [{@include [Text]}{@arg [NameArg] `drop`}]({@include [Url]}/drop.html) */ + /** [{@include [Text]}{@setArg [NameArg] `drop`}]({@include [Url]}/drop.html) */ interface Drop { - /** [{@include [Text]}{@arg [NameArg] `dropNulls`}]({@include [Url]}/drop.html#dropnulls) */ + /** [{@include [Text]}{@setArg [NameArg] `dropNulls`}]({@include [Url]}/drop.html#dropnulls) */ interface DropNulls - /** [{@include [Text]}{@arg [NameArg] `dropNaNs`}]({@include [Url]}/drop.html#dropnans) */ + /** [{@include [Text]}{@setArg [NameArg] `dropNaNs`}]({@include [Url]}/drop.html#dropnans) */ interface DropNaNs - /** [{@include [Text]}{@arg [NameArg] `dropNA`}]({@include [Url]}/drop.html#dropna) */ + /** [{@include [Text]}{@setArg [NameArg] `dropNA`}]({@include [Url]}/drop.html#dropna) */ interface DropNA } - /** [{@include [Text]}{@arg [NameArg] Access APIs}]({@include [Url]}/apilevels.html) */ + /** [{@include [Text]}{@setArg [NameArg] Access APIs}]({@include [Url]}/apilevels.html) */ interface AccessApis { - /** [{@include [Text]}{@arg [NameArg] String API}]({@include [Url]}/stringapi.html) */ + /** [{@include [Text]}{@setArg [NameArg] String API}]({@include [Url]}/stringapi.html) */ interface StringApi - /** [{@include [Text]}{@arg [NameArg] Column Accessors API}]({@include [Url]}/columnaccessorsapi.html) */ + /** [{@include [Text]}{@setArg [NameArg] Column Accessors API}]({@include [Url]}/columnaccessorsapi.html) */ interface ColumnAccessorsApi - /** [{@include [Text]}{@arg [NameArg] KProperties API}]({@include [Url]}/kpropertiesapi.html) */ + /** [{@include [Text]}{@setArg [NameArg] KProperties API}]({@include [Url]}/kpropertiesapi.html) */ interface KPropertiesApi - /** [{@include [Text]}{@arg [NameArg] Extension Properties API}]({@include [Url]}/extensionpropertiesapi.html) */ + /** [{@include [Text]}{@setArg [NameArg] Extension Properties API}]({@include [Url]}/extensionpropertiesapi.html) */ interface ExtensionPropertiesApi } - /** [{@include [Text]}{@arg [NameArg] Column Selectors}]({@include [Url]}/columnselectors.html) */ + /** [{@include [Text]}{@setArg [NameArg] Column Selectors}]({@include [Url]}/columnselectors.html) */ interface ColumnSelectors } diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt index 3a0f611092..c3fa49a553 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenColumn.kt @@ -13,12 +13,12 @@ import org.jetbrains.kotlinx.dataframe.ColumnExpression as DfColumnExpression internal interface ExpressionsGivenColumn { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg - /** {@arg [OperationArg] operation} */ + /** {@setArg [OperationArg] operation} */ interface SetDefaultOperationArg /** Provide a new value for every selected cell given its column using a [column expression][DfColumnExpression]. */ @@ -29,9 +29,9 @@ internal interface ExpressionsGivenColumn { * * For example: * - * `df.`{@includeArg [OperationArg]}` { `[mean][DataColumn.mean]`(skipNA = true) }` + * `df.`{@getArg [OperationArg]}` { `[mean][DataColumn.mean]`(skipNA = true) }` * - * `df.`{@includeArg [OperationArg]}` { `[count][DataColumn.count]` { it > 10 } }` + * `df.`{@getArg [OperationArg]}` { `[count][DataColumn.count]` { it > 10 } }` * @include [SetDefaultOperationArg] */ interface WithExample diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt index eba50e1657..cfdf12dfc2 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenDataFrame.kt @@ -22,7 +22,7 @@ internal interface ExpressionsGivenDataFrame { * * For example: * - * {@includeArg [OperationArg]}` { `[select][DataFrame.select]` { lastName } }` + * {@getArg [OperationArg]}` { `[select][DataFrame.select]` { lastName } }` */ interface WithExample } 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 aa3fa6d010..03cb30bc9f 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 @@ -29,12 +29,12 @@ import org.jetbrains.kotlinx.dataframe.RowValueExpression as DfRowValueExpressio internal interface ExpressionsGivenRow { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg - /** {@arg [OperationArg] operation} */ + /** {@setArg [OperationArg] operation} */ interface SetDefaultOperationArg /** @@ -53,9 +53,9 @@ internal interface ExpressionsGivenRow { * * For example: * - * `df.`{@includeArg [OperationArg]}` { name.firstName + " " + name.lastName }` + * `df.`{@getArg [OperationArg]}` { name.firstName + " " + name.lastName }` * - * `df.`{@includeArg [OperationArg]}` { 2021 - age }` + * `df.`{@getArg [OperationArg]}` { 2021 - age }` * @include [SetDefaultOperationArg] */ interface WithExample @@ -74,9 +74,9 @@ internal interface ExpressionsGivenRow { * * For example: * - * `df.`{@includeArg [OperationArg]}` { name.firstName + " from " + it }` + * `df.`{@getArg [OperationArg]}` { name.firstName + " from " + it }` * - * `df.`{@includeArg [OperationArg]}` { it.uppercase() }` + * `df.`{@getArg [OperationArg]}` { it.uppercase() }` * {@include [SetDefaultOperationArg]} */ interface WithExample diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt index c640597d6c..1fee481a01 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/ExpressionsGivenRowAndColumn.kt @@ -13,12 +13,12 @@ import org.jetbrains.kotlinx.dataframe.RowColumnExpression as DfRowColumnExpress internal interface ExpressionsGivenRowAndColumn { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg - /** {@arg [OperationArg] operation} */ + /** {@setArg [OperationArg] operation} */ interface SetDefaultOperationArg /** Provide a new value for every selected cell given both its row and column using a [row-column expression][DfRowColumnExpression]. */ @@ -29,7 +29,7 @@ internal interface ExpressionsGivenRowAndColumn { * * For example: * - * `df.`{@includeArg [OperationArg]}` { row, col ->` + * `df.`{@getArg [OperationArg]}` { row, col ->` * * `row.age / col.`[mean][DataColumn.mean]`(skipNA = true)` * diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt index 755ae5e4ee..818c9dec9e 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/SelectingColumns.kt @@ -26,12 +26,12 @@ internal interface SelectingColumnsLink internal interface SelectingColumns { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface OperationArg - /** {@arg [OperationArg] operation} */ + /** {@setArg [OperationArg] operation} */ interface SetDefaultOperationArg /** Select or express columns using the Column(s) Selection DSL. @@ -47,11 +47,11 @@ internal interface SelectingColumns { * * For example: * - * `df.`{@includeArg [OperationArg]}` { length `[and][ColumnsSelectionDsl.and]` age }` + * `df.`{@getArg [OperationArg]}` { length `[and][ColumnsSelectionDsl.and]` age }` * - * `df.`{@includeArg [OperationArg]}` { `[cols][ColumnsSelectionDsl.cols]`(1..5) }` + * `df.`{@getArg [OperationArg]}` { `[cols][ColumnsSelectionDsl.cols]`(1..5) }` * - * `df.`{@includeArg [OperationArg]}` { `[colsOf][colsOf]`<`[Double][Double]`>() }` + * `df.`{@getArg [OperationArg]}` { `[colsOf][colsOf]`<`[Double][Double]`>() }` * @include [SetDefaultOperationArg] */ interface WithExample @@ -69,7 +69,7 @@ internal interface SelectingColumns { * * For example: * - * `df.`{@includeArg [OperationArg]}`("length", "age")` + * `df.`{@getArg [OperationArg]}`("length", "age")` * @include [SetDefaultOperationArg] */ interface WithExample @@ -91,7 +91,7 @@ internal interface SelectingColumns { * * `val age by `[column][column]`<`[Double][Double]`>()` * - * `df.`{@includeArg [OperationArg]}`(length, age)` + * `df.`{@getArg [OperationArg]}`(length, age)` * @include [SetDefaultOperationArg] */ interface WithExample @@ -110,7 +110,7 @@ internal interface SelectingColumns { * data class Person(val length: Double, val age: Double) * ``` * - * `df.`{@includeArg [OperationArg]}`(Person::length, Person::age)` + * `df.`{@getArg [OperationArg]}`(Person::length, Person::age)` * @include [SetDefaultOperationArg] */ interface WithExample 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 c560e0966b..daabc164c6 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 @@ -22,18 +22,18 @@ import org.jetbrains.kotlinx.dataframe.index internal interface SelectingRows { /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface FirstOperationArg /** - * The key for an @arg that will define the operation name for the examples below. + * The key for an @setArg that will define the operation name for the examples below. * Make sure to [alias][your examples]. */ interface SecondOperationArg - /** {@arg [FirstOperationArg] operation}{@arg [SecondOperationArg] where} */ + /** {@setArg [FirstOperationArg] operation}{@setArg [SecondOperationArg] where} */ interface SetDefaultOperationArg /** [Entire-Row Condition][EntireRowCondition.WithExample] */ @@ -47,9 +47,9 @@ internal interface SelectingRows { * * For example: * - * `df.`{@includeArg [FirstOperationArg]}` { `[index][index]`() % 2 == 0 }` + * `df.`{@getArg [FirstOperationArg]}` { `[index][index]`() % 2 == 0 }` * - * `df.`{@includeArg [FirstOperationArg]}` { `[diff][diff]` { age } == 0 }` + * `df.`{@getArg [FirstOperationArg]}` { `[diff][diff]` { age } == 0 }` * @include [SetDefaultOperationArg] */ interface WithExample @@ -68,9 +68,9 @@ internal interface SelectingRows { * * For example: * - * `df.`{@includeArg [FirstOperationArg]}` { length }.`{@includeArg [SecondOperationArg]}` { it > 10.0 }` + * `df.`{@getArg [FirstOperationArg]}` { length }.`{@getArg [SecondOperationArg]}` { it > 10.0 }` * - * `df.`{@includeArg [FirstOperationArg]}` { `[cols][ColumnsSelectionDsl.cols]`(1..5) }.`{@includeArg [SecondOperationArg]}` { `[index][index]`() > 4 && city != "Paris" }` + * `df.`{@getArg [FirstOperationArg]}` { `[cols][ColumnsSelectionDsl.cols]`(1..5) }.`{@getArg [SecondOperationArg]}` { `[index][index]`() > 4 && city != "Paris" }` * @include [SetDefaultOperationArg] */ interface WithExample diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt index 3660dd5397..312bae0f4f 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/documentation/utils.kt @@ -1,4 +1,14 @@ package org.jetbrains.kotlinx.dataframe.documentation -/** ## ‎ */ +/** + * + * {@include [Indent]} + * + */ internal interface LineBreak + +/**      */ +internal interface Indent + +/** {@include [Indent]}{@include [Indent]} */ +internal interface DoubleIndent diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt index 1881b97314..93fe387f83 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/DataFrameImpl.kt @@ -27,7 +27,7 @@ import org.jetbrains.kotlinx.dataframe.impl.columns.resolveSingle import org.jetbrains.kotlinx.dataframe.io.renderToString import kotlin.reflect.KProperty -private const val unnamedColumnPrefix = "untitled" +internal const val unnamedColumnPrefix = "untitled" internal open class DataFrameImpl(cols: List, val nrow: Int) : DataFrame, AggregatableInternal { diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt index daad1cde21..37a63906c4 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/join.kt @@ -29,12 +29,12 @@ import org.jetbrains.kotlinx.dataframe.type import kotlin.reflect.full.withNullability internal fun defaultJoinColumns(left: DataFrame, right: DataFrame): JoinColumnsSelector = - { left.columnNames().intersect(right.columnNames()).map { it.toColumnAccessor() }.let { ColumnsList(it) } } + { left.columnNames().intersect(right.columnNames().toSet()).map { it.toColumnAccessor() }.let { ColumnsList(it) } } internal fun defaultJoinColumns(dataFrames: Iterable>): JoinColumnsSelector = { dataFrames.map { it.columnNames() }.fold, Set?>(null) { set, names -> - set?.intersect(names) ?: names.toSet() + set?.intersect(names.toSet()) ?: names.toSet() }.orEmpty().map { it.toColumnAccessor() }.let { ColumnsList(it) } } @@ -114,7 +114,7 @@ internal fun DataFrame.joinImpl( // group row indices by key from right data frame val groupedRight = when (joinType) { - JoinType.Exclude -> rightJoinKeyToIndex.map { it.first to emptyList() }.toMap() + JoinType.Exclude -> rightJoinKeyToIndex.associate { it.first to emptyList() } else -> rightJoinKeyToIndex.groupBy({ it.first }) { it.second } } @@ -129,7 +129,7 @@ internal fun DataFrame.joinImpl( } // for every row index in right data frame store a flag indicating whether this row was matched by some row in left data frame - val rightMatched = Array(other.nrow) { false } + val rightMatched = BooleanArray(other.nrow) { false } // number of rows in right data frame that were not matched by any row in left data frame. Used for correct allocation of an output array var rightUnmatchedCount = other.nrow @@ -162,8 +162,8 @@ internal fun DataFrame.joinImpl( val newRightColumnsCount = newRightColumns.size val outputColumnsCount = leftColumnsCount + newRightColumnsCount - val outputData = Array>(outputColumnsCount) { arrayOfNulls(outputRowsCount) } - val hasNulls = Array(outputColumnsCount) { false } + val outputData = List>(outputColumnsCount) { arrayOfNulls(outputRowsCount) } + val hasNulls = BooleanArray(outputColumnsCount) { false } var row = 0 diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt new file mode 100644 index 0000000000..306e4678bb --- /dev/null +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/joinWith.kt @@ -0,0 +1,98 @@ +package org.jetbrains.kotlinx.dataframe.impl.api + +import org.jetbrains.kotlinx.dataframe.DataColumn +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.DataRow +import org.jetbrains.kotlinx.dataframe.api.JoinExpression +import org.jetbrains.kotlinx.dataframe.api.JoinType +import org.jetbrains.kotlinx.dataframe.api.JoinedDataRow +import org.jetbrains.kotlinx.dataframe.api.allowLeftNulls +import org.jetbrains.kotlinx.dataframe.api.allowRightNulls +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.count +import org.jetbrains.kotlinx.dataframe.api.indices +import org.jetbrains.kotlinx.dataframe.api.toDataFrame +import org.jetbrains.kotlinx.dataframe.impl.ColumnNameGenerator +import org.jetbrains.kotlinx.dataframe.impl.DataRowImpl + +internal class JoinedDataRowImpl( + leftOwner: DataFrame, + val index: Int, + rightOwner: DataFrame, + index1: Int +) : JoinedDataRow, DataRowImpl(index, leftOwner) { + override val right: DataRow = DataRowImpl(index1, rightOwner) +} + +internal fun DataFrame.joinWithImpl( + right: DataFrame, + type: JoinType = JoinType.Inner, + addNewColumns: Boolean, + joinExpression: JoinExpression +): DataFrame { + val generator = ColumnNameGenerator(columnNames()) + if (addNewColumns) { + right.columnNames().forEach { generator.addUnique(it) } + } + val rightColumnsCount = if (addNewColumns) right.columnsCount() else 0 + val outputData = List(columnsCount() + rightColumnsCount) { mutableListOf() } + val rightMatched = BooleanArray(right.count()) { false } + for (l in indices()) { + var leftMatched = false + for (r in right.indices()) { + val joined = JoinedDataRowImpl(this, l, right, r) + val matched = joinExpression(joined, joined) + if (matched && type == JoinType.Exclude) { + leftMatched = true + break + } + if (matched) { + rightMatched[r] = true + leftMatched = true + val left = get(l).values() + for (col in left.indices) { + outputData[col].add(left[col]) + } + if (addNewColumns) { + val offset = left.size + val row = right.get(r).values() + for (col in row.indices) { + outputData[col + offset].add(row[col]) + } + } + } + } + if (!leftMatched && type.allowRightNulls) { + val left = get(l).values() + for (col in left.indices) { + outputData[col].add(left[col]) + } + if (addNewColumns) { + for (col in left.size..outputData.lastIndex) { + outputData[col].add(null) + } + } + } + } + + if (type.allowLeftNulls) { + rightMatched.forEachIndexed { row, matched -> + if (!matched) { + repeat(columnsCount()) { col -> + outputData[col].add(null) + } + val offset = columnsCount() + val rowData = right[row].values() + for (col in rowData.indices) { + outputData[offset + col].add(rowData[col]) + } + } + } + } + + val df: DataFrame<*> = outputData.mapIndexed { index, values -> + DataColumn.createWithTypeInference(generator.names[index], values) + }.toDataFrame() + + return df.cast() +} 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 229c13d0dc..cfbc6a73d0 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 @@ -183,7 +183,7 @@ public fun DataFrame.html(): String = toStandaloneHTML().toString() public fun DataFrame.toStandaloneHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, - getFooter: (DataFrame) -> String = { "DataFrame [${it.size}]" }, + getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions() /** @@ -192,25 +192,31 @@ public fun DataFrame.toStandaloneHTML( public fun DataFrame.toHTML( configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT, cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer, - getFooter: (DataFrame) -> String = { "DataFrame [${it.size}]" }, + getFooter: (DataFrame) -> String? = { "DataFrame [${it.size}]" }, ): DataFrameHtmlData { val limit = configuration.rowsLimit ?: Int.MAX_VALUE val footer = getFooter(this) - val bodyFooter = buildString { - val openPTag = "

" - if (limit < nrow) { + val bodyFooter = footer?.let { + buildString { + val openPTag = "

" + if (limit < nrow) { + append(openPTag) + append("... showing only top $limit of $nrow rows

") + } append(openPTag) - append("... showing only top $limit of $nrow rows

") + append(footer) + append("

") } - append(openPTag) - append(footer) - append("

") } - val tableHtml = toHtmlData(configuration, cellRenderer) + var tableHtml = toHtmlData(configuration, cellRenderer) + + if (bodyFooter != null) { + tableHtml += DataFrameHtmlData("", bodyFooter, "") + } - return tableHtml + DataFrameHtmlData("", bodyFooter, "") + return tableHtml } /** 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 aabe39112a..67e3c38f56 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 @@ -1,13 +1,41 @@ package org.jetbrains.kotlinx.dataframe.util -internal const val DF_READ_DEPRECATION_MESSAGE = "Replaced with `unfold` operation." +/* + * This file contains deprecation messages for the whole core module. + * After each release, all messages should be reviewed and updated. + * Level.WARNING -> Level.ERROR + * Level.ERROR -> Remove + */ +// region WARNING in 0.11.0, ERROR in 0.12.0 + +private const val message_0_12_0 = "Was removed in 0.12.0." + +internal const val DF_READ_DEPRECATION_MESSAGE = "Replaced with `unfold` operation. $message_0_12_0" internal const val DF_READ_REPLACE_MESSAGE = "this.unfold(*columns)" -internal const val ITERABLE_COLUMNS_DEPRECATION_MESSAGE = "Replaced with `toColumnSet()` operation." +internal const val ITERABLE_COLUMNS_DEPRECATION_MESSAGE = "Replaced with `toColumnSet()` operation. $message_0_12_0" -internal const val DIFF_DEPRECATION_MESSAGE = "Replaced to explicitly indicate nullable return value; added a new non-null overload." +// endregion -internal const val DIFF_REPLACE_MESSAGE = "this.diffOrNull(expression)" +// region WARNING in 0.12.0, ERROR in 0.13.0 +private const val message_0_13_0 = "Will be removed in 0.13.0." + +internal const val DIFF_DEPRECATION_MESSAGE = "Replaced to explicitly indicate nullable return value; added a new non-null overload. $message_0_13_0" +internal const val DIFF_REPLACE_MESSAGE = "this.diffOrNull(expression)" internal const val DIFF_OR_NULL_IMPORT = "org.jetbrains.kotlinx.dataframe.api.diffOrNull" + +internal const val UPDATE_AS_NULLABLE_MESSAGE = "This function is useless unless in combination with `withValue(null)`, but then you can just use `with { null }`. $message_0_13_0" +internal const val UPDATE_AS_NULLABLE_REPLACE = "this as Update" + +internal const val UPDATE_WITH_VALUE = "Replaced in favor of `with { value }`. $message_0_13_0" +internal const val UPDATE_WITH_VALUE_REPLACE = "this.with { value }" + +// endregion + +// region WARNING in 0.13.0, ERROR in 0.14.0 + +private const val message_0_14_0 = "Will be removed in 0.14.0." + +// endregion 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 new file mode 100644 index 0000000000..53f76daa45 --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/constructors.kt @@ -0,0 +1,27 @@ +package org.jetbrains.kotlinx.dataframe.api + +import io.kotest.matchers.shouldBe +import org.junit.Test + +class ConstructorsTests { + + @Test + fun `untitled column naming`() { + val builder = DynamicDataFrameBuilder() + repeat(5) { + builder.add(columnOf(1, 2, 3)) + } + builder.toDataFrame() shouldBe dataFrameOf(List(5) { columnOf(1, 2, 3) }) + } + + @Test + fun `duplicated name`() { + val builder = DynamicDataFrameBuilder() + val column by columnOf(1, 2, 3) + builder.add(column) + builder.add(column) + val df = builder.toDataFrame() + df.columnsCount() shouldBe 2 + df.columnNames() shouldBe listOf(column.name(), "${column.name()}1") + } +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt new file mode 100644 index 0000000000..8ac9ebc5f0 --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/sort.kt @@ -0,0 +1,70 @@ +package org.jetbrains.kotlinx.dataframe.api + +import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.DataColumn +import org.jetbrains.kotlinx.dataframe.nrow +import org.junit.Test + +class SortDataColumn { + + @Test + fun `value column sort with`() { + val col = DataColumn.createValueColumn("", listOf(1, 6, 8, 4, 2, 9)) + val sortedCol = col.sort() + val descSortedCol = col.sortDesc() + + col.sortWith { a, b -> a - b } shouldBe sortedCol + col.sortWith { a, b -> b - a } shouldBe descSortedCol + + col.sortWith(Int::compareTo) shouldBe sortedCol + col.sortWith(compareBy { it }) shouldBe sortedCol + } + + @Test + fun `frame column sort with`() { + val col = DataColumn.createFrameColumn( + "", + listOf( + dataFrameOf("a")(1, 2), + dataFrameOf("a")(1), + dataFrameOf("a")(1, 2, 3) + ) + ) + val sortedCol = DataColumn.createFrameColumn( + "", + listOf( + dataFrameOf("a")(1), + dataFrameOf("a")(1, 2), + dataFrameOf("a")(1, 2, 3) + ) + ) + + col.sortWith { df1, df2 -> df1.nrow - df2.nrow } shouldBe sortedCol + col.sortWith(compareBy { it.nrow }) shouldBe sortedCol + } + + @Test + fun `column group sort with`() { + val a by column() + val b by column() + + val col = DataColumn.createColumnGroup( + "", + dataFrameOf( + columnOf(1, 3, 2) named a, + columnOf("hello", "world", "!") named b, + ), + ) + + val sortedCol = DataColumn.createColumnGroup( + "", + dataFrameOf( + columnOf(1, 2, 3) named a, + columnOf("hello", "!", "world") named b, + ), + ) + + col.sortWith { df1, df2 -> df1[a] - df2[a] } shouldBe sortedCol + col.sortWith(compareBy { it[a] }) shouldBe sortedCol + } +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt index b9da8c9402..c3d34c30ed 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/explainer/PluginCallbackProxy.kt @@ -52,6 +52,11 @@ object PluginCallbackProxy : PluginCallback { val names = mutableMapOf>() val expressionsByStatement = mutableMapOf>() + private var manualOutput: DataFrameHtmlData? = null + fun overrideHtmlOutput(manualOutput: DataFrameHtmlData) { + this.manualOutput = manualOutput + } + data class Expression( val source: String, val containingClassFqName: String?, @@ -61,69 +66,76 @@ object PluginCallbackProxy : PluginCallback { fun start() { expressionsByStatement.clear() + manualOutput = null } fun save() { // ensure stable table ids across test invocation sessionId = 0 tableInSessionId = 0 - var output = DataFrameHtmlData.tableDefinitions() + DataFrameHtmlData( - // copy writerside stlyles - style = """ - body { - font-family: "JetBrains Mono",SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace; - } - - :root { - color: #19191C; - background-color: #fff; - } - - :root[theme="dark"] { - background-color: #19191C; - color: #FFFFFFCC - } - - details details { - margin-left: 20px; - } - - summary { - padding: 6px; - } - """.trimIndent() - ) - - // make copy to avoid concurrent modification exception - val statements = expressionsByStatement.toMap() - when (statements.size) { - 0 -> error("function doesn't have any dataframe expression") - 1 -> { - output += statementOutput(statements.values.single()) - } - else -> { - statements.forEach { (index, expressions) -> - var details: DataFrameHtmlData = statementOutput(expressions) + var output: DataFrameHtmlData + val manualOutput = this.manualOutput + if (manualOutput == null) { + output = DataFrameHtmlData.tableDefinitions() + DataFrameHtmlData( + // copy writerside stlyles + style = """ + body { + font-family: "JetBrains Mono",SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + details details { + margin-left: 20px; + } + + summary { + padding: 6px; + } + """.trimIndent() + ) - details = details.copy( - body = - """ -
- ${expressions.joinToString(".") { it.source } - .also { - if (it.length > 95) TODO("expression is too long ${it.length}. better to split sample in multiple snippets") - } - .escapeHtmlForIFrame()} - ${details.body} -
-
- """.trimIndent() - ) + // make copy to avoid concurrent modification exception + val statements = expressionsByStatement.toMap() + when (statements.size) { + 0 -> error("function doesn't have any dataframe expression") + 1 -> { + output += statementOutput(statements.values.single()) + } + else -> { + statements.forEach { (index, expressions) -> + var details: DataFrameHtmlData = statementOutput(expressions) - output += details + details = details.copy( + body = + """ +
+ ${expressions.joinToString(".") { it.source } + .also { + if (it.length > 95) TODO("expression is too long ${it.length}. better to split sample in multiple snippets") + } + .escapeHtmlForIFrame()} + ${details.body} +
+
+ """.trimIndent() + ) + output += details + } } } + } else { + output = manualOutput } + val input = expressionsByStatement.values.first().first() val name = "${input.containingClassFqName}.${input.containingFunName}" val destination = File("build/dataframes").also { @@ -143,50 +155,67 @@ object PluginCallbackProxy : PluginCallback { ) } + private fun List.joinToSource(): String = + joinToString(".") { it.source } + private fun statementOutput( expressions: List, ): DataFrameHtmlData { var data = DataFrameHtmlData() - if (expressions.size < 2) error("Sample without output or input (i.e. function returns some value)") - for ((i, expression) in expressions.withIndex()) { - when (i) { - 0 -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + val allow = setOf( + "toDataFrame", "peek(dataFrameOf(col), dataFrameOf(col))" + ) + if (expressions.isEmpty()) { + error("No dataframe expressions in sample") + } + if (expressions.size == 1) { + if (allow.any { expressions[0].source.contains(it) }) { + val expression = expressions[0] + data += convertToHTML(expression.df) + } else { + error("${expressions.joinToSource()} Sample without output or input (i.e. function returns some value)") + } + } else { + for ((i, expression) in expressions.withIndex()) { + when (i) { + 0 -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Input ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description - } + """.trimIndent() + ) + data += description + } - expressions.lastIndex -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + expressions.lastIndex -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Output ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description - } + """.trimIndent() + ) + data += description + } - else -> { - val table = convertToHTML(expression.df) - val description = table.copy( - body = """ + else -> { + val table = convertToHTML(expression.df) + val description = table.copy( + body = """
Step $i: ${convertToDescription(expression.df)} ${table.body}
- """.trimIndent() - ) - data += description + """.trimIndent() + ) + data += description + } } } } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt index 13a0943580..8584a85608 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Create.kt @@ -1,7 +1,9 @@ package org.jetbrains.kotlinx.dataframe.samples.api import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.AnyFrame import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.api.DynamicDataFrameBuilder import org.jetbrains.kotlinx.dataframe.api.Infer import org.jetbrains.kotlinx.dataframe.api.ValueProperty import org.jetbrains.kotlinx.dataframe.api.add @@ -431,4 +433,21 @@ class Create : TestBase() { df["scores"].kind shouldBe ColumnKind.Frame df["summary"]["min score"].values() shouldBe listOf(3, 5) } + + @Test + @TransformDataFrameExpressions + fun duplicatedColumns() { + // SampleStart + fun peek(vararg dataframes: AnyFrame): AnyFrame { + val builder = DynamicDataFrameBuilder() + for (df in dataframes) { + df.columns().firstOrNull()?.let { builder.add(it) } + } + return builder.toDataFrame() + } + + val col by columnOf(1, 2, 3) + peek(dataFrameOf(col), dataFrameOf(col)) + // SampleEnd + } } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt new file mode 100644 index 0000000000..b9636e97b0 --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/JoinWith.kt @@ -0,0 +1,660 @@ +package org.jetbrains.kotlinx.dataframe.samples.api + +import io.kotest.matchers.shouldBe +import kotlinx.datetime.LocalDate +import kotlinx.datetime.toJavaLocalDate +import org.jetbrains.kotlinx.dataframe.AnyFrame +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.FormattingDSL +import org.jetbrains.kotlinx.dataframe.api.Infer +import org.jetbrains.kotlinx.dataframe.api.JoinedDataRow +import org.jetbrains.kotlinx.dataframe.api.RGBColor +import org.jetbrains.kotlinx.dataframe.api.and +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.colsOf +import org.jetbrains.kotlinx.dataframe.api.column +import org.jetbrains.kotlinx.dataframe.api.convert +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf +import org.jetbrains.kotlinx.dataframe.api.excludeJoinWith +import org.jetbrains.kotlinx.dataframe.api.filterJoinWith +import org.jetbrains.kotlinx.dataframe.api.fullJoinWith +import org.jetbrains.kotlinx.dataframe.api.getValue +import org.jetbrains.kotlinx.dataframe.api.innerJoin +import org.jetbrains.kotlinx.dataframe.api.innerJoinWith +import org.jetbrains.kotlinx.dataframe.api.joinWith +import org.jetbrains.kotlinx.dataframe.api.leftJoin +import org.jetbrains.kotlinx.dataframe.api.leftJoinWith +import org.jetbrains.kotlinx.dataframe.api.rightJoin +import org.jetbrains.kotlinx.dataframe.api.rightJoinWith +import org.jetbrains.kotlinx.dataframe.api.with +import org.jetbrains.kotlinx.dataframe.explainer.PluginCallbackProxy +import org.jetbrains.kotlinx.dataframe.explainer.TransformDataFrameExpressions +import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData +import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration +import org.jetbrains.kotlinx.dataframe.io.renderValueForHtml +import org.jetbrains.kotlinx.dataframe.io.toHTML +import org.jetbrains.kotlinx.dataframe.jupyter.ChainedCellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer +import org.jetbrains.kotlinx.dataframe.jupyter.RenderedContent +import org.junit.Test +import java.time.format.DateTimeFormatter + +class JoinWith : TestBase() { + + @DataSchema + interface Campaigns { + val name: String + val startDate: LocalDate + val endDate: LocalDate + } + + @DataSchema + interface Visits { + val date: LocalDate + val userId: Int + } + + private val campaigns = dataFrameOf("name", "startDate", "endDate")( + "Winter Sale", LocalDate(2023, 1, 1), LocalDate(2023, 1, 31), + "Spring Sale", LocalDate(2023, 4, 1), LocalDate(2023, 4, 30), + "Summer Sale", LocalDate(2023, 7, 1), LocalDate(2023, 7, 31), + "Autumn Sale", LocalDate(2023, 10, 1), LocalDate(2023, 10, 31), + ).cast() + + private val visits = dataFrameOf("date", "usedId")( + LocalDate(2023, 1, 10), 1, + LocalDate(2023, 1, 20), 2, + LocalDate(2023, 4, 15), 1, + LocalDate(2023, 5, 1), 3, + LocalDate(2023, 7, 10), 2, + ).cast() + + class ColoredValue(val value: T, val backgroundColor: RGBColor, val textColor: RGBColor) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ColoredValue<*> + + return value == other.value + } + + override fun hashCode(): Int { + return value?.hashCode() ?: 0 + } + } + + private val renderer = object : ChainedCellRenderer(DefaultCellRenderer) { + override fun maybeContent(value: Any?, configuration: DisplayConfiguration): RenderedContent? { + return if (value is ColoredValue<*>) { + if (value.value is LocalDate) { + RenderedContent.text(DateTimeFormatter.ofPattern("dd MMMM yyyy").format(value.value.toJavaLocalDate())) + } else { + renderValueForHtml(value.value, configuration.cellContentLimit, configuration.decimalFormat) + } + } else { + null + } + } + + override fun maybeTooltip(value: Any?, configuration: DisplayConfiguration): String? { + return null + } + } + + private fun AnyFrame.uwrapColoredValues(): AnyFrame { + return convert { colsOf?>().rec() }.with(Infer.Type) { it?.value } + } + + private fun T.colored(background: RGBColor, text: RGBColor) = ColoredValue(this, background, text) + private fun T.winter(background: RGBColor = RGBColor(179, 205, 224), text: RGBColor = RGBColor(0, 0, 51)) = ColoredValue(this, background, text) + private fun T.spring(background: RGBColor = RGBColor(204, 235, 197), text: RGBColor = RGBColor(0, 51, 0)) = ColoredValue(this, background, text) + private fun T.summer(background: RGBColor = RGBColor(176, 224, 230), text: RGBColor = RGBColor(25, 25, 112)) = ColoredValue(this, background, text) + private fun T.autumn(background: RGBColor = RGBColor(221, 160, 221), text: RGBColor = RGBColor(85, 26, 139)) = ColoredValue(this, background, text) + + private val coloredCampaigns = dataFrameOf("name", "startDate", "endDate")( + "Winter Sale".winter(), LocalDate(2023, 1, 1).winter(), LocalDate(2023, 1, 31).winter(), + "Spring Sale".spring(), LocalDate(2023, 4, 1).spring(), LocalDate(2023, 4, 30).spring(), + "Summer Sale".summer(), LocalDate(2023, 7, 1).summer(), LocalDate(2023, 7, 31).summer(), + "Autumn Sale".autumn(), LocalDate(2023, 10, 1).autumn(), LocalDate(2023, 10, 31).autumn(), + ) + + private val coloredVisits = dataFrameOf("date", "usedId")( + LocalDate(2023, 1, 10).winter(), 1.winter(), + LocalDate(2023, 1, 20).winter(), 2.winter(), + LocalDate(2023, 4, 15).spring(), 1.spring(), + LocalDate(2023, 5, 1).colored(FormattingDSL.white, FormattingDSL.black), 3.colored(FormattingDSL.white, FormattingDSL.black), + LocalDate(2023, 7, 10).summer(), 2.summer(), + ) + + private fun AnyFrame.toColoredHTML() = toHTML( + getFooter = { null }, + cellRenderer = renderer, + configuration = DisplayConfiguration.DEFAULT.copy( + cellFormatter = { row, col -> + val value = row[col] + if (value is ColoredValue<*>) { + background(value.backgroundColor) and textColor(value.textColor) + } else { + background(white) + } + } + ) + ) + + private val joinExpression: JoinedDataRow.(it: JoinedDataRow) -> Boolean = { + right[{ "date">() }].value in + "startDate">().value.."endDate">().value + } + + private fun DataFrameHtmlData.wrap(title: String): DataFrameHtmlData { + return copy( + body = """ +
+ $title + $body +
+ """.trimIndent() + ) + } + + private fun DataFrameHtmlData.wrap(): DataFrameHtmlData { + return copy( + body = """ +
+ $body +
+ """.trimIndent() + ) + } + + private fun snippetOutput(coloredResult: DataFrame, result: DataFrame) { + coloredCampaigns.uwrapColoredValues() shouldBe campaigns + coloredVisits.uwrapColoredValues() shouldBe visits + coloredResult.uwrapColoredValues() shouldBe result + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus(coloredCampaigns.toColoredHTML().wrap("campaigns")) + .plus(coloredVisits.toColoredHTML().wrap("visits")) + .plus(coloredResult.toColoredHTML().wrap("result")) + .plus( + DataFrameHtmlData( + style = """ + body { + display: flex; + align-items: flex-start; + overflow-x: auto; + font-family: "JetBrains Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 14px; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + .table-container { + margin-right: 20px; + } + + .table-container:not(:last-child) { + margin-right: 20px; + } + + td { + white-space: nowrap; + } + """.trimIndent() + ) + ) + ) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.innerJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.filterJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.leftJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.rightJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.fullJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_accessors() { + val result = run { + // SampleStart + val date by column() + val startDate by column() + val endDate by column() + + campaigns.excludeJoinWith(visits) { + right[date] in startDate()..endDate() + } + // SampleEnd + } + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_strings() { + val result = + // SampleStart + campaigns.innerJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_strings() { + val result = + // SampleStart + campaigns.filterJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_strings() { + val result = + // SampleStart + campaigns.leftJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_strings() { + val result = + // SampleStart + campaigns.rightJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_strings() { + val result = + // SampleStart + campaigns.fullJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_strings() { + val result = + // SampleStart + campaigns.excludeJoinWith(visits) { + right.getValue("date") in "startDate"().."endDate"() + } + // SampleEnd + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun joinWith_properties() { + val result = + // SampleStart + campaigns.innerJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.innerJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun filterJoinWith_properties() { + val result = + // SampleStart + campaigns.filterJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.filterJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun leftJoinWith_properties() { + val result = + // SampleStart + campaigns.leftJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.leftJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun rightJoinWith_properties() { + val result = + // SampleStart + campaigns.rightJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.rightJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun fullJoinWith_properties() { + val result = + // SampleStart + campaigns.fullJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.fullJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun excludeJoinWith_properties() { + val result = + // SampleStart + campaigns.excludeJoinWith(visits) { + right.date in startDate..endDate + } + // SampleEnd + val coloredResult = coloredCampaigns.excludeJoinWith(coloredVisits, joinExpression = joinExpression) + snippetOutput(coloredResult, result) + } + + @TransformDataFrameExpressions + @Test + fun crossProduct() { + val result = + // SampleStart + campaigns.joinWith(visits) { true } + // SampleEnd + val coloredResult = coloredCampaigns.joinWith(coloredVisits) { true } + snippetOutput(coloredResult, result) + } + + val df1 = dataFrameOf("index", "age", "name")( + 1.spring(), 15.spring(), "BOB".spring(), + 2.summer(), 19.summer(), "ALICE".summer(), + 3.autumn(), 20.autumn(), "CHARLIE".autumn() + ) + + val df2 = dataFrameOf("index", "age", "name")( + 1.spring(), 15.spring(), "Bob".spring(), + 2.summer(), 19.summer(), "Alice".summer(), + 4.winter(), 21.winter(), "John".winter() + ) + + @TransformDataFrameExpressions + @Test + fun compareInnerColumns() { + // SampleStart + df1.innerJoin(df2, "index", "age") + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus(df1.innerJoin(df2, "index", "age").toColoredHTML().wrap("result")) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareInnerValues() { + // SampleStart + df1.innerJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.innerJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareLeft() { + // SampleStart + df1.leftJoin(df2, "index", "age") + df1.leftJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.leftJoin(df2, "index", "age").toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(DataFrameHtmlData(body = "

")) + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap()) + .plus(df2.toColoredHTML().wrap()) + .plus( + df1.leftJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap() + ) + .wrapRow() + ) + .plus(other) + ) + } + + @TransformDataFrameExpressions + @Test + fun compareRight() { + // SampleStart + df1.rightJoin(df2, "index", "age") + df1.rightJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + // SampleEnd + + PluginCallbackProxy.overrideHtmlOutput( + manualOutput = DataFrameHtmlData.tableDefinitions() + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap("df1")) + .plus(df2.toColoredHTML().wrap("df2")) + .plus( + df1.rightJoin(df2, "index", "age").toColoredHTML().wrap("result") + ) + .wrapRow() + ) + .plus(DataFrameHtmlData(body = "

")) + .plus( + DataFrameHtmlData() + .plus(df1.toColoredHTML().wrap()) + .plus(df2.toColoredHTML().wrap()) + .plus( + df1.rightJoinWith(df2) { it["index"] == right["index"] && it["age"] == right["age"] } + .toColoredHTML().wrap() + ) + .wrapRow() + ) + .plus(other) + ) + } + + private fun DataFrameHtmlData.wrapRow(): DataFrameHtmlData { + return copy( + body = """ +
+ $body +
+ """.trimIndent() + ) + } + + private val other = DataFrameHtmlData( + style = """ + body { + font-family: "JetBrains Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 14px; + } + + :root { + color: #19191C; + background-color: #fff; + } + + :root[theme="dark"] { + background-color: #19191C; + color: #FFFFFFCC + } + + .table-row { + display: flex; + align-items: flex-start; + overflow-x: auto; + } + + .table-container:not(:last-child) { + margin-right: 20px; + } + + td { + white-space: nowrap; + } + """.trimIndent() + ) +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt index 1a8bf5c505..82d0a081b6 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Modify.kt @@ -469,7 +469,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitInplace_properties() { // SampleStart - df.split { name.firstName }.by { it.chars().toList() }.inplace() + df.split { name.firstName }.by { it.asIterable() }.inplace() // SampleEnd } @@ -480,7 +480,7 @@ class Modify : TestBase() { val name by columnGroup() val firstName by name.column() - df.split { firstName }.by { it.chars().toList() }.inplace() + df.split { firstName }.by { it.asIterable() }.inplace() // SampleEnd } @@ -488,7 +488,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitInplace_strings() { // SampleStart - df.split { "name"["firstName"]() }.by { it.chars().toList() }.inplace() + df.split { "name"["firstName"]() }.by { it.asIterable() }.inplace() // SampleEnd } @@ -496,9 +496,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun split_properties() { // SampleStart - df.split { name }.by { it.values() }.into("nameParts") - - df.split { name.lastName }.by(" ").default("").inward { "word$it" } + df.split { name.lastName }.by { it.asIterable() }.into("char1", "char2") // SampleEnd } @@ -509,9 +507,7 @@ class Modify : TestBase() { val name by columnGroup() val lastName by name.column() - df.split { name }.by { it.values() }.into("nameParts") - - df.split { lastName }.by(" ").default("").inward { "word$it" } + df.split { lastName }.by { it.asIterable() }.into("char1", "char2") // SampleEnd } @@ -519,18 +515,61 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun split_strings() { // SampleStart - df.split { name }.by { it.values() }.into("nameParts") + df.split { "name"["lastName"]() }.by { it.asIterable() }.into("char1", "char2") + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_properties() { + // SampleStart + df.split { name.lastName } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_accessors() { + // SampleStart + val name by columnGroup() + val lastName by name.column() - df.split { "name"["lastName"] }.by(" ").default("").inward { "word$it" } + df.split { lastName } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } + // SampleEnd + } + + @Test + @TransformDataFrameExpressions + fun split1_strings() { + // SampleStart + df.split { "name"["lastName"]() } + .by { it.asIterable() }.default(' ') + .inward { "char$it" } // SampleEnd } @Test @TransformDataFrameExpressions fun splitRegex() { - val merged = df.merge { name.lastName and name.firstName }.by { it[0] + " (" + it[1] + ")" }.into("name") - val name by column() // SampleStart + val merged = df.merge { name.lastName and name.firstName } + .by { it[0] + " (" + it[1] + ")" } + .into("name") + // SampleEnd + } + + private val merged = df.merge { name.lastName and name.firstName }.by { it[0] + " (" + it[1] + ")" }.into("name") + + @Test + @TransformDataFrameExpressions + fun splitRegex1() { + // SampleStart + val name by column() + merged.split { name } .match("""(.*) \((.*)\)""") .inward("firstName", "lastName") @@ -562,7 +601,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitIntoRows_properties() { // SampleStart - df.split { name.firstName }.by { it.chars().toList() }.intoRows() + df.split { name.firstName }.by { it.asIterable() }.intoRows() df.split { name }.by { it.values() }.intoRows() // SampleEnd @@ -575,7 +614,7 @@ class Modify : TestBase() { val name by columnGroup() val firstName by name.column() - df.split { firstName }.by { it.chars().toList() }.intoRows() + df.split { firstName }.by { it.asIterable() }.intoRows() df.split { name }.by { it.values() }.intoRows() // SampleEnd @@ -585,7 +624,7 @@ class Modify : TestBase() { @TransformDataFrameExpressions fun splitIntoRows_strings() { // SampleStart - df.split { "name"["firstName"]() }.by { it.chars().toList() }.intoRows() + df.split { "name"["firstName"]() }.by { it.asIterable() }.intoRows() df.split { colGroup("name") }.by { it.values() }.intoRows() // SampleEnd diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt new file mode 100644 index 0000000000..8e0f2e790c --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/BaseJoinTest.kt @@ -0,0 +1,26 @@ +package org.jetbrains.kotlinx.dataframe.testSets.person + +import org.jetbrains.kotlinx.dataframe.DataFrame +import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.cast +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf + +open class BaseJoinTest : BaseTest() { + val df2 = dataFrameOf("name", "origin", "grade", "age")( + "Alice", "London", 3, "young", + "Alice", "London", 5, "old", + "Bob", "Tokyo", 4, "young", + "Bob", "Paris", 5, "old", + "Charlie", "Moscow", 1, "young", + "Charlie", "Moscow", 2, "old", + "Bob", "Paris", 4, null + ) + val typed2: DataFrame = df2.cast() + + @DataSchema + interface Person2 { + val name: String + val origin: String? + val grade: Int + } +} diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt index 47fa300d4c..68962157d8 100644 --- a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinTests.kt @@ -1,49 +1,26 @@ package org.jetbrains.kotlinx.dataframe.testSets.person import io.kotest.matchers.shouldBe -import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.annotations.DataSchema +import org.jetbrains.kotlinx.dataframe.api.JoinType import org.jetbrains.kotlinx.dataframe.api.addId import org.jetbrains.kotlinx.dataframe.api.all import org.jetbrains.kotlinx.dataframe.api.append -import org.jetbrains.kotlinx.dataframe.api.cast import org.jetbrains.kotlinx.dataframe.api.column import org.jetbrains.kotlinx.dataframe.api.count -import org.jetbrains.kotlinx.dataframe.api.dataFrameOf import org.jetbrains.kotlinx.dataframe.api.distinct import org.jetbrains.kotlinx.dataframe.api.excludeJoin import org.jetbrains.kotlinx.dataframe.api.filter import org.jetbrains.kotlinx.dataframe.api.filterJoin import org.jetbrains.kotlinx.dataframe.api.fullJoin import org.jetbrains.kotlinx.dataframe.api.innerJoin +import org.jetbrains.kotlinx.dataframe.api.join import org.jetbrains.kotlinx.dataframe.api.leftJoin import org.jetbrains.kotlinx.dataframe.api.remove import org.jetbrains.kotlinx.dataframe.api.rightJoin import org.jetbrains.kotlinx.dataframe.api.select import org.junit.Test -class JoinTests : BaseTest() { - - val df2 = dataFrameOf("name", "origin", "grade", "age")( - "Alice", "London", 3, "young", - "Alice", "London", 5, "old", - "Bob", "Tokyo", 4, "young", - "Bob", "Paris", 5, "old", - "Charlie", "Moscow", 1, "young", - "Charlie", "Moscow", 2, "old", - "Bob", "Paris", 4, null - ) - -// Generated Code - - @DataSchema - interface Person2 { - val name: String - val origin: String? - val grade: Int - } - - val typed2: DataFrame = df2.cast() +class JoinTests : BaseJoinTest() { @Test fun `inner join`() { @@ -118,4 +95,14 @@ class JoinTests : BaseTest() { res shouldBe expected } + + @Test + fun `test overloads contract`() { + typed.innerJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Inner) { name and it.city.match(right.origin) } + typed.leftJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Left) { name and it.city.match(right.origin) } + typed.rightJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Right) { name and it.city.match(right.origin) } + typed.fullJoin(typed2) { name and it.city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Full) { name and it.city.match(right.origin) } + typed.excludeJoin(typed2) { city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Exclude) { city.match(right.origin) } + typed.filterJoin(typed2) { city.match(right.origin) } shouldBe typed.join(typed2, JoinType.Filter) { city.match(right.origin) } + } } diff --git a/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt new file mode 100644 index 0000000000..39a5e9886c --- /dev/null +++ b/core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/JoinWithTests.kt @@ -0,0 +1,130 @@ +package org.jetbrains.kotlinx.dataframe.testSets.person + +import io.kotest.matchers.shouldBe +import org.jetbrains.kotlinx.dataframe.api.JoinType +import org.jetbrains.kotlinx.dataframe.api.addId +import org.jetbrains.kotlinx.dataframe.api.all +import org.jetbrains.kotlinx.dataframe.api.append +import org.jetbrains.kotlinx.dataframe.api.column +import org.jetbrains.kotlinx.dataframe.api.count +import org.jetbrains.kotlinx.dataframe.api.dataFrameOf +import org.jetbrains.kotlinx.dataframe.api.distinct +import org.jetbrains.kotlinx.dataframe.api.excludeJoin +import org.jetbrains.kotlinx.dataframe.api.excludeJoinWith +import org.jetbrains.kotlinx.dataframe.api.filter +import org.jetbrains.kotlinx.dataframe.api.filterJoinWith +import org.jetbrains.kotlinx.dataframe.api.fullJoinWith +import org.jetbrains.kotlinx.dataframe.api.innerJoinWith +import org.jetbrains.kotlinx.dataframe.api.joinWith +import org.jetbrains.kotlinx.dataframe.api.leftJoinWith +import org.jetbrains.kotlinx.dataframe.api.print +import org.jetbrains.kotlinx.dataframe.api.remove +import org.jetbrains.kotlinx.dataframe.api.rightJoinWith +import org.jetbrains.kotlinx.dataframe.api.select +import org.junit.Test + +class JoinWithTests : BaseJoinTest() { + + @Test + fun `inner join`() { + val res = typed.joinWith(typed2) { + name == right.name && city == right.origin + } + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 7 + res["age1"].hasNulls() shouldBe false + res.count { name == "Charlie" && city == "Moscow" } shouldBe 4 + res.select { city and name }.distinct().rowsCount() shouldBe 3 + res[Person2::grade].hasNulls() shouldBe false + } + + @Test + fun `left join`() { + val res = typed.leftJoinWith(typed2) { name == right.name && city == right.origin } + + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 10 + res["age1"].hasNulls() shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 6 + res.count { it["grade"] == null } shouldBe 3 + res.age.hasNulls() shouldBe false + } + + @Test + fun `right join`() { + val res = typed.rightJoinWith(typed2) { + name == right.name && city == right.origin + } + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 9 + res["age1"].hasNulls() shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 4 + res[Person2::grade].hasNulls() shouldBe false + res.age.hasNulls() shouldBe true + val newEntries = res.filter { it["age"] == null } + newEntries.rowsCount() shouldBe 2 + newEntries.all { it["name1"] == "Bob" && it["origin"] == "Paris" && weight == null } shouldBe true + } + + @Test + fun `outer join`() { + val res = typed.fullJoinWith(typed2) { name == right.name && city == right.origin } + println(res) + res.columnsCount() shouldBe 8 + res.rowsCount() shouldBe 12 + res.columns().all { it.hasNulls() } shouldBe true + res.select { city and name }.distinct().rowsCount() shouldBe 7 + val distinct = res.select { name and age and city and weight }.distinct() + val expected = typed.append(null, null, null, null) + distinct shouldBe expected + } + + @Test + fun `filter join`() { + val res = typed.filterJoinWith(typed2) { city == right.origin } + val expected = typed.innerJoinWith(typed2.select { origin }) { city == right.origin }.remove("origin") + res shouldBe expected + } + + @Test + fun `filter not join`() { + val res = typed.excludeJoinWith(typed2) { city == right.origin } + res.rowsCount() shouldBe 3 + res.city.toSet() shouldBe typed.city.toSet() - typed2.origin.toSet() + + val indexColumn = column("__index__") + val withIndex = typed.addId(indexColumn) + val joined = withIndex.filterJoinWith(typed2) { city == right.origin } + val joinedIndices = joined[indexColumn].toSet() + val expected = withIndex.filter { !joinedIndices.contains(it[indexColumn]) }.remove(indexColumn) + + res shouldBe expected + } + + @Test + fun rightJoin() { + val df = dataFrameOf("a", "b")( + 1, "a", + 2, "b", + 3, "c", + ) + + val df1 = dataFrameOf("a", "c")( + 5, "V", + 1, "I", + 2, "II", + 3, "III", + ) + df.append(4, "e").excludeJoin(df1).print() + } + + @Test + fun `test overloads contract`() { + typed.innerJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Inner) { name == right.name && city == right.origin } + typed.leftJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Left) { name == right.name && city == right.origin } + typed.rightJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Right) { name == right.name && city == right.origin } + typed.fullJoinWith(typed2) { name == right.name && city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Full) { name == right.name && city == right.origin } + typed.excludeJoinWith(typed2) { city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Exclude) { city == right.origin } + typed.filterJoinWith(typed2) { city == right.origin } shouldBe typed.joinWith(typed2, JoinType.Filter) { city == right.origin } + } +} diff --git a/docs/StardustDocs/c.list b/docs/StardustDocs/c.list index e97afd856f..8339a6eb7d 100644 --- a/docs/StardustDocs/c.list +++ b/docs/StardustDocs/c.list @@ -1,6 +1,6 @@ + SYSTEM "https://resources.jetbrains.com/writerside/1.0/categories.dtd"> diff --git a/docs/StardustDocs/cfg/build-script.xml b/docs/StardustDocs/cfg/build-script.xml index 0118bbec2c..10cda55263 100644 --- a/docs/StardustDocs/cfg/build-script.xml +++ b/docs/StardustDocs/cfg/build-script.xml @@ -1,5 +1,5 @@ + SYSTEM "https://resources.jetbrains.com/writerside/1.0/build-script.dtd"> diff --git a/docs/StardustDocs/cfg/buildprofiles.xml b/docs/StardustDocs/cfg/buildprofiles.xml index a0ccbf802f..41f924fb36 100644 --- a/docs/StardustDocs/cfg/buildprofiles.xml +++ b/docs/StardustDocs/cfg/buildprofiles.xml @@ -1,6 +1,6 @@ - https://kotlin.github.io/dataframe/ diff --git a/docs/StardustDocs/d.tree b/docs/StardustDocs/d.tree index 1f81491eff..b1d1c72798 100644 --- a/docs/StardustDocs/d.tree +++ b/docs/StardustDocs/d.tree @@ -1,6 +1,6 @@ + SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd"> + diff --git a/docs/StardustDocs/project.ihp b/docs/StardustDocs/project.ihp index a3c7fd3331..7a6a0bbb98 100644 --- a/docs/StardustDocs/project.ihp +++ b/docs/StardustDocs/project.ihp @@ -1,5 +1,5 @@ - + diff --git a/docs/StardustDocs/r.list b/docs/StardustDocs/r.list index fc3c4bf694..bf01304e53 100644 --- a/docs/StardustDocs/r.list +++ b/docs/StardustDocs/r.list @@ -1,5 +1,5 @@ - + diff --git a/docs/StardustDocs/redirection-rules.xml b/docs/StardustDocs/redirection-rules.xml index 3725ab4d21..654fc8703f 100644 --- a/docs/StardustDocs/redirection-rules.xml +++ b/docs/StardustDocs/redirection-rules.xml @@ -1,5 +1,5 @@ - + Created after removal of "About Dataframe" from Dataframe diff --git a/docs/StardustDocs/sitemap.xml b/docs/StardustDocs/sitemap.xml new file mode 100644 index 0000000000..95bde29c87 --- /dev/null +++ b/docs/StardustDocs/sitemap.xml @@ -0,0 +1,717 @@ + + + + + + + https://kotlin.github.io/dataframe/ + 2023-06-26T12:33:14+00:00 + 1.00 + + + https://kotlin.github.io/dataframe/overview.html + 2023-06-26T12:33:14+00:00 + 0.80 + + + https://kotlin.github.io/dataframe/operations.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/hierarchical.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/collectionsinterop.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/extensionpropertiesapi.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/dataframe.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/schemas.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/gettingstartedgradleadvanced.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/apilevels.html + 2023-06-26T12:33:14+00:00 + 0.64 + + + https://kotlin.github.io/dataframe/filter.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/select.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columnselectors.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/move.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/group.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/split.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/merge.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/gather.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/groupby.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/rename.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/datarow.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/update.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/convert.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/replace.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/pivot.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/summarystatistics.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/add.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/append.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columns.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columnnames.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columntypes.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columnscount.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/concat.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/corr.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/count.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/countdistinct.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/cumsum.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/describe.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/distinct.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/drop.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/slicerows.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/duplicate.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/explode.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/fill.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/first.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/flatten.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/iterate.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/format.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/getcolumn.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/datacolumn.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/head.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/implode.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/infertype.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/insert.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/join.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/last.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/map.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/minmax.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/mean.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/median.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/parse.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/remove.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/reorder.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/reverse.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/rows.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/rowscount.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schema.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/shuffle.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/single.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/sortby.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/std.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/sum.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/tolist.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/tomap.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/unfold.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/ungroup.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/values.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/valuecounts.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/xs.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/nanandna.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/create.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/read.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/createdataframe.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/modify.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasgradle.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/gettingstartedjupyternotebook.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/kpropertiesapi.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/info.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/access.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/multipledataframes.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/types.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasjupyter.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasinheritance.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemascustom.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasexternaljupyter.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasimportopenapigradle.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/schemasimportopenapijupyter.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/gettingstarteddatalore.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/stringapi.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/columnaccessorsapi.html + 2023-06-26T12:33:14+00:00 + 0.51 + + + https://kotlin.github.io/dataframe/filterrows.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/indexing.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/datetimecolumns.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/moverename.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/groupungroupflatten.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/splitmerge.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/addremove.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/appendduplicate.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/groupbyconcat.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/updateconvert.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/explodeimplode.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/pivotgather.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/convertto.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/getcolumns.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/statisticalrelationship.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/rowstats.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/cumulativestatistics.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/reorderrows.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/adjustschema.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/insertreplace.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/getrow.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/tohtml.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/jupyterrendering.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/getvalues.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/createcolumn.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/concatdf.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/columnoperations.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/minby.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/rendering.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/io.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/write.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/gradlereference.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/gettingstartedgradle.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/adddf.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/cast.html + 2023-06-26T12:33:14+00:00 + 0.41 + + + https://kotlin.github.io/dataframe/stringcolumns.html + 2023-06-26T12:33:14+00:00 + 0.33 + + + https://kotlin.github.io/dataframe/maxby.html + 2023-06-26T12:33:14+00:00 + 0.33 + + + https://kotlin.github.io/dataframe/columnarithmetics.html + 2023-06-26T12:33:14+00:00 + 0.33 + + + https://kotlin.github.io/dataframe/gettingstarted.html + 2023-06-26T12:33:14+00:00 + 0.33 + + + https://kotlin.github.io/dataframe/specialcolumntypes.html + 2023-06-26T12:33:14+00:00 + 0.26 + + + https://kotlin.github.io/dataframe/columnstatistics.html + 2023-06-26T12:33:14+00:00 + 0.26 + + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Access.byRow.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Access.byRow.html index 9894ddc3ed..8b3efc9f49 100644 --- a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Access.byRow.html +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Access.byRow.html @@ -197,9 +197,9 @@ -
- df.df[0].name - +
+ df.df[0].name +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -218,11 +218,11 @@

-
-
-
- df.df[3, 5, 6].select { name and age } - +
+
+
+ df.df[3, 5, 6].select { name and age } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -241,11 +241,11 @@

-
-
-
- df.df[3..5] - +
+
+
+ df.df[3..5] +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -258,8 +258,8 @@

-
-
+
+
+ \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.createDataFrameFromMap.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.createDataFrameFromMap.html new file mode 100644 index 0000000000..f0f27e06d8 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.createDataFrameFromMap.html @@ -0,0 +1,476 @@ + + + + + + + + +
+ +

+ + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.duplicatedColumns.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.duplicatedColumns.html new file mode 100644 index 0000000000..958449a85d --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Create.duplicatedColumns.html @@ -0,0 +1,476 @@ + + + + + + + + +
+ +

+ + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html index a598a5bf8e..ff9b8671a6 100644 --- a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.DataRowApi.conditions.html @@ -197,9 +197,9 @@ -
- df.filter { index() % 5 == 0 } - +
+ df.filter { index() % 5 == 0 } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -212,11 +212,11 @@

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

-
-
-
- df.update { weight }.where { index() &gt; 4 && city != "Paris" }.with { 50 } - +
+
+
+ df.update { weight }.where { index() &gt; 4 && city != "Paris" }.with { 50 } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -258,8 +258,8 @@

-
-
+
+
+ \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareInnerValues.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareInnerValues.html new file mode 100644 index 0000000000..a400a558b7 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareInnerValues.html @@ -0,0 +1,519 @@ + + + + + + +
+ +
+ df1 +
+ +
+
+ df2 +
+ +
+
+ result +
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareLeft.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareLeft.html new file mode 100644 index 0000000000..8e5675eb0b --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareLeft.html @@ -0,0 +1,570 @@ + + + + + + +
+ +
+ df1 +
+ +
+
+ df2 +
+ +
+
+ result +
+ +
+
+

+
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareRight.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareRight.html new file mode 100644 index 0000000000..2da46d6ef4 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.compareRight.html @@ -0,0 +1,570 @@ + + + + + + +
+ +
+ df1 +
+ +
+
+ df2 +
+ +
+
+ result +
+ +
+
+

+
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.crossProduct.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.crossProduct.html new file mode 100644 index 0000000000..704cb0cc1a --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.crossProduct.html @@ -0,0 +1,513 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.excludeJoinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.excludeJoinWith.html new file mode 100644 index 0000000000..f1e6ba8965 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.excludeJoinWith.html @@ -0,0 +1,511 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.filterJoinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.filterJoinWith.html new file mode 100644 index 0000000000..a5e2aea1f3 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.filterJoinWith.html @@ -0,0 +1,511 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.fullJoinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.fullJoinWith.html new file mode 100644 index 0000000000..9b58956dee --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.fullJoinWith.html @@ -0,0 +1,513 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.joinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.joinWith.html new file mode 100644 index 0000000000..738dbb9801 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.joinWith.html @@ -0,0 +1,513 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.leftJoinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.leftJoinWith.html new file mode 100644 index 0000000000..73f6376a12 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.leftJoinWith.html @@ -0,0 +1,513 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.rightJoinWith.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.rightJoinWith.html new file mode 100644 index 0000000000..6ecdd3b9b2 --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.JoinWith.rightJoinWith.html @@ -0,0 +1,513 @@ + + + + + + +
+ campaigns +
+ +
+
+ visits +
+ +
+
+ result +
+ +
+ + + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.addDataFrames.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.addDataFrames.html index b6f57efe54..1de3e1a11e 100644 --- a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.addDataFrames.html +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.addDataFrames.html @@ -193,9 +193,9 @@ -
- df.select { name named "name2" } - +
+ df.select { name named "name2" } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -208,11 +208,11 @@

-
-
-
- df.select { age named "age2" } - +
+
+
+ df.select { age named "age2" } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -225,11 +225,11 @@

-
-
-
- df.add(df1, df2) - +
+
+
+ df.add(df1, df2) +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -242,8 +242,8 @@

-
-
+
+
\ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.split1.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.split1.html new file mode 100644 index 0000000000..169b6e326e --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.split1.html @@ -0,0 +1,591 @@ + + + + + + + + +
+ Input DataFrame: rowsCount = 7, columnsCount = 5 +
+ +

+
+
+ Step 1: Split +
+ +

+
+
+ Step 2: SplitWithTransform +
+ +

+
+
+ Step 3: SplitWithTransform +
+ +

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

+
+ + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitInplace.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitInplace.html index cb30c9ae00..743a4e3323 100644 --- a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitInplace.html +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitInplace.html @@ -519,15 +519,15 @@ /**/ - -call_DataFrame(function() { DataFrame.renderTable(4) }); - - -/**/ - -call_DataFrame(function() { DataFrame.renderTable(5) }); - - -/**/ - -call_DataFrame(function() { DataFrame.renderTable(6) }); - - -/**/ - -call_DataFrame(function() { DataFrame.renderTable(7) }); - - \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitRegex1.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitRegex1.html new file mode 100644 index 0000000000..9a7d16348d --- /dev/null +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.splitRegex1.html @@ -0,0 +1,545 @@ + + + + + + + + +
+ Input DataFrame: rowsCount = 7, columnsCount = 5 +
+ +

+
+
+ Step 1: Split +
+ +

+
+
+ Step 2: SplitWithTransform +
+ +

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

+
+ + + \ No newline at end of file diff --git a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html index df38de7ad2..445dbebbef 100644 --- a/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html +++ b/docs/StardustDocs/snippets/org.jetbrains.kotlinx.dataframe.samples.api.Modify.update.html @@ -210,9 +210,9 @@ -
- df.update { age }.with { it * 2 } - +
+ df.update { age }.with { it * 2 } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -231,11 +231,11 @@

-
-
-
- df.update { colsOf&lt;String&gt;().recursively() }.with { it.uppercase() } - +
+
+
+ df.update { colsOf&lt;String&gt;().recursively() }.with { it.uppercase() } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -254,11 +254,11 @@

-
-
-
- df.update { weight }.at(1..4).notNull { it / 2 } - +
+
+
+ df.update { weight }.at(1..4).notNull { it / 2 } +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -283,11 +283,11 @@

-
-
-
- df.update { name.lastName and age }.at(1, 3, 4).withNull() - +
+
+
+ df.update { name.lastName and age }.at(1, 3, 4).withNull() +
Input DataFrame: rowsCount = 7, columnsCount = 5
@@ -312,8 +312,8 @@

-
-
+
+