Repository navigation
Give NaN for a pair of samples with no shared called site - #343
Merged
Merged
Conversation
A pair of samples that is never called at the same site has no distance, but pairwise_diffs (haploid and diploid), decomposition.pairwise_distance and relatedness.ibs gave it 0.0. That marked the pair as identical, or for IBS as sharing nothing, and the distance moments and pcoa used the false value. These functions now give NaN for such a pair: - _pairwise_diffs_matrix_gpu holds the rule once. Its 'normalize' mode serves both pairwise_diffs functions, and a new 'scale' mode serves pairwise_distance and divergence.pairwise_distance_matrix. - relatedness.ibs gives NaN in the eager, streaming and empty-source paths. The diagonal stays 1. - dist_moments skips the undefined pairs, so the windowed dist_* columns do too, and gives NaN when there are pairs but none is defined. - pcoa raises ValueError on a NaN distance and counts the undefined pairs. - lostruct leaves a window with no local PCA out of the MDS and gives it a NaN mds row. It raises ValueError when fewer than two windows have a local PCA. Before, such a window made pcoa fail with LinAlgError. Fixes #322.
pairwise_diffs_diploid looked up a population by name only, so a population given as a list of rows raised TypeError, and so did dist_moments on a GenotypeMatrix. It now selects rows with population_rows, as the haploid path and ibs do.
dist_moments still gave 0.0 when missing data left too few defined pairs for a moment, for example one defined pair among six, which looked like a window with no spread. A moment with too few defined pairs is now NaN when some pair has no distance, and keeps the 0.0 small-sample rule otherwise. lostruct failed inside corners with an unclear error when the valid windows gave a one-dimensional MDS, and when n_components was not 2. corners now gets the first two MDS axes, and lostruct names the cause when there are fewer than two. The pcoa error counts NaN rows as well as pairs, so a NaN only on the diagonal is not reported as 0 pairs. The docs now give every reason a window can be invalid, the tutorial's corners call returns window indices as lostruct does, and the missing data page covers missing_data='exclude' with no complete site. Tests cover these cases, and the test references that still used 0.0 for an undefined pair use NaN.
nspope
approved these changes
Oct 27, 2026
nspope
left a comment
Collaborator
There was a problem hiding this comment.
This looks good to me; pulled it down and don't see any issues in practice.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #322.
A pair of samples with no site called in both has no distance. Before this PR, these functions gave such a pair 0.0, which reads as "identical" (or, for IBS, as "share nothing"):
pairwise_diffs_haploidandpairwise_diffs_diploid;decomposition.pairwise_distance;relatedness.ibs.The distance moments and
pcoathen used the false value. This PR gives these pairs NaN, the convention of #321. Each consumer skips the NaN pairs or raises an error.Behavior changes
ibs, this applies to the eager, streaming and empty-source paths. The diagonal stays 1.dist_var,dist_skew,dist_kurt(scalar and windoweddist_*columns) skip the NaN pairs. A moment needs 2, 3 or 4 defined pairs. With fewer, the moment is NaN when missing data caused the shortage, and 0.0 when the sample itself is too small (the rule before this PR).pcoaraisesValueErroron a NaN distance. The message counts the NaN pairs and rows and names the two sources of NaN (pairwise_distance,pc_dist).lostructleaves out of the MDS each window thatpc_distcannot compare (too few variants, or no variation whennormalizeis set), and gives that window a NaNmdsrow. Before, such a window madepcoafail withLinAlgError.lostructraisesValueErrorwhen fewer than two windows can be compared, or when corners are requested and the MDS has fewer than two dimensions.cornersnow gets the first two MDS axes, son_componentsother than 2 works.pairwise_diffs_diploid(and sodist_momentson aGenotypeMatrix) accepts a population given as a list of rows. Before, this raisedTypeError.Refactors with no change in value
_pairwise_diffs_matrix_gpuholds the NaN rule in one place. Its'normalize'mode serves bothpairwise_diffsfunctions. A new'scale'mode servespairwise_distanceanddivergence.pairwise_distance_matrix, which had their own copies of the scaling. Thereturn_joint_validparameter is gone, because no caller needs it now.Left as they are, on purpose
pc_disttopcoasnippet inquickstart.rstandexamples/local_pca.py. Their SNP windows always have enough variants, so a NaN row cannot occur there.dist_momentsstill computes on the host with NumPy. That is dist_moments computes the distance moments with NumPy on the host #336.Docs
The changelog,
missing_data.rst,features.rstand the local PCA tutorial describe the NaN rule. The tutorial now filters the NaNmdsrows before it plots, and its corner indices count all windows, aslostruct's do.Tests
tests/test_undefined_pair_distances.pycovers:pairwise_distancefor each metric, and thepcoaerrors;missing_data='exclude';lostructon three engine paths, including the jackknife, and its guards.Each new test fails on
main. Test references that used 0.0 for an undefined pair now use NaN.Full suite: 2034 passed, 81 skipped, 15 xfailed.
ruff check pg_gpu/ tests/ examples/passes.