Skip to Content
Silent failuresMy sample labels look swapped

My sample labels look swapped

A result is the wrong way round. The case sample carries the control’s genotypes, or a known carrier comes back negative while someone unrelated comes back positive. Everything upstream ran cleanly. Somewhere in a join, the cohort changed order and the labels did not follow it.

What actually happened

Two files contain the same samples in a different order, and something combined them positionally.

A VCF’s genotype columns are ordered, and that order is declared once in the #CHROM header line. A matrix written out of one tool and read into another carries its columns in whatever order it had. A sort, a join, a glob expanding in a different locale, a dictionary that used to preserve insertion order and now does not — any of these reorders a cohort.

When column 1 of one file is paired with column 1 of another and those are not the same person, every value is attributed to the wrong individual. The result is not noise. It is a confident, specific, wrong answer about a named person.

Why nothing crashed

Both files have the right number of columns. Both have valid sample identifiers. Every value is of the right type and in the right range.

Positional joins are the default in a great deal of scientific code — paste, cbind, np.hstack, a zip over two sorted lists — and none of them check that the things being paired refer to the same subject. They cannot: by the time the data is a matrix, the identifiers have usually been dropped.

And the output is still a valid cohort. Reversing two labels in a case–control study produces a result that is internally consistent and has the sign flipped.

What Ligase says

Two VCFs with the same two samples in opposite column order:

DimensionErrorin stepmergeligase check — nothing ran
left.sample_set=[sample_a, sample_b]from VCF header sample columns: crates/ligase-core/tests/fixtures/tiny_two_samples.vcf
right.sample_set=[sample_b, sample_a]from VCF header sample columns: crates/ligase-core/tests/fixtures/tiny_swapped_samples.vcf
The same samples appear in both, in a different order.
These must match, in order. Did the sample order change upstream?

The engine reports the sets in order, as lists rather than as sets, because order is the thing that differs. It then says so explicitly — the same samples appear in both, in a different order — which distinguishes this from the other thing it could have been, a genuinely different cohort.

The closing question points upstream rather than at the current step, because that is where a reordering happens. The step that failed the check is almost never the step that caused the problem.

How it is caught

sample_set is an ordered dimension, read from the VCF header’s sample columns, the BAM’s @RG SM tags, or FASTQ filenames:

>>> lg.variants("cohort_a.vcf", organism="human").dimension("sample_set").value '[sample_a, sample_b]'

Sample sets must match by identity and order wherever steps join samples. A set comparison would pass this case, which is exactly why it is not a set.

Last updated on