Shape / columns · All operations

Bin into groups of equal size

A variant of Group values into bins

Assign each row to a tile or quantile so that each bin holds about the same number of rows.

Status

In arcform

Runs as a SQL step

arcform carries it as a sql: step today.

The DuckDB step

Assign each row to a quartile so each bin holds about the same number of rows

It reads the orders table the proofs share. Create it first:

SQL
CREATE TABLE orders AS SELECT * FROM (VALUES
  (1,  'Alice Ng',      'north', DATE '2026-01-03', 120.00, 'widget', 'a|b',   '{"channel":"web","items":[1,2]}'),
  (2,  'alice ng',      'north', DATE '2026-01-05',  80.50, 'gadget', 'b',     '{"channel":"shop","items":[3]}'),
  (3,  'Bob  Okafor',   'south', DATE '2026-01-05', 300.00, 'widget', 'a|c',   '{"channel":"web","items":[4,5,6]}'),
  (4,  'Carla Díaz',    'east',  DATE '2026-01-09',  NULL,  'gizmo',  NULL,    '{"channel":"web","items":[]}'),
  (5,  'Bob Okafor',    'south', DATE '2026-01-12',  45.25, 'gadget', 'c',     '{"channel":"phone","items":[7]}'),
  (6,  'Dev Patel',     'west',  DATE '2026-01-12', 510.00, 'widget', 'a',     '{"channel":"web","items":[8,9]}'),
  (7,  'Carla Diaz',    'east',  DATE '2026-01-20',  99.99, 'gizmo',  'b|c',   '{"channel":"shop","items":[10]}'),
  (8,  'Dev Patel',     'west',  DATE '2026-02-02', 510.00, 'widget', 'a',     '{"channel":"web","items":[8,9]}'),
  (9,  'Eve Larsen',    NULL,    DATE '2026-02-03',  15.00, 'gadget', NULL,    '{"channel":"web","items":[11]}'),
  (10, 'Alice Ng',      'north', DATE '2026-02-10', 220.00, 'gizmo',  'a|b|c', '{"channel":"phone","items":[12,13]}')
) t(order_id, customer, region, order_date, amount, product, tags, meta);

Then run the statement:

SQL
-- the missing amount is ranked in a partition of its own and then given no quartile, so it keeps its row without shifting the others
CREATE TEMP TABLE result AS
SELECT order_id, amount,
       CASE WHEN amount IS NOT NULL THEN ntile(4) OVER (PARTITION BY amount IS NULL ORDER BY amount) END AS quartile FROM orders;
SELECT * FROM result ORDER BY amount;
SELECT CASE WHEN ((SELECT count(*) FROM result) = 10 AND (SELECT quartile FROM result WHERE order_id = 4) IS NULL AND (SELECT list(n ORDER BY quartile) FROM (SELECT quartile, count(*) AS n FROM result WHERE quartile IS NOT NULL GROUP BY quartile)) = [3, 2, 2, 2] AND (SELECT quartile FROM result WHERE order_id = 9) = 1 AND (SELECT quartile FROM result WHERE order_id = 1) = 2 AND (SELECT quartile FROM result WHERE order_id = 7) = 2 AND (SELECT quartile FROM result WHERE order_id = 10) = 3 AND (SELECT min(quartile) FROM result WHERE amount = 510) = 4 AND (SELECT max(quartile) FROM result WHERE amount = 510) = 4) THEN 'as expected' ELSE error('the nine amounts were not dealt into four quartiles of 3, 2, 2 and 2 rows in order with the missing amount kept and unbinned') END AS verdict;
  • It needs nothing installed beyond DuckDB itself.
  • The statement checks its own result and raises an error when it is wrong: “the nine amounts were not dealt into four quartiles of 3, 2, 2 and 2 rows in order with the missing amount kept and unbinned”.
  • It passed on DuckDB v2.0.0 (Cyanoptera) d54cbbe3df.

What other tools call it

Meridian's classification; an independent blind rating agreed on 95.7% of 324 rows (variant, version 3).

Alteryx Designer (1)
  • Tileread 2026-09-27

    Assign each record a tile number by one of five methods: equal records, equal sum, manual, ...

Polars (1)
  • Expr.qcutread 2026-09-27

    Bin continuous values into categories by their quantiles

RapidMiner Studio (2)
Tableau Prep (2)
  • NTILEread 2026-09-27

    Distribute the rows of a partition into a number of tiles

  • Tileread 2026-09-27

    Distribute rows into a number of buckets by ordered fields within optional groups

Background

Equal-size binning puts each row in one of a number of groups that hold about the same count of rows, such as quartiles or deciles.[1][2]

When to use it

Reach for equal-size bins to rank rows into groups, such as customers into spending deciles, or when a few extreme values would leave most equal-width bins nearly empty. (Meridian's view) A common case of variable bin widths is equiprobable bins, where the number of samples in each bin is expected to be approximately equal.[3]

If Oracle's NTILE is asked for more buckets than there are rows, one bucket per row is filled and the remaining buckets are empty.[4] SQL Server's NTILE is nondeterministic.[5] Order by the value and then by a unique key, so rows with tied values are dealt to the same groups on every run. (Meridian's view)

The method

pandas's qcut discretises a variable into equal-sized buckets based on rank or on sample quantiles, with 4 for quartiles and 10 for deciles.[2]

The value of a quantile may not be uniquely determined in some cases, as with the median of a set of even size.[6] scikit-learn's KBinsDiscretizer lets the quantile method be chosen from definitions such as inverted_cdf and averaged_inverted_cdf.[7] RapidMiner's Discretize by Frequency can set the number of bins to the square root of the number of non-missing values.[1]

Compared with the alternatives

DuckDB's percent_rank gives each row's relative rank as (rank() - 1) / (total partition rows - 1).[8] Use percent_rank when a row's exact standing matters, and a tile number when a handful of groups is easier to read. (Meridian's view)