A few months ago someone at a conference asked me what the Power Query Table.Partition() function could be used for, and I had to admit I had no idea. However, when I thought about it, I realised one obvious use: for creating histograms! Now I know there are lots of other good ways to create histograms in Excel but here’s one more, and hopefully it will satisfy the curiosity of anyone else who is wondering about Table.Partition().
Let’s start with a table in Excel (called “Customers”) containing a list of names and ages:
Here’s the M code for the query to find the buckets:
let
//Get data from Customers table
Source = Excel.CurrentWorkbook(){[Name="Customers"]}[Content],
//Get a list of all the values in the Age column
Ages = Table.Column(Source,"Age"),
//Find the maximum age
MaxAge = List.Max(Ages),
//The number of buckets is the max age divided by ten, then rounded up to the nearest integer
NumberOfBuckets = Number.RoundUp(MaxAge/10),
//Hash function to determine which bucket each customer goes into
BucketHashFunction = (age) => Number.RoundDown(age/10),
//Use Table.Partition() to split the table into multiple buckets
CreateBuckets = Table.Partition(Source, "Age", NumberOfBuckets, BucketHashFunction),
//Turn the resulting list into a table
#"Table from List" = Table.FromList(CreateBuckets, Splitter.SplitByNothing()
, null, null, ExtraValues.Error),
//Add a zero-based index column
#"Added Index" = Table.AddIndexColumn(#"Table from List", "Index", 0, 1),
//Calculate the name of each bucket
#"Added Custom" = Table.AddColumn(#"Added Index", "Bucket",
each Number.ToText([Index]*10) & " to " & Number.ToText(([Index]+1)*10)),
//Find the number of rows in each bucket - ie the count of customers
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Count", each Table.RowCount([Column1])),
//Remove unnecessary columns
#"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"Column1", "Index"})
in
#"Removed Columns"
And here’s the output in Excel, with a bar chart:
How does this work?
- After loading the data from the Excel table in the Source step, the first problem is to determine how many buckets we’ll need. This is fairly straightforward: I use Table.Column() to get a list containing all of the values in the Age column, then use List.Max() to find the maximum age, then divide this number by ten and round up to the nearest integer.
- Now for Table.Partition(). The first thing to understand about this function is what it returns: it takes a table and returns a list of tables, so you start with one table and end up with multiple tables. Each row from the original table will end up in one of the output tables. A list object is something like an array.
- One of the parameters that the Table.Partition() function needs is a hash function that determines which bucket table each row from the original table goes into. The BucketHashFunction step serves this purpose here: it takes a value, divides it by ten and rounds the result down; for example pass in the age 88 and you get the value 8 back.
- The CreateBuckets step calls Table.Partition() with the four parameters it needs: the name of the table to partition, the column to partition by, the number of buckets to create and the hash function. For each row in the original table the age of each customer is passed to the hash function. The number that the hash function returns is the index of the table in the list that Table.Partition() returns. In the example above nine buckets are created, so Table.Partition() returns a list containing nine tables; for the age 8, the hash function returns 0 so the row is put in the table at index 0 in the list; for the age 88 the hash function returns 8, so the row is put in the table at index 8 in the list. The output of this step, the list of tables, looks like this:
- The next thing to do is to convert the list itself to a table, then add a custom column to show the names for each bucket. This is achieved by adding a zero-based index column and then using that index value to generate the required text in the step #”Added Custom”.
- Next, find the number of customers in each bucket. Remember that at this point the query still includes a column (called “Column1”) that contains a value of type table, so all that is needed is to create another custom column that calls Table.RowCount() for each bucket table, as seen in the step #”Added Custom1”.
- Finally I remove the columns that aren’t needed for the output table.
I’m not convinced this is the most efficient solution for large data sets (I bet query folding stops very early on if you try this on a SQL Server data source) but it’s a good example of how Table.Partition() works. What other uses for it can you think of?
You can download the sample workbook here.