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:
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:
-- 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-27Assign each record a tile number by one of five methods: equal records, equal sum, manual, ...
Polars (1)
Expr.qcutread 2026-09-27Bin continuous values into categories by their quantiles
RapidMiner Studio (2)
Discretize by Frequencyread 2026-09-27Bin numeric columns into a given number of equal-frequency bins
Discretize by Sizeread 2026-09-27Bin numeric columns so that each bin holds a given number of rows
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)
Sources
- [1]Discretize by Frequency - Rapidminer Documentationdocumentation
- [2]pandas.qcut — pandas 3.1.0 documentationdocumentation
- [3]Histogram - Wikipediasecondary source
- [4]NTILEdocumentation
- [5]NTILE (Transact-SQL) - SQL Server | Microsoft Learndocumentation
- [6]Quantile - Wikipediasecondary source
- [7]KBinsDiscretizer — scikit-learn 1.9.1 documentationdocumentation
- [8]Window Functions – DuckDBdocumentation