I went for a solution that doesn't presuppose any kind of sorting: it just looks for a value and remembers in which column (on any row) it appeared.
Starting with this input:
a,b,a
c,c,b
d,e,e
f,f,g
g,g,h
h,h,i
It generates a map of values to positions:
{
"a": [0, 2],
"b": [1, 2],
"c": [0, 1],
"d": [0],
"e": [1, 2],
"f": [0, 1],
"g": [2, 0, 1],
"h": [2, 0, 1],
"i": [2],
}
From the map, it iterates over the values and writes each value into its own row, in the found positions (columns):
a,,a
,b,b
c,c,
d,,
,e,e
f,f,
g,g,g
h,h,h
,,i
Since it doesn't presuppose sorting, this:
b,a,a
d,d,d
c,e,e
becomes:
{
"b": [0],
"a": [1, 2],
"d": [0, 1, 2],
"c": [0],
"e": [1, 2],
}
becomes:
b,,
,a,a
d,d,d
c,,
,e,e
This (formatted for legibility):
| ABCDE12345_001 | FGHIJ6789_002 | ABCDE12345_001 |
| KLMNO5432_003 | KLMNO5432_003 | FGHIJ6789_002 |
| PQRST24680_123 | UVWXY13579_555 | UVWXY13579_555 |
| ZABCD876530_009 | ZABCD876530_009 | AABBCCDDEE_987 |
| AABBCCDDEE_987 | AABBCCDDEE_987 | LMNOP98765_999 |
| LMNOP98765_999 | ZYXWV54321_777 | ZYXWV54321_777 |
becomes:
| ABCDE12345_001 | | ABCDE12345_001 |
| | FGHIJ6789_002 | FGHIJ6789_002 |
| KLMNO5432_003 | KLMNO5432_003 | |
| PQRST24680_123 | | |
| | UVWXY13579_555 | UVWXY13579_555 |
| ZABCD876530_009 | ZABCD876530_009 | |
| AABBCCDDEE_987 | AABBCCDDEE_987 | AABBCCDDEE_987 |
| LMNOP98765_999 | | LMNOP98765_999 |
| | ZYXWV54321_777 | ZYXWV54321_777 |
I've include the program in Python, and Go (because I really like writing Go).