Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
Before a table is published, the pipeline profiles it: how many rows carry each column, how many of those values are null, and how many distinct values there are. A column that was ninety percent populated yesterday and is empty today is the cheapest possible signal that an upstream change broke something.
Write profile_columns(rows). Return one profile per column.
Function to write
profile_columns(rows: list[dict]) -> list[dict]One dict per column with column, present, missing, nulls, empty_rate and distinct.
How to approach it
Find every column name first, across all rows, then measure one column at a time.
Sample cases
+ 2 held back until you submit
a real table
One column is complete, one is half null, one is missing from some rows, and one repeats its values.
Input
Argument 1
[
{
'order_id': 'o1',
'sku': 'A1',
'coupon': None,
'qty': 2
},
{
'order_id': 'o2',
'sku': 'A1',
'qty': 2
},
{
'order_id': 'o3',
'sku': 'B2',
'coupon': 'SAVE10',
'qty': 5
},
{
'order_id': 'o4',
'sku': 'B2',
'coupon': None
}
]Returns
| column | present | missing | nulls | empty_rate | distinct |
|---|---|---|---|---|---|
| order_id | 4 | 0 | 0 | 0 | 4 |
| sku | 4 | 0 | 0 | 0 | 2 |
| coupon | 3 | 1 | 2 | 0.75 | 1 |
| qty | 3 | 1 | 0 | 0.25 | 2 |
no rows
Nothing to profile means an empty list, and in particular no division by zero.
Input
Argument 1
[] (empty list)
Returns
[] (empty list)
a column that is always null
Every value is null, so the empty rate is 1.0 and the distinct count is zero.
Input
Argument 1
| a |
|---|
| None |
| None |
Returns
| column | present | missing | nulls | empty_rate | distinct |
|---|---|---|---|---|---|
| a | 2 | 0 | 2 | 1 | 0 |
Constraints
present counts the rows that carry the column at all. missing counts the rows that do not. nulls counts the rows that carry it with a value of None.empty_rate is the share of all rows with no usable value — missing plus null, over the total row count — rounded to four decimal places.distinct counts the distinct non-null values. Values that cannot be hashed, such as a list, still have to be counted.column, present, missing, nulls, empty_rate and distinct.Worked example
The coupon column appears in three of the four rows and is null in two of them, so present is 3, missing is 1, nulls is 2 and the empty rate is 0.75 — three of four rows have nothing usable. Only SAVE10 is a real value, so distinct is 1.
Reading the column list off rows[0] is the shortcut that makes this fast to write and wrong on exactly this table: qty is absent from the last row and coupon is absent from the second, so whichever row you sample decides which columns the profile even mentions. A profile that does not mention a column reads identically to one that found nothing wrong with it.
What this tests
Two-pass thinking — discover the columns, then measure each one — and the habit of distinguishing absent from null. They come from different upstream faults and collapsing them loses the more actionable of the two.
profile_columns(rows: list[dict]) -> list[dict]Submit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.