Docs: worked example for combining CSV files with different headers (#1200) (#2148)

Add a section to the record-heterogeneity page showing how to
concatenate multiple CSV files whose headers differ, using unsparsify
(and unsparsify -f then regularize for the streaming case), with new
sample data files under docs/src/data/het/.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
John Kerl 2026-07-06 16:57:21 -04:00 committed by GitHub
parent 951e65561b
commit c69411f1b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 147 additions and 0 deletions

View file

@ -0,0 +1,3 @@
id,product,sales
1,pencil,100
2,eraser,17
1 id product sales
2 1 pencil 100
3 2 eraser 17

View file

@ -0,0 +1,3 @@
id,product,color,sales
3,pencil,red,120
4,eraser,green,32
1 id product color sales
2 3 pencil red 120
3 4 eraser green 32

View file

@ -0,0 +1,3 @@
id,product,sales,returns
5,pencil,200,6
6,notebook,41,2
1 id product sales returns
2 5 pencil 200 6
3 6 notebook 41 2

View file

@ -474,6 +474,95 @@ a,b,c
7,8,9,10
</pre>
### Combining multiple CSV files with different headers
A common special case of the above: you have several CSV files whose column names overlap but
don't all match -- say, data exported year by year, where columns were added or reordered over
time -- and you want to concatenate them into a single rectangular table, with empty cells where a
file didn't have a given column.
<pre class="pre-highlight-in-pair">
<b>cat data/het/sales-2021.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,sales
1,pencil,100
2,eraser,17
</pre>
<pre class="pre-highlight-in-pair">
<b>cat data/het/sales-2022.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,color,sales
3,pencil,red,120
4,eraser,green,32
</pre>
<pre class="pre-highlight-in-pair">
<b>cat data/het/sales-2023.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,sales,returns
5,pencil,200,6
6,notebook,41,2
</pre>
Since each file's records take their schema from that file's header line, simply catting the
files together gives a schema change partway through the record stream -- which RFC-4180 CSV
output doesn't allow:
<pre class="pre-highlight-in-pair">
<b>mlr --csv cat data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,sales
1,pencil,100
2,eraser,17
mlr: CSV schema change: first keys "id,product,sales"; current keys "id,product,color,sales"
mlr: exiting due to data error
</pre>
The solution is the [`unsparsify`](reference-verbs.md#unsparsify) verb, which we saw above: it
gives every record the union of all field names, filling in empty values (or, using
`--fill-with`, whatever you like) where a record lacks a field:
<pre class="pre-highlight-in-pair">
<b>mlr --csv unsparsify data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,sales,color,returns
1,pencil,100,,
2,eraser,17,,
3,pencil,120,red,
4,eraser,32,green,
5,pencil,200,,6
6,notebook,41,,2
</pre>
Note that `unsparsify` (without `-f`) is
[non-streaming](streaming-and-memory.md): it can't know the full set of field names until it has
read all input, so it retains all records in memory before producing any output. If your files
are too large for that, but you know the complete list of field names up front, you can use
`unsparsify -f` which is streaming. Fields filled in by `-f` are appended to each record, so if
the input files order their columns differently, follow up with
[`regularize`](reference-verbs.md#regularize) or
[`sort-within-records`](reference-verbs.md#sort-within-records) (also both streaming) to give all
records the same column ordering:
<pre class="pre-highlight-in-pair">
<b>mlr --csv unsparsify -f id,product,sales,color,returns then regularize data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv</b>
</pre>
<pre class="pre-non-highlight-in-pair">
id,product,sales,color,returns
1,pencil,100,,
2,eraser,17,,
3,pencil,120,red,
4,eraser,32,green,
5,pencil,200,,6
6,notebook,41,,2
</pre>
## Processing heterogeneous data
Above we saw how to make heterogeneous data homogeneous, and then how to print heterogeneous data.

View file

@ -205,6 +205,55 @@ GENMD-RUN-COMMAND
mlr --csv --allow-ragged-csv-input cat data/het/ragged.csv
GENMD-EOF
### Combining multiple CSV files with different headers
A common special case of the above: you have several CSV files whose column names overlap but
don't all match -- say, data exported year by year, where columns were added or reordered over
time -- and you want to concatenate them into a single rectangular table, with empty cells where a
file didn't have a given column.
GENMD-RUN-COMMAND
cat data/het/sales-2021.csv
GENMD-EOF
GENMD-RUN-COMMAND
cat data/het/sales-2022.csv
GENMD-EOF
GENMD-RUN-COMMAND
cat data/het/sales-2023.csv
GENMD-EOF
Since each file's records take their schema from that file's header line, simply catting the
files together gives a schema change partway through the record stream -- which RFC-4180 CSV
output doesn't allow:
GENMD-RUN-COMMAND-TOLERATING-ERROR
mlr --csv cat data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv
GENMD-EOF
The solution is the [`unsparsify`](reference-verbs.md#unsparsify) verb, which we saw above: it
gives every record the union of all field names, filling in empty values (or, using
`--fill-with`, whatever you like) where a record lacks a field:
GENMD-RUN-COMMAND
mlr --csv unsparsify data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv
GENMD-EOF
Note that `unsparsify` (without `-f`) is
[non-streaming](streaming-and-memory.md): it can't know the full set of field names until it has
read all input, so it retains all records in memory before producing any output. If your files
are too large for that, but you know the complete list of field names up front, you can use
`unsparsify -f` which is streaming. Fields filled in by `-f` are appended to each record, so if
the input files order their columns differently, follow up with
[`regularize`](reference-verbs.md#regularize) or
[`sort-within-records`](reference-verbs.md#sort-within-records) (also both streaming) to give all
records the same column ordering:
GENMD-RUN-COMMAND
mlr --csv unsparsify -f id,product,sales,color,returns then regularize data/het/sales-2021.csv data/het/sales-2022.csv data/het/sales-2023.csv
GENMD-EOF
## Processing heterogeneous data
Above we saw how to make heterogeneous data homogeneous, and then how to print heterogeneous data.