Class: Polars::GroupBy

Inherits:
Object
  • Object
show all
Defined in:
lib/polars/group_by.rb

Overview

Starts a new GroupBy operation.

Instance Method Summary collapse

Instance Method Details

#agg(aggs) ⇒ DataFrame

Use multiple aggregations on columns.

This can be combined with complete lazy API and is considered idiomatic polars.

Examples:

df = Polars::DataFrame.new(
  {"foo" => ["one", "two", "two", "one", "two"], "bar" => [5, 3, 2, 4, 1]}
)
df.group_by("foo", maintain_order: true).agg(
  [
    Polars.sum("bar").suffix("_sum"),
    Polars.col("bar").sort.tail(2).sum.suffix("_tail_sum")
  ]
)
# =>
# shape: (2, 3)
# ┌─────┬─────────┬──────────────┐
# │ foo ┆ bar_sum ┆ bar_tail_sum │
# │ --- ┆ ---     ┆ ---          │
# │ str ┆ i64     ┆ i64          │
# ╞═════╪═════════╪══════════════╡
# │ one ┆ 9       ┆ 9            │
# │ two ┆ 6       ┆ 5            │
# └─────┴─────────┴──────────────┘

Parameters:

  • aggs (Object)

    Single / multiple aggregation expression(s).

Returns:



138
139
140
141
142
143
# File 'lib/polars/group_by.rb', line 138

def agg(aggs)
  @df.lazy
    .group_by(@by, maintain_order: @maintain_order)
    .agg(aggs)
    .collect(no_optimization: true)
end

#countDataFrame

Count the number of values in each group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).count
# =>
# shape: (3, 2)
# ┌────────┬───────┐
# │ d      ┆ count │
# │ ---    ┆ ---   │
# │ str    ┆ u32   │
# ╞════════╪═══════╡
# │ Apple  ┆ 3     │
# │ Orange ┆ 1     │
# │ Banana ┆ 2     │
# └────────┴───────┘

Returns:



417
418
419
# File 'lib/polars/group_by.rb', line 417

def count
  agg(Polars.len.alias("count"))
end

#eachObject

Allows iteration over the groups of the group by operation.

Examples:

df = Polars::DataFrame.new({"foo" => ["a", "a", "b"], "bar" => [1, 2, 3]})
df.group_by("foo", maintain_order: true).each.to_h
# =>
# {"a"=>shape: (2, 2)
# ┌─────┬─────┐
# │ foo ┆ bar │
# │ --- ┆ --- │
# │ str ┆ i64 │
# ╞═════╪═════╡
# │ a   ┆ 1   │
# │ a   ┆ 2   │
# └─────┴─────┘, "b"=>shape: (1, 2)
# ┌─────┬─────┐
# │ foo ┆ bar │
# │ --- ┆ --- │
# │ str ┆ i64 │
# ╞═════╪═════╡
# │ b   ┆ 3   │
# └─────┴─────┘}

Returns:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/polars/group_by.rb', line 35

def each
  return to_enum(:each) unless block_given?

  temp_col = "__POLARS_GB_GROUP_INDICES"
  groups_df =
    @df.lazy
      .with_row_index(name: temp_col)
      .group_by(@by, maintain_order: @maintain_order)
      .agg(Polars.col(temp_col))
      .collect(no_optimization: true)

  group_names = groups_df.select(Polars.all.exclude(temp_col))

  # When grouping by a single column, group name is a single value
  # When grouping by multiple columns, group name is a tuple of values
  if @by.is_a?(::String) || @by.is_a?(Expr)
    _group_names = group_names.to_series.each
  else
    _group_names = group_names.iter_rows
  end

  _group_indices = groups_df.select(temp_col).to_series
  _current_index = 0

  while _current_index < _group_indices.length
    group_name = _group_names.next
    group_data = @df[_group_indices[_current_index]]
    _current_index += 1

    yield group_name, group_data
  end
end

#firstDataFrame

Aggregate the first values in the group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).first
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────┬───────┐
# │ d      ┆ a   ┆ b    ┆ c     │
# │ ---    ┆ --- ┆ ---  ┆ ---   │
# │ str    ┆ i64 ┆ f64  ┆ bool  │
# ╞════════╪═════╪══════╪═══════╡
# │ Apple  ┆ 1   ┆ 0.5  ┆ true  │
# │ Orange ┆ 2   ┆ 0.5  ┆ true  │
# │ Banana ┆ 4   ┆ 13.0 ┆ false │
# └────────┴─────┴──────┴───────┘

Returns:



272
273
274
# File 'lib/polars/group_by.rb', line 272

def first
  agg(Polars.all.first)
end

#head(n = 5) ⇒ DataFrame

Get the first n rows of each group.

Examples:

