Skip to content

Group By ​

DimensionalData.jl provides a groupby function for dimensional grouping. This guide covers:

  • simple grouping with a function

  • grouping with Bins

  • grouping with another existing AbstractDimArray or Dimension

Grouping functions ​

Let's look at the kind of functions that can be used to group DateTime. Other types will follow the same principles, but are usually simpler.

First, load some packages:

julia
using DimensionalData
using Dates
using Statistics
const DD = DimensionalData

Now create a demo DateTime range

julia
julia> tempo = range(DateTime(2000), step=Hour(1), length=365*24*2)
DateTime("2000-01-01T00:00:00"):Hour(1):DateTime("2001-12-30T23:00:00")

Let's see how some common functions work.

The hour function will transform values to the hour of the day - the integers 0:23

julia
julia> hour.(tempo)
17520-element Vector{Int64}:
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
  ⋮
 15
 16
 17
 18
 19
 20
 21
 22
 23

Tuple groupings

julia
julia> yearmonth.(tempo)
17520-element Vector{Tuple{Int64, Int64}}:
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 (2000, 1)
 ⋮
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)
 (2001, 12)

Grouping and reducing ​

Let's define an array with a time dimension of the times used above:

julia
julia> A = rand(X(1:0.01:2), Ti(tempo))
┌ 101×17520 DimArray{Float64, 2} ┐
├────────────────────────────────┴─────────────────────────────────────── dims ┐
  ↓ X Sampled{Float64} 1.0:0.01:2.0 ForwardOrdered Regular Points,
  → Ti Sampled{DateTime} DateTime("2000-01-01T00:00:00"):Hour(1):DateTime("2001-12-30T23:00:00") ForwardOrdered Regular Points
└──────────────────────────────────────────────────────────────────────────────┘
 ↓ →    2000-01-01T00:00:00   2000-01-01T01:00:00  …   2001-12-30T23:00:00
 1.0   0.558508              0.698367                 0.668025
 1.01  0.0668559             0.237574                 0.967313
 1.02  0.450109              0.713462                 0.970181
 1.03  0.668984              0.379499                 0.714098
 ⋮                                                 ⋱  ⋮
 1.97  0.24221               0.936318                 0.408579
 1.98  0.969632              0.709608                 0.527233
 1.99  0.498699              0.270122              …  0.479234
 2.0   0.020678              0.95795                  0.254546

Group by month, using the month function:

julia
julia> groups = groupby(A, Ti=>month)
┌ 12-element DimGroupByArray{DimArray{Float64,2},1} ┐
├───────────────────────────────────────────────────┴──────── dims ┐
  ↓ Ti Sampled{Int64} [1, …, 12] ForwardOrdered Irregular Points
├──────────────────────────────────────────────────────── metadata ┤
  Dict{Symbol, Any} with 1 entry:
  :groupby => :Ti=>month
├───────────────────────────────────────────────────┴── group dims ┐
  ↓ X, → Ti
└──────────────────────────────────────────────────────────────────┘
  1  101×1488 DimArray
  2  101×1368 DimArray
  ⋮
 12  101×1464 DimArray

We can take the mean of each group by broadcasting over them:

julia
julia> mean.(groups)
┌ 12-element DimArray{Float64, 1} ┐
├─────────────────────────────────┴────────────────────────── dims ┐
  ↓ Ti Sampled{Int64} [1, …, 12] ForwardOrdered Irregular Points
└──────────────────────────────────────────────────────────────────┘
  1  0.50113
  2  0.499647
  3  0.50036
  4  0.499903
  5  0.500635
  ⋮
  8  0.500791
  9  0.49967
 10  0.499702
 11  0.499031
 12  0.500899

Binning ​

Sometimes we want to further aggregate our groups after running a function, or just bin the raw data directly. We can use the Bins wrapper to do this.

For quick analysis, we can break our groups into N bins.

julia
julia> groupby(A, Ti=>Bins(month, 4))
┌ 4-element DimGroupByArray{DimArray{Float64,2},1} ┐
├──────────────────────────────────────────────────┴───────────────────── dims ┐
  ↓ Ti Sampled{IntervalSets.Interval{:closed, :open, Float64}} [1.0 .. 3.75275 (closed-open), …, 9.25825 .. 12.011 (closed-open)] ForwardOrdered Irregular Intervals{Start}
├──────────────────────────────────────────────────────────────────── metadata ┤
  Dict{Symbol, Any} with 1 entry:
  :groupby => :Ti=>Bins(month, 4)…
├──────────────────────────────────────────────────┴─────────────── group dims ┐
  ↓ X, → Ti
└──────────────────────────────────────────────────────────────────────────────┘
 1.0 .. 3.75275 (closed-open)     101×4344 DimArray
 ⋮
 9.25825 .. 12.011 (closed-open)  101×4392 DimArray

Doing this requires slightly padding the bin edges, so the lookup of the output is less than ideal.

Select by Dimension ​

We can also select by Dimensions and any objects with dims methods.

Trivially, grouping by an object's own dimension is similar to eachslice:

julia
julia> groupby(A, dims(A, Ti))
┌ 17520-element DimGroupByArray{DimArray{Float64,2},1} ┐
├──────────────────────────────────────────────────────┴───────────────── dims ┐
  ↓ Ti Sampled{DateTime} [DateTime("2000-01-01T00:00:00"), …, DateTime("2001-12-30T23:00:00")] ForwardOrdered Irregular Points
├──────────────────────────────────────────────────────────────────── metadata ┤
  Dict{Symbol, Any} with 1 entry:
  :groupby => :Ti=>[DateTime("2000-01-01T00:00:00"), DateTime("2000-01-01T01:00…
├──────────────────────────────────────────────────────┴─────────── group dims ┐
  ↓ X, → Ti
└──────────────────────────────────────────────────────────────────────────────┘
 2000-01-01T00:00:00  101×1 DimArray
 2000-01-01T01:00:00  101×1 DimArray
 ⋮
 2001-12-30T23:00:00  101×1 DimArray

TODO: Apply custom function (i.e. normalization) to grouped output.