282 lines
9.1 KiB
Markdown
Vendored
282 lines
9.1 KiB
Markdown
Vendored
[//]: # (title: groupBy)
|
|
|
|
<!---IMPORT org.jetbrains.kotlinx.dataframe.samples.api.Analyze-->
|
|
<!---IMPORT org.jetbrains.kotlinx.dataframe.samples.api.Modify-->
|
|
|
|
Splits the rows of [`DataFrame`](DataFrame.md) into groups using one or several columns as grouping keys.
|
|
|
|
```text
|
|
groupBy(moveToTop = true) { columns }
|
|
[ transformations ]
|
|
reducer | aggregator | pivot
|
|
|
|
transformations = [ .sortByCount() | .sortByCountAsc() | .sortBy { columns } | .sortByDesc { columns } ]
|
|
[ .updateGroups { frameExpression } ]
|
|
[ .add(column) { rowExpression } ]
|
|
|
|
reducer = .minBy { column } | .maxBy { column } | .first [ { rowCondition } ] | .last [ { rowCondition } ]
|
|
.concat() | .into([column]) [{ rowExpression }] | .values { valueColumns }
|
|
|
|
aggregator = .count() | .concat() | .into([column]) [{ rowExpression }] | .values { valueColumns } | .aggregate { aggregations } | .<stat> [ { columns } ]
|
|
|
|
pivot = .pivot { columns }
|
|
[ .default(defaultValue) ]
|
|
pivotReducer | pivotAggregator
|
|
```
|
|
|
|
See [column selectors](ColumnSelectors.md) for how to select the columns for this operation,
|
|
[groupBy transformations](#transformation), [groupBy aggregations](#aggregation), and [pivot+groupBy](pivot.md#pivot-groupby).
|
|
|
|
<!---FUN groupBy-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { name }
|
|
df.groupBy { city and name.lastName }
|
|
df.groupBy { age / 10 named "ageDecade" }
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy("name")
|
|
df.groupBy { "city" and "name"["lastName"] }
|
|
df.groupBy { "age"<Int>() / 10 named "ageDecade" }
|
|
```
|
|
|
|
</tab></tabs>
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupBy.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
Grouping columns can be created inplace:
|
|
|
|
<!---FUN groupByExpr-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { expr { name.firstName.length + name.lastName.length } named "nameLength" }
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy { expr { "name"["firstName"]<String>().length + "name"["lastName"]<String>().length } named "nameLength" }
|
|
```
|
|
|
|
</tab></tabs>
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByExpr.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
With optional `moveToTop` parameter you can choose whether to make a selected *nested column* a top-level column:
|
|
|
|
<!---FUN groupByMoveToTop-->
|
|
|
|
```kotlin
|
|
df.groupBy(moveToTop = true) { name.lastName }
|
|
```
|
|
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByMoveToTop.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
or to keep it inside a `ColumnGroup`:
|
|
|
|
<!---FUN groupByMoveToTopFalse-->
|
|
|
|
```kotlin
|
|
df.groupBy(moveToTop = false) { name.lastName }
|
|
```
|
|
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByMoveToTopFalse.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
Returns `GroupBy` object.
|
|
|
|
## Transformation
|
|
|
|
`GroupBy DataFrame` is a [`DataFrame`](DataFrame.md) with one chosen [`FrameColumn`](DataColumn.md#framecolumn) containing data groups.
|
|
|
|
It supports the following operations:
|
|
* [`add`](add.md)
|
|
* [`sortBy`](sortBy.md)
|
|
* [`map`](map.md)
|
|
* [`pivot`](pivot.md#pivot-groupby)
|
|
* [`concat`](concat.md)
|
|
|
|
Any [`DataFrame`](DataFrame.md) with `FrameColumn` can be reinterpreted as `GroupBy DataFrame`:
|
|
|
|
<!---FUN dataFrameToGroupBy-->
|
|
|
|
```kotlin
|
|
val key by columnOf(1, 2) // create int column with name "key"
|
|
val data by columnOf(df[0..3], df[4..6]) // create frame column with name "data"
|
|
val df = dataFrameOf(key, data) // create dataframe with two columns
|
|
|
|
df.asGroupBy { data } // convert dataframe to GroupBy by interpreting 'data' column as groups
|
|
```
|
|
|
|
<!---END-->
|
|
|
|
And any [`GroupBy DataFrame`](groupBy.md#transformation) can be reinterpreted as [`DataFrame`](DataFrame.md) with `FrameColumn`:
|
|
|
|
<!---FUN groupByToFrame-->
|
|
|
|
```kotlin
|
|
df.groupBy { city }.toDataFrame()
|
|
```
|
|
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByToFrame.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
Use [`concat`](concat.md) to union all data groups of `GroupBy` into original [`DataFrame`](DataFrame.md) preserving new order of rows produced by grouping:
|
|
|
|
<!---FUN concatGroupBy-->
|
|
|
|
```kotlin
|
|
df.groupBy { name }.concat()
|
|
```
|
|
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Modify.concatGroupBy.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
<!---TODO ## Reducing--->
|
|
|
|
To compute one or several [statistics](summaryStatistics.md) per every group of `GroupBy` use `aggregate` function.
|
|
Its body will be executed for every data group and has a receiver of type [`DataFrame`](DataFrame.md) that represents current data group being aggregated.
|
|
To add a new column to the resulting [`DataFrame`](DataFrame.md), pass the name of new column to infix function `into`:
|
|
|
|
<!---FUN groupByAggregations-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { city }.aggregate {
|
|
count() into "total"
|
|
count { age > 18 } into "adults"
|
|
median { age } into "median age"
|
|
min { age } into "min age"
|
|
maxBy { age }.name into "oldest"
|
|
}
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy("city").aggregate {
|
|
count() into "total"
|
|
count { "age"<Int>() > 18 } into "adults"
|
|
median("age") into "median age"
|
|
min("age") into "min age"
|
|
maxBy("age")["name"] into "oldest"
|
|
}
|
|
// or
|
|
df.groupBy("city").aggregate {
|
|
count() into "total"
|
|
count { "age"<Int>() > 18 } into "adults"
|
|
"age"<Int>().median() into "median age"
|
|
"age"<Int>().min() into "min age"
|
|
maxBy("age")["name"] into "oldest"
|
|
}
|
|
```
|
|
|
|
</tab></tabs>
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByAggregations.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
If only one aggregation function is used, column name can be omitted:
|
|
|
|
<!---FUN groupByAggregateWithoutInto-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { city }.aggregate { maxBy { age }.name }
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy("city").aggregate { maxBy("age")["name"] }
|
|
```
|
|
|
|
</tab></tabs>
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByAggregateWithoutInto.html" width="100%"/>
|
|
<!---END-->
|
|
|
|
Most common aggregation functions can be computed directly at [`GroupBy DataFrame`](groupBy.md#transformation) :
|
|
|
|
<!---FUN groupByDirectAggregations-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { city }.max() // max for every column with mutually comparable values
|
|
df.groupBy { city }.mean() // mean for every numeric column
|
|
df.groupBy { city }.max { age } // max age into column "age"
|
|
df.groupBy { city }.sum("total weight") { weight } // sum of weights into column "total weight"
|
|
df.groupBy { city }.count() // number of rows into column "count"
|
|
df.groupBy { city }
|
|
.max { name.firstName.map { it.length } and name.lastName.map { it.length } } // maximum length of firstName or lastName into column "max"
|
|
df.groupBy { city }
|
|
.medianFor { age and weight } // median age into column "age", median weight into column "weight"
|
|
df.groupBy { city }
|
|
.minFor { (age into "min age") and (weight into "min weight") } // min age into column "min age", min weight into column "min weight"
|
|
df.groupBy { city }.meanOf("mean ratio") { weight?.div(age) } // mean of weight/age into column "mean ratio"
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy("city").max() // max for every column with mutually comparable values
|
|
df.groupBy("city").mean() // mean for every numeric column
|
|
df.groupBy("city").max("age") // max age into column "age"
|
|
df.groupBy("city").sum("weight", name = "total weight") // sum of weights into column "total weight"
|
|
df.groupBy("city").count() // number of rows into column "count"
|
|
df.groupBy("city").max {
|
|
"name"["firstName"]<String>().map { it.length } and "name"["lastName"]<String>().map { it.length }
|
|
} // maximum length of firstName or lastName into column "max"
|
|
df.groupBy("city")
|
|
.medianFor("age", "weight") // median age into column "age", median weight into column "weight"
|
|
df.groupBy("city")
|
|
.minFor { ("age"<Int>() into "min age") and ("weight"<Int?>() into "min weight") } // min age into column "min age", min weight into column "min weight"
|
|
df.groupBy("city").meanOf("mean ratio") {
|
|
"weight"<Int?>()?.div("age"<Int>())
|
|
} // mean of weight/age into column "mean ratio"
|
|
```
|
|
|
|
</tab></tabs>
|
|
<!---END-->
|
|
|
|
To get all column values for every group without aggregation use `values` function:
|
|
* for [ValueColumn](DataColumn.md#valuecolumn) of type `T` it will gather group values into lists of type `List<T>`
|
|
* for [ColumnGroup](DataColumn.md#columngroup) it will gather group values into [`DataFrame`](DataFrame.md) and convert [ColumnGroup](DataColumn.md#columngroup) into [FrameColumn](DataColumn.md#framecolumn)
|
|
|
|
<!---FUN groupByWithoutAggregation-->
|
|
<tabs>
|
|
<tab title="Properties">
|
|
|
|
```kotlin
|
|
df.groupBy { city }.values()
|
|
df.groupBy { city }.values { name and age }
|
|
df.groupBy { city }.values { weight into "weights" }
|
|
```
|
|
|
|
</tab>
|
|
<tab title="Strings">
|
|
|
|
```kotlin
|
|
df.groupBy("city").values()
|
|
df.groupBy("city").values("name", "age")
|
|
df.groupBy("city").values { "weight" into "weights" }
|
|
```
|
|
|
|
</tab></tabs>
|
|
<inline-frame src="resources/org.jetbrains.kotlinx.dataframe.samples.api.Analyze.groupByWithoutAggregation.html" width="100%"/>
|
|
<!---END-->
|