df = Polars::DataFrame.new(
  {
    "letters" => ["c", "c", "a", "c", "a", "b"],
    "nrs" => [1, 2, 3, 4, 5, 6]
  }
)
# =>
# shape: (6, 2)
# ┌─────────┬─────┐
# │ letters ┆ nrs │
# │ ---     ┆ --- │
# │ str     ┆ i64 │
# ╞═════════╪═════╡
# │ c       ┆ 1   │
# │ c       ┆ 2   │
# │ a       ┆ 3   │
# │ c       ┆ 4   │
# │ a       ┆ 5   │
# │ b       ┆ 6   │
# └─────────┴─────┘
df.group_by("letters").head(2).sort("letters")
# =>
# shape: (5, 2)
# ┌─────────┬─────┐
# │ letters ┆ nrs │
# │ ---     ┆ --- │
# │ str     ┆ i64 │
# ╞═════════╪═════╡
# │ a       ┆ 3   │
# │ a       ┆ 5   │
# │ b       ┆ 6   │
# │ c       ┆ 1   │
# │ c       ┆ 2   │
# └─────────┴─────┘

Parameters:

  • n (Integer) (defaults to: 5)

    Number of rows to return.

Returns:



189
190
191
192
193
194
# File 'lib/polars/group_by.rb', line 189

def head(n = 5)
  @df.lazy
    .group_by(@by, maintain_order: @maintain_order)
    .head(n)
    .collect(no_optimization: true)
end

#lastDataFrame

Aggregate the last values in the group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).last
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────┬───────┐
# │ d      ┆ a   ┆ b    ┆ c     │
# │ ---    ┆ --- ┆ ---  ┆ ---   │
# │ str    ┆ i64 ┆ f64  ┆ bool  │
# ╞════════╪═════╪══════╪═══════╡
# │ Apple  ┆ 3   ┆ 10.0 ┆ false │
# │ Orange ┆ 2   ┆ 0.5  ┆ true  │
# │ Banana ┆ 5   ┆ 14.0 ┆ true  │
# └────────┴─────┴──────┴───────┘

Returns:



301
302
303
# File 'lib/polars/group_by.rb', line 301

def last
  agg(Polars.all.last)
end

#maxDataFrame

Reduce the groups to the maximal value.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).max
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────┬──────┐
# │ d      ┆ a   ┆ b    ┆ c    │
# │ ---    ┆ --- ┆ ---  ┆ ---  │
# │ str    ┆ i64 ┆ f64  ┆ bool │
# ╞════════╪═════╪══════╪══════╡
# │ Apple  ┆ 3   ┆ 10.0 ┆ true │
# │ Orange ┆ 2   ┆ 0.5  ┆ true │
# │ Banana ┆ 5   ┆ 14.0 ┆ true │
# └────────┴─────┴──────┴──────┘

Returns:



388
389
390
# File 'lib/polars/group_by.rb', line 388

def max
  agg(Polars.all.max)
end

#meanDataFrame

Reduce the groups to the mean values.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).mean
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────────┬──────────┐
# │ d      ┆ a   ┆ b        ┆ c        │
# │ ---    ┆ --- ┆ ---      ┆ ---      │
# │ str    ┆ f64 ┆ f64      ┆ f64      │
# ╞════════╪═════╪══════════╪══════════╡
# │ Apple  ┆ 2.0 ┆ 4.833333 ┆ 0.666667 │
# │ Orange ┆ 2.0 ┆ 0.5      ┆ 1.0      │
# │ Banana ┆ 4.5 ┆ 13.5     ┆ 0.5      │
# └────────┴─────┴──────────┴──────────┘

Returns:



446
447
448
# File 'lib/polars/group_by.rb', line 446

def mean
  agg(Polars.all.mean)
end

#medianDataFrame

Return the median per group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "d" => ["Apple", "Banana", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).median
# =>
# shape: (2, 3)
# ┌────────┬─────┬──────┐
# │ d      ┆ a   ┆ b    │
# │ ---    ┆ --- ┆ ---  │
# │ str    ┆ f64 ┆ f64  │
# ╞════════╪═════╪══════╡
# │ Apple  ┆ 2.0 ┆ 4.0  │
# │ Banana ┆ 4.0 ┆ 13.0 │
# └────────┴─────┴──────┘

Returns:



533
534
535
# File 'lib/polars/group_by.rb', line 533

def median
  agg(Polars.all.median)
end

#minDataFrame

Reduce the groups to the minimal value.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"],
  }
)
df.group_by("d", maintain_order: true).min
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────┬───────┐
# │ d      ┆ a   ┆ b    ┆ c     │
# │ ---    ┆ --- ┆ ---  ┆ ---   │
# │ str    ┆ i64 ┆ f64  ┆ bool  │
# ╞════════╪═════╪══════╪═══════╡
# │ Apple  ┆ 1   ┆ 0.5  ┆ false │
# │ Orange ┆ 2   ┆ 0.5  ┆ true  │
# │ Banana ┆ 4   ┆ 13.0 ┆ false │
# └────────┴─────┴──────┴───────┘

Returns:



359
360
361
# File 'lib/polars/group_by.rb', line 359

def min
  agg(Polars.all.min)
end

#n_uniqueDataFrame

Count the unique values per group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 1, 3, 4, 5],
    "b" => [0.5, 0.5, 0.5, 10, 13, 14],
    "d" => ["Apple", "Banana", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).n_unique
# =>
# shape: (2, 3)
# ┌────────┬─────┬─────┐
# │ d      ┆ a   ┆ b   │
# │ ---    ┆ --- ┆ --- │
# │ str    ┆ u32 ┆ u32 │
# ╞════════╪═════╪═════╡
# │ Apple  ┆ 2   ┆ 2   │
# │ Banana ┆ 3   ┆ 3   │
# └────────┴─────┴─────┘

Returns:



473
474
475
# File 'lib/polars/group_by.rb', line 473

def n_unique
  agg(Polars.all.n_unique)
end

#plot(*args, **options) ⇒ Vega::LiteChart

Plot data.

Returns:

  • (Vega::LiteChart)

Raises:

  • (ArgumentError)


540
541
542
543
544
545
546
# File 'lib/polars/group_by.rb', line 540

def plot(*args, **options)
  raise ArgumentError, "Multiple groups not supported" if @by.is_a?(::Array) && @by.size > 1
  # same message as Ruby
  raise ArgumentError, "unknown keyword: :group" if options.key?(:group)

  @df.plot(*args, **options, group: @by)
end

#quantile(quantile, interpolation: "nearest") ⇒ DataFrame

Compute the quantile per group.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).quantile(1)
# =>
# shape: (3, 3)
# ┌────────┬─────┬──────┐
# │ d      ┆ a   ┆ b    │
# │ ---    ┆ --- ┆ ---  │
# │ str    ┆ f64 ┆ f64  │
# ╞════════╪═════╪══════╡
# │ Apple  ┆ 3.0 ┆ 10.0 │
# │ Orange ┆ 2.0 ┆ 0.5  │
# │ Banana ┆ 5.0 ┆ 14.0 │
# └────────┴─────┴──────┘

Parameters:

  • quantile (Float)

    Quantile between 0.0 and 1.0.

  • interpolation ("nearest", "higher", "lower", "midpoint", "linear") (defaults to: "nearest")

    Interpolation method.

Returns:



506
507
508
# File 'lib/polars/group_by.rb', line 506

def quantile(quantile, interpolation: "nearest")
  agg(Polars.all.quantile(quantile, interpolation: interpolation))
end

#sumDataFrame

Reduce the groups to the sum.

Examples:

df = Polars::DataFrame.new(
  {
    "a" => [1, 2, 2, 3, 4, 5],
    "b" => [0.5, 0.5, 4, 10, 13, 14],
    "c" => [true, true, true, false, false, true],
    "d" => ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"]
  }
)
df.group_by("d", maintain_order: true).sum
# =>
# shape: (3, 4)
# ┌────────┬─────┬──────┬─────┐
# │ d      ┆ a   ┆ b    ┆ c   │
# │ ---    ┆ --- ┆ ---  ┆ --- │
# │ str    ┆ i64 ┆ f64  ┆ u32 │
# ╞════════╪═════╪══════╪═════╡
# │ Apple  ┆ 6   ┆ 14.5 ┆ 2   │
# │ Orange ┆ 2   ┆ 0.5  ┆ 1   │
# │ Banana ┆ 9   ┆ 27.0 ┆ 1   │
# └────────┴─────┴──────┴─────┘

Returns:



330
331
332
# File 'lib/polars/group_by.rb', line 330

def sum
  agg(Polars.all.sum)
end

#tail(n = 5) ⇒ DataFrame

Get the last n rows of each group.

Examples:

df = Polars::DataFrame.new(
  {
    "letters" => ["c", "c", "a", "c", "a", "b"],
    "nrs" => [1, 2, 3, 4, 5, 6]
  }
)
# =>
# shape: (6, 2)
# ┌─────────┬─────┐
# │ letters ┆ nrs │
# │ ---     ┆ --- │
# │ str     ┆ i64 │
# ╞═════════╪═════╡
# │ c       ┆ 1   │
# │ c       ┆ 2   │
# │ a       ┆ 3   │
# │ c       ┆ 4   │
# │ a       ┆ 5   │
# │ b       ┆ 6   │
# └─────────┴─────┘
df.group_by("letters").tail(2).sort("letters")
# =>
# shape: (5, 2)
# ┌─────────┬─────┐
# │ letters ┆ nrs │
# │ ---     ┆ --- │
# │ str     ┆ i64 │
# ╞═════════╪═════╡
# │ a       ┆ 3   │
# │ a       ┆ 5   │
# │ b       ┆ 6   │
# │ c       ┆ 2   │
# │ c       ┆ 4   │
# └─────────┴─────┘

Parameters:

  • n (Integer) (defaults to: 5)

    Number of rows to return.

Returns:



240
241
242
243
244
245
# File 'lib/polars/group_by.rb', line 240

def tail(n = 5)
  @df.lazy
    .group_by(@by, maintain_order: @maintain_order)
    .tail(n)
    .collect(no_optimization: true)
end