@jbrowse/plugin-variants 4.0.3 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/esm/ChordVariantDisplay/models/stateModelFactory.d.ts +9 -0
  2. package/esm/LDDisplay/SharedLDConfigSchema.d.ts +102 -0
  3. package/esm/LDDisplay/SharedLDConfigSchema.js +83 -0
  4. package/esm/LDDisplay/afterAttach.d.ts +2 -0
  5. package/esm/LDDisplay/afterAttach.js +123 -0
  6. package/esm/LDDisplay/components/BaseDisplayComponent.d.ts +15 -0
  7. package/esm/LDDisplay/components/BaseDisplayComponent.js +39 -0
  8. package/esm/LDDisplay/components/LDColorLegend.d.ts +15 -0
  9. package/esm/LDDisplay/components/LDColorLegend.js +75 -0
  10. package/esm/LDDisplay/components/LDDisplayComponent.d.ts +5 -0
  11. package/esm/LDDisplay/components/LDDisplayComponent.js +203 -0
  12. package/esm/LDDisplay/components/LinesConnectingMatrixToGenomicPosition.d.ts +16 -0
  13. package/esm/LDDisplay/components/LinesConnectingMatrixToGenomicPosition.js +109 -0
  14. package/esm/LDDisplay/configSchema1.d.ts +115 -0
  15. package/esm/LDDisplay/configSchema1.js +16 -0
  16. package/esm/LDDisplay/configSchema2.d.ts +115 -0
  17. package/esm/LDDisplay/configSchema2.js +16 -0
  18. package/esm/LDDisplay/index.d.ts +2 -0
  19. package/esm/LDDisplay/index.js +35 -0
  20. package/esm/LDDisplay/renderSvg.d.ts +3 -0
  21. package/esm/LDDisplay/renderSvg.js +36 -0
  22. package/esm/LDDisplay/shared.d.ts +367 -0
  23. package/esm/LDDisplay/shared.js +467 -0
  24. package/esm/LDDisplay/stateModel1.d.ts +365 -0
  25. package/esm/LDDisplay/stateModel1.js +10 -0
  26. package/esm/LDDisplay/stateModel2.d.ts +365 -0
  27. package/esm/LDDisplay/stateModel2.js +10 -0
  28. package/esm/LDRenderer/LDRenderer.d.ts +30 -0
  29. package/esm/LDRenderer/LDRenderer.js +109 -0
  30. package/esm/LDRenderer/components/LDRendering.d.ts +2 -0
  31. package/esm/LDRenderer/components/LDRendering.js +4 -0
  32. package/esm/LDRenderer/configSchema.d.ts +8 -0
  33. package/esm/LDRenderer/configSchema.js +10 -0
  34. package/esm/LDRenderer/index.d.ts +2 -0
  35. package/esm/LDRenderer/index.js +11 -0
  36. package/esm/LDRenderer/makeImageData.d.ts +20 -0
  37. package/esm/LDRenderer/makeImageData.js +158 -0
  38. package/esm/LDRenderer/types.d.ts +8 -0
  39. package/esm/LDRenderer/types.js +1 -0
  40. package/esm/LDTrack/configSchema.d.ts +85 -0
  41. package/esm/LDTrack/configSchema.js +7 -0
  42. package/esm/LDTrack/index.d.ts +2 -0
  43. package/esm/LDTrack/index.js +14 -0
  44. package/esm/LinearVariantDisplay/model.d.ts +139 -42
  45. package/esm/LinearVariantDisplay/model.js +46 -8
  46. package/esm/MultiLinearVariantDisplay/configSchema.d.ts +27 -1
  47. package/esm/MultiLinearVariantDisplay/model.d.ts +2647 -58
  48. package/esm/MultiLinearVariantDisplay/model.js +6 -0
  49. package/esm/MultiLinearVariantDisplay/renderSvg.d.ts +10 -2
  50. package/esm/MultiLinearVariantMatrixDisplay/configSchema.d.ts +25 -0
  51. package/esm/MultiLinearVariantMatrixDisplay/configSchema.js +26 -0
  52. package/esm/MultiLinearVariantMatrixDisplay/model.d.ts +2648 -59
  53. package/esm/MultiLinearVariantMatrixDisplay/model.js +6 -0
  54. package/esm/MultiLinearVariantMatrixRenderer/MultiLinearVariantMatrixRenderer.d.ts +2 -2
  55. package/esm/MultiLinearVariantMatrixRenderer/MultiLinearVariantMatrixRenderer.js +11 -9
  56. package/esm/MultiLinearVariantMatrixRenderer/components/MultiLinearVariantMatrixRendering.d.ts +8 -0
  57. package/esm/MultiLinearVariantMatrixRenderer/components/MultiLinearVariantMatrixRendering.js +14 -2
  58. package/esm/MultiLinearVariantMatrixRenderer/makeImageData.js +14 -8
  59. package/esm/MultiLinearVariantRenderer/MultiVariantRenderer.d.ts +2 -2
  60. package/esm/MultiLinearVariantRenderer/MultiVariantRenderer.js +4 -3
  61. package/esm/MultiLinearVariantRenderer/components/MultiLinearVariantRendering.d.ts +4 -0
  62. package/esm/MultiLinearVariantRenderer/components/MultiLinearVariantRendering.js +23 -2
  63. package/esm/MultiLinearVariantRenderer/makeImageData.js +8 -3
  64. package/esm/PlinkLDAdapter/PlinkLDAdapter.d.ts +25 -0
  65. package/esm/PlinkLDAdapter/PlinkLDAdapter.js +147 -0
  66. package/esm/PlinkLDAdapter/PlinkLDTabixAdapter.d.ts +24 -0
  67. package/esm/PlinkLDAdapter/PlinkLDTabixAdapter.js +156 -0
  68. package/esm/PlinkLDAdapter/configSchema.d.ts +10 -0
  69. package/esm/PlinkLDAdapter/configSchema.js +25 -0
  70. package/esm/PlinkLDAdapter/configSchemaTabix.d.ts +24 -0
  71. package/esm/PlinkLDAdapter/configSchemaTabix.js +46 -0
  72. package/esm/PlinkLDAdapter/index.d.ts +2 -0
  73. package/esm/PlinkLDAdapter/index.js +25 -0
  74. package/esm/PlinkLDAdapter/types.d.ts +29 -0
  75. package/esm/PlinkLDAdapter/types.js +1 -0
  76. package/esm/VariantFeatureWidget/VariantSampleGrid/VariantSampleGrid.js +1 -1
  77. package/esm/VariantRPC/MultiVariantGetFeatureDetails.d.ts +14 -0
  78. package/esm/VariantRPC/MultiVariantGetFeatureDetails.js +15 -0
  79. package/esm/VariantRPC/executeClusterGenotypeMatrix.d.ts +3 -1
  80. package/esm/VariantRPC/executeClusterGenotypeMatrix.js +8 -4
  81. package/esm/VariantRPC/getLDMatrix.d.ts +48 -0
  82. package/esm/VariantRPC/getLDMatrix.js +388 -0
  83. package/esm/VariantRPC/getLDMatrixFromPlink.d.ts +16 -0
  84. package/esm/VariantRPC/getLDMatrixFromPlink.js +105 -0
  85. package/esm/VariantRPC/getPhasedGenotypeMatrix.d.ts +20 -0
  86. package/esm/VariantRPC/getPhasedGenotypeMatrix.js +50 -0
  87. package/esm/VariantRPC/types.d.ts +3 -0
  88. package/esm/VcfAdapter/VcfAdapter.d.ts +1 -1
  89. package/esm/VcfAdapter/VcfAdapter.js +1 -2
  90. package/esm/VcfExtensionPoints/index.js +29 -3
  91. package/esm/VcfFeature/index.d.ts +2 -1
  92. package/esm/VcfFeature/index.js +4 -2
  93. package/esm/index.d.ts +1 -0
  94. package/esm/index.js +23 -0
  95. package/esm/shared/MultiVariantBaseModel.d.ts +2638 -53
  96. package/esm/shared/MultiVariantBaseModel.js +100 -47
  97. package/esm/shared/SharedVariantConfigSchema.d.ts +27 -1
  98. package/esm/shared/SharedVariantConfigSchema.js +28 -1
  99. package/esm/shared/VariantFeatureCache.d.ts +27 -0
  100. package/esm/shared/VariantFeatureCache.js +48 -0
  101. package/esm/shared/VariantRendererType.d.ts +23 -0
  102. package/esm/shared/VariantRendererType.js +15 -0
  103. package/esm/shared/applyColorPalette.d.ts +9 -0
  104. package/esm/shared/applyColorPalette.js +23 -0
  105. package/esm/shared/colorByAutorun.d.ts +10 -0
  106. package/esm/shared/colorByAutorun.js +39 -0
  107. package/esm/shared/components/AddFiltersDialog.d.ts +3 -3
  108. package/esm/shared/components/AddFiltersDialog.js +29 -22
  109. package/esm/shared/components/LDFilterDialog.d.ts +13 -0
  110. package/esm/shared/components/LDFilterDialog.js +102 -0
  111. package/esm/shared/components/MAFFilterDialog.js +23 -16
  112. package/esm/shared/components/MultiVariantClusterDialog/ClusterDialogAuto.js +19 -6
  113. package/esm/shared/components/MultiVariantClusterDialog/types.d.ts +4 -1
  114. package/esm/shared/components/MultiVariantColorLegend.js +4 -4
  115. package/esm/shared/components/MultiVariantLegendBar.js +1 -1
  116. package/esm/shared/components/MultiVariantTooltip.d.ts +1 -0
  117. package/esm/shared/components/MultiVariantTooltip.js +2 -2
  118. package/esm/shared/components/RecombinationTrack.d.ts +21 -0
  119. package/esm/shared/components/RecombinationTrack.js +54 -0
  120. package/esm/shared/components/RecombinationYScaleBar.d.ts +7 -0
  121. package/esm/shared/components/RecombinationYScaleBar.js +34 -0
  122. package/esm/shared/components/RectBg.js +1 -1
  123. package/esm/shared/components/SetColorDialogRowPalettizer.d.ts +3 -8
  124. package/esm/shared/components/SetColorDialogRowPalettizer.js +2 -14
  125. package/esm/shared/components/SourcesDataGrid.js +4 -4
  126. package/esm/shared/components/TreeSidebar.js +11 -1
  127. package/esm/shared/drawAlleleCount.js +9 -0
  128. package/esm/shared/drawPhased.d.ts +1 -1
  129. package/esm/shared/drawPhased.js +31 -2
  130. package/esm/shared/getMultiVariantFeaturesAutorun.d.ts +1 -0
  131. package/esm/shared/getMultiVariantFeaturesAutorun.js +3 -0
  132. package/esm/shared/getMultiVariantSourcesAutorun.d.ts +1 -0
  133. package/esm/shared/getMultiVariantSourcesAutorun.js +3 -0
  134. package/esm/shared/getSources.d.ts +5 -9
  135. package/esm/shared/getSources.js +30 -25
  136. package/esm/shared/mafFilterUtils.d.ts +5 -0
  137. package/esm/shared/mafFilterUtils.js +17 -0
  138. package/esm/shared/minorAlleleFrequencyUtils.d.ts +2 -0
  139. package/esm/shared/minorAlleleFrequencyUtils.js +259 -17
  140. package/esm/shared/setupMultiVariantAutoruns.js +2 -0
  141. package/esm/shared/treeDrawingAutorun.d.ts +1 -0
  142. package/esm/shared/treeDrawingAutorun.js +7 -1
  143. package/esm/shared/types.d.ts +1 -2
  144. package/package.json +12 -11
@@ -0,0 +1,25 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ function x() { }
3
+ const PlinkLDAdapter = ConfigurationSchema('PlinkLDAdapter', {
4
+ ldLocation: {
5
+ type: 'fileLocation',
6
+ defaultValue: {
7
+ uri: '/path/to/plink.ld',
8
+ locationType: 'UriLocation',
9
+ },
10
+ },
11
+ }, {
12
+ explicitlyTyped: true,
13
+ preProcessSnapshot: snap => {
14
+ return snap.uri
15
+ ? {
16
+ ...snap,
17
+ ldLocation: {
18
+ uri: snap.uri,
19
+ baseUri: snap.baseUri,
20
+ },
21
+ }
22
+ : snap;
23
+ },
24
+ });
25
+ export default PlinkLDAdapter;
@@ -0,0 +1,24 @@
1
+ declare const PlinkLDTabixAdapter: import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
2
+ ldLocation: {
3
+ type: string;
4
+ defaultValue: {
5
+ uri: string;
6
+ locationType: string;
7
+ };
8
+ };
9
+ index: import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
10
+ indexType: {
11
+ model: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
12
+ type: string;
13
+ defaultValue: string;
14
+ };
15
+ location: {
16
+ type: string;
17
+ defaultValue: {
18
+ uri: string;
19
+ locationType: string;
20
+ };
21
+ };
22
+ }, import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
23
+ }, import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
24
+ export default PlinkLDTabixAdapter;
@@ -0,0 +1,46 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ import { types } from '@jbrowse/mobx-state-tree';
3
+ function x() { }
4
+ const PlinkLDTabixAdapter = ConfigurationSchema('PlinkLDTabixAdapter', {
5
+ ldLocation: {
6
+ type: 'fileLocation',
7
+ defaultValue: {
8
+ uri: '/path/to/plink.ld.gz',
9
+ locationType: 'UriLocation',
10
+ },
11
+ },
12
+ index: ConfigurationSchema('PlinkLDTabixIndex', {
13
+ indexType: {
14
+ model: types.enumeration('IndexType', ['TBI', 'CSI']),
15
+ type: 'stringEnum',
16
+ defaultValue: 'TBI',
17
+ },
18
+ location: {
19
+ type: 'fileLocation',
20
+ defaultValue: {
21
+ uri: '/path/to/plink.ld.gz.tbi',
22
+ locationType: 'UriLocation',
23
+ },
24
+ },
25
+ }),
26
+ }, {
27
+ explicitlyTyped: true,
28
+ preProcessSnapshot: snap => {
29
+ return snap.uri
30
+ ? {
31
+ ...snap,
32
+ ldLocation: {
33
+ uri: snap.uri,
34
+ baseUri: snap.baseUri,
35
+ },
36
+ index: {
37
+ location: {
38
+ uri: `${snap.uri}.tbi`,
39
+ baseUri: snap.baseUri,
40
+ },
41
+ },
42
+ }
43
+ : snap;
44
+ },
45
+ });
46
+ export default PlinkLDTabixAdapter;
@@ -0,0 +1,2 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ export default function PlinkLDAdapterF(pluginManager: PluginManager): void;
@@ -0,0 +1,25 @@
1
+ import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType';
2
+ import configSchema from "./configSchema.js";
3
+ import configSchemaTabix from "./configSchemaTabix.js";
4
+ export default function PlinkLDAdapterF(pluginManager) {
5
+ pluginManager.addAdapterType(() => new AdapterType({
6
+ name: 'PlinkLDAdapter',
7
+ displayName: 'PLINK LD adapter',
8
+ configSchema,
9
+ adapterMetadata: {
10
+ category: 'Linkage disequilibrium',
11
+ description: 'Adapter for pre-computed LD data from PLINK --r2 output (loads into memory)',
12
+ },
13
+ getAdapterClass: () => import("./PlinkLDAdapter.js").then(r => r.default),
14
+ }));
15
+ pluginManager.addAdapterType(() => new AdapterType({
16
+ name: 'PlinkLDTabixAdapter',
17
+ displayName: 'PLINK LD tabix adapter',
18
+ configSchema: configSchemaTabix,
19
+ adapterMetadata: {
20
+ category: 'Linkage disequilibrium',
21
+ description: 'Adapter for pre-computed LD data from PLINK --r2 output (tabix-indexed)',
22
+ },
23
+ getAdapterClass: () => import("./PlinkLDTabixAdapter.js").then(r => r.default),
24
+ }));
25
+ }
@@ -0,0 +1,29 @@
1
+ export interface PlinkLDRecord {
2
+ chrA: string;
3
+ bpA: number;
4
+ snpA: string;
5
+ chrB: string;
6
+ bpB: number;
7
+ snpB: string;
8
+ r2: number;
9
+ dprime?: number;
10
+ mafA?: number;
11
+ mafB?: number;
12
+ }
13
+ export interface PlinkLDHeader {
14
+ columns: string[];
15
+ hasR2: boolean;
16
+ hasDprime: boolean;
17
+ hasMafA: boolean;
18
+ hasMafB: boolean;
19
+ chrAIdx: number;
20
+ bpAIdx: number;
21
+ snpAIdx: number;
22
+ chrBIdx: number;
23
+ bpBIdx: number;
24
+ snpBIdx: number;
25
+ r2Idx: number;
26
+ dprimeIdx: number;
27
+ mafAIdx: number;
28
+ mafBIdx: number;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -43,7 +43,7 @@ export default function VariantSampleGrid(props) {
43
43
  }, [rows, filteredRows, descriptions]);
44
44
  return !rows.length ? null : (_jsxs(BaseCard, { ...props, title: "Samples", children: [error ? _jsx(Typography, { color: "error", children: `${error}` }) : null, _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(CascadingMenuButton, { menuItems: [
45
45
  {
46
- label: 'Show allele counts ("dosage") instead of exact GT',
46
+ label: 'Show allele counts ("dosage")',
47
47
  helpText: 'This converts a genotype like 1/1 into 1:2 which says there were two occurrences of the ALT allele 1 in the genotype',
48
48
  type: 'checkbox',
49
49
  checked: useCounts,
@@ -0,0 +1,14 @@
1
+ import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType';
2
+ interface GetFeatureArgs {
3
+ featureId: string;
4
+ sessionId: string;
5
+ trackInstanceId: string;
6
+ rendererType: string;
7
+ }
8
+ export declare class MultiVariantGetFeatureDetails extends RpcMethodType {
9
+ name: string;
10
+ execute(args: GetFeatureArgs): Promise<{
11
+ feature: import("@jbrowse/core/util").SimpleFeatureSerialized | undefined;
12
+ }>;
13
+ }
14
+ export {};
@@ -0,0 +1,15 @@
1
+ import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType';
2
+ export class MultiVariantGetFeatureDetails extends RpcMethodType {
3
+ name = 'MultiVariantGetFeatureDetails';
4
+ async execute(args) {
5
+ const { featureId, sessionId, trackInstanceId, rendererType } = args;
6
+ const RendererType = this.pluginManager.getRendererType(rendererType);
7
+ const feature = RendererType.getFeatureById(featureId, {
8
+ sessionId,
9
+ trackInstanceId,
10
+ });
11
+ return {
12
+ feature: feature?.toJSON(),
13
+ };
14
+ }
15
+ }
@@ -1,4 +1,4 @@
1
- import type { Source } from '../shared/types.ts';
1
+ import type { SampleInfo, Source } from '../shared/types.ts';
2
2
  import type PluginManager from '@jbrowse/core/PluginManager';
3
3
  import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
4
4
  import type { Region, StopToken } from '@jbrowse/core/util';
@@ -15,6 +15,8 @@ export declare function executeClusterGenotypeMatrix({ pluginManager, args, }: {
15
15
  lengthCutoffFilter: number;
16
16
  statusCallback: (arg: string) => void;
17
17
  sources: Source[];
18
+ renderingMode?: string;
19
+ sampleInfo?: Record<string, SampleInfo>;
18
20
  };
19
21
  }): Promise<{
20
22
  order: number[];
@@ -1,10 +1,14 @@
1
1
  import { clusterData, toNewick } from '@gmod/hclust';
2
2
  import { getGenotypeMatrix } from "./getGenotypeMatrix.js";
3
+ import { getPhasedGenotypeMatrix } from "./getPhasedGenotypeMatrix.js";
3
4
  export async function executeClusterGenotypeMatrix({ pluginManager, args, }) {
4
- const matrix = await getGenotypeMatrix({
5
- pluginManager,
6
- args,
7
- });
5
+ const { renderingMode, sampleInfo } = args;
6
+ const matrix = renderingMode === 'phased' && sampleInfo
7
+ ? await getPhasedGenotypeMatrix({
8
+ pluginManager,
9
+ args: { ...args, sampleInfo },
10
+ })
11
+ : await getGenotypeMatrix({ pluginManager, args });
8
12
  const sampleLabels = Object.keys(matrix);
9
13
  const result = await clusterData({
10
14
  data: Object.values(matrix),
@@ -0,0 +1,48 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ import type { Region } from '@jbrowse/core/util';
4
+ import type { StopToken } from '@jbrowse/core/util/stopToken';
5
+ export type LDMetric = 'r2' | 'dprime';
6
+ export interface FilterStats {
7
+ totalVariants: number;
8
+ passedVariants: number;
9
+ filteredByMaf: number;
10
+ filteredByLength: number;
11
+ filteredByMultiallelic: number;
12
+ filteredByHwe: number;
13
+ filteredByCallRate: number;
14
+ }
15
+ export interface RecombinationData {
16
+ values: Float32Array;
17
+ positions: number[];
18
+ }
19
+ export interface LDMatrixResult {
20
+ snps: {
21
+ id: string;
22
+ refName: string;
23
+ start: number;
24
+ end: number;
25
+ }[];
26
+ ldValues: Float32Array;
27
+ metric: LDMetric;
28
+ filterStats: FilterStats;
29
+ recombination: RecombinationData;
30
+ }
31
+ export declare function getLDMatrix({ pluginManager, args, }: {
32
+ pluginManager: PluginManager;
33
+ args: {
34
+ adapterConfig: AnyConfigurationModel;
35
+ stopToken?: StopToken;
36
+ sessionId: string;
37
+ headers?: Record<string, string>;
38
+ regions: Region[];
39
+ bpPerPx: number;
40
+ minorAlleleFrequencyFilter: number;
41
+ lengthCutoffFilter: number;
42
+ hweFilterThreshold?: number;
43
+ callRateFilter?: number;
44
+ jexlFilters?: string[];
45
+ ldMetric?: LDMetric;
46
+ signedLD?: boolean;
47
+ };
48
+ }): Promise<LDMatrixResult>;
@@ -0,0 +1,388 @@
1
+ import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache';
2
+ import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/renderers/util/serializableFilterChain';
3
+ import { checkStopToken2, createStopTokenChecker, } from '@jbrowse/core/util/stopToken';
4
+ import { firstValueFrom, toArray } from 'rxjs';
5
+ import { getFeaturesThatPassMinorAlleleFrequencyFilter } from "../shared/minorAlleleFrequencyUtils.js";
6
+ const SPLITTER = /[/|]/;
7
+ function isPhased(genotypes) {
8
+ const firstVal = Object.values(genotypes)[0];
9
+ return firstVal?.includes('|') ?? false;
10
+ }
11
+ function calculateDprime(D, pA, pB, signedLD) {
12
+ const qA = 1 - pA;
13
+ const qB = 1 - pB;
14
+ if (D > 0) {
15
+ const Dmax = Math.min(pA * qB, qA * pB);
16
+ if (Dmax > 0) {
17
+ return Math.min(1, D / Dmax);
18
+ }
19
+ }
20
+ else if (D < 0) {
21
+ const absDmin = Math.min(pA * pB, qA * qB);
22
+ if (absDmin > 0) {
23
+ return signedLD
24
+ ? Math.max(-1, D / absDmin)
25
+ : Math.min(1, Math.abs(D) / absDmin);
26
+ }
27
+ }
28
+ return 0;
29
+ }
30
+ function getChiSquareCritical(pValue) {
31
+ if (pValue <= 0) {
32
+ return Number.POSITIVE_INFINITY;
33
+ }
34
+ if (pValue >= 1) {
35
+ return 0;
36
+ }
37
+ if (pValue === 0.05) {
38
+ return 3.841;
39
+ }
40
+ if (pValue === 0.01) {
41
+ return 6.635;
42
+ }
43
+ if (pValue === 0.001) {
44
+ return 10.828;
45
+ }
46
+ if (pValue === 0.0001) {
47
+ return 15.137;
48
+ }
49
+ return -2 * Math.log(pValue);
50
+ }
51
+ function encodeGenotypes(genotypes, samples, splitCache) {
52
+ const encoded = new Int8Array(samples.length);
53
+ for (const [i, sample] of samples.entries()) {
54
+ const val = genotypes[sample];
55
+ const alleles = splitCache[val] ?? (splitCache[val] = val.split(SPLITTER));
56
+ let nonRefCount = 0;
57
+ let uncalledCount = 0;
58
+ for (const allele of alleles) {
59
+ if (allele === '.') {
60
+ uncalledCount++;
61
+ }
62
+ else if (allele !== '0') {
63
+ nonRefCount++;
64
+ }
65
+ }
66
+ if (uncalledCount === alleles.length) {
67
+ encoded[i] = -1;
68
+ }
69
+ else if (nonRefCount === 0) {
70
+ encoded[i] = 0;
71
+ }
72
+ else if (nonRefCount === alleles.length) {
73
+ encoded[i] = 2;
74
+ }
75
+ else {
76
+ encoded[i] = 1;
77
+ }
78
+ }
79
+ return encoded;
80
+ }
81
+ function encodePhasedHaplotypes(genotypes, samples) {
82
+ const hap1 = new Int8Array(samples.length);
83
+ const hap2 = new Int8Array(samples.length);
84
+ for (const [i, sample] of samples.entries()) {
85
+ const val = genotypes[sample];
86
+ const alleles = val.split('|');
87
+ if (alleles.length !== 2) {
88
+ hap1[i] = -1;
89
+ hap2[i] = -1;
90
+ continue;
91
+ }
92
+ if (alleles[0] === '.') {
93
+ hap1[i] = -1;
94
+ }
95
+ else {
96
+ hap1[i] = alleles[0] === '0' ? 0 : 1;
97
+ }
98
+ if (alleles[1] === '.') {
99
+ hap2[i] = -1;
100
+ }
101
+ else {
102
+ hap2[i] = alleles[1] === '0' ? 0 : 1;
103
+ }
104
+ }
105
+ return { hap1, hap2 };
106
+ }
107
+ function calculateLDStatsPhased(haps1, haps2, signedLD = false) {
108
+ let n01 = 0;
109
+ let n10 = 0;
110
+ let n11 = 0;
111
+ let total = 0;
112
+ const numSamples = haps1.hap1.length;
113
+ for (let i = 0; i < numSamples; i++) {
114
+ const a1 = haps1.hap1[i];
115
+ const b1 = haps2.hap1[i];
116
+ if (a1 >= 0 && b1 >= 0) {
117
+ if (a1 === 0 && b1 === 1) {
118
+ n01++;
119
+ }
120
+ else if (a1 === 1 && b1 === 0) {
121
+ n10++;
122
+ }
123
+ else if (a1 === 1 && b1 === 1) {
124
+ n11++;
125
+ }
126
+ total++;
127
+ }
128
+ }
129
+ for (let i = 0; i < numSamples; i++) {
130
+ const a2 = haps1.hap2[i];
131
+ const b2 = haps2.hap2[i];
132
+ if (a2 >= 0 && b2 >= 0) {
133
+ if (a2 === 0 && b2 === 1) {
134
+ n01++;
135
+ }
136
+ else if (a2 === 1 && b2 === 0) {
137
+ n10++;
138
+ }
139
+ else if (a2 === 1 && b2 === 1) {
140
+ n11++;
141
+ }
142
+ total++;
143
+ }
144
+ }
145
+ if (total < 4) {
146
+ return { r2: 0, dprime: 0 };
147
+ }
148
+ const p01 = n01 / total;
149
+ const p10 = n10 / total;
150
+ const p11 = n11 / total;
151
+ const pA = p10 + p11;
152
+ const pB = p01 + p11;
153
+ const qA = 1 - pA;
154
+ const qB = 1 - pB;
155
+ if (pA <= 0 || pA >= 1 || pB <= 0 || pB >= 1) {
156
+ return { r2: 0, dprime: 0 };
157
+ }
158
+ const D = p11 - pA * pB;
159
+ const denom = pA * qA * pB * qB;
160
+ const r = denom > 0 ? D / Math.sqrt(denom) : 0;
161
+ const r2 = Math.min(1, Math.max(0, r * r));
162
+ const dprime = calculateDprime(D, pA, pB, signedLD);
163
+ return { r2: signedLD ? r : r2, dprime };
164
+ }
165
+ function calculateLDStats(geno1, geno2, signedLD = false) {
166
+ let n = 0;
167
+ let sumG1 = 0;
168
+ let sumG2 = 0;
169
+ let sumG1sq = 0;
170
+ let sumG2sq = 0;
171
+ let sumProd = 0;
172
+ for (const [i, element] of geno1.entries()) {
173
+ const g1 = element;
174
+ const g2 = geno2[i];
175
+ if (g1 >= 0 && g2 >= 0) {
176
+ n++;
177
+ sumG1 += g1;
178
+ sumG2 += g2;
179
+ sumG1sq += g1 * g1;
180
+ sumG2sq += g2 * g2;
181
+ sumProd += g1 * g2;
182
+ }
183
+ }
184
+ if (n < 2) {
185
+ return { r2: 0, dprime: 0 };
186
+ }
187
+ const pA = sumG1 / (2 * n);
188
+ const pB = sumG2 / (2 * n);
189
+ if (pA <= 0 || pA >= 1 || pB <= 0 || pB >= 1) {
190
+ return { r2: 0, dprime: 0 };
191
+ }
192
+ const mean1 = sumG1 / n;
193
+ const mean2 = sumG2 / n;
194
+ const var1 = sumG1sq / n - mean1 * mean1;
195
+ const var2 = sumG2sq / n - mean2 * mean2;
196
+ let r = 0;
197
+ let r2 = 0;
198
+ if (var1 > 0 && var2 > 0) {
199
+ const cov = sumProd / n - mean1 * mean2;
200
+ r = cov / Math.sqrt(var1 * var2);
201
+ r2 = Math.min(1, Math.max(0, r * r));
202
+ }
203
+ const covG = sumProd / n - mean1 * mean2;
204
+ const D = covG / 2;
205
+ const dprime = calculateDprime(D, pA, pB, signedLD);
206
+ return { r2: signedLD ? r : r2, dprime };
207
+ }
208
+ export async function getLDMatrix({ pluginManager, args, }) {
209
+ const { minorAlleleFrequencyFilter, regions, adapterConfig, sessionId, lengthCutoffFilter, hweFilterThreshold = 0.001, callRateFilter = 0, jexlFilters = [], stopToken, ldMetric = 'r2', signedLD = false, } = args;
210
+ const lastCheck = createStopTokenChecker(stopToken);
211
+ const adapter = await getAdapter(pluginManager, sessionId, adapterConfig);
212
+ const dataAdapter = adapter.dataAdapter;
213
+ const sources = await dataAdapter.getSources(regions);
214
+ const samples = sources.map(s => s.name);
215
+ if (samples.length === 0) {
216
+ return {
217
+ snps: [],
218
+ ldValues: new Float32Array(0),
219
+ metric: ldMetric,
220
+ filterStats: {
221
+ totalVariants: 0,
222
+ passedVariants: 0,
223
+ filteredByMaf: 0,
224
+ filteredByLength: 0,
225
+ filteredByMultiallelic: 0,
226
+ filteredByHwe: 0,
227
+ filteredByCallRate: 0,
228
+ },
229
+ recombination: {
230
+ values: new Float32Array(0),
231
+ positions: [],
232
+ },
233
+ };
234
+ }
235
+ const splitCache = {};
236
+ const rawFeatures = await firstValueFrom(dataAdapter.getFeaturesInMultipleRegions(regions, args).pipe(toArray()));
237
+ const totalVariants = rawFeatures.length;
238
+ const filteredFeatures = getFeaturesThatPassMinorAlleleFrequencyFilter({
239
+ minorAlleleFrequencyFilter,
240
+ lengthCutoffFilter,
241
+ lastCheck,
242
+ splitCache,
243
+ features: rawFeatures,
244
+ });
245
+ let filteredByLength = 0;
246
+ let filteredByMaf = 0;
247
+ for (const feature of rawFeatures) {
248
+ if (feature.get('end') - feature.get('start') > lengthCutoffFilter) {
249
+ filteredByLength++;
250
+ }
251
+ else {
252
+ const featureId = feature.id();
253
+ const wasPassed = filteredFeatures.some(f => f.feature.id() === featureId);
254
+ if (!wasPassed) {
255
+ filteredByMaf++;
256
+ }
257
+ }
258
+ }
259
+ let featuresAfterJexl = filteredFeatures;
260
+ if (jexlFilters.length > 0) {
261
+ const filterChain = new SerializableFilterChain({ filters: jexlFilters });
262
+ featuresAfterJexl = filteredFeatures.filter(({ feature }) => filterChain.passes(feature, undefined, undefined));
263
+ }
264
+ const snps = [];
265
+ const encodedGenotypes = [];
266
+ const phasedHaplotypes = [];
267
+ let filteredByMultiallelic = 0;
268
+ let filteredByHwe = 0;
269
+ let filteredByCallRate = 0;
270
+ const callRateFilterEnabled = callRateFilter > 0;
271
+ let dataIsPhased = false;
272
+ if (featuresAfterJexl.length > 0) {
273
+ const firstGenotypes = featuresAfterJexl[0].feature.get('genotypes');
274
+ dataIsPhased = isPhased(firstGenotypes);
275
+ }
276
+ const chiSqCritical = getChiSquareCritical(hweFilterThreshold);
277
+ const hweFilterEnabled = hweFilterThreshold > 0;
278
+ for (const { feature } of featuresAfterJexl) {
279
+ const alt = feature.get('ALT');
280
+ if (alt && alt.length > 1) {
281
+ filteredByMultiallelic++;
282
+ continue;
283
+ }
284
+ const genotypes = feature.get('genotypes');
285
+ const encoded = encodeGenotypes(genotypes, samples, splitCache);
286
+ let nHomRef = 0;
287
+ let nHet = 0;
288
+ let nHomAlt = 0;
289
+ let nValid = 0;
290
+ for (const g of encoded) {
291
+ if (g === 0) {
292
+ nHomRef++;
293
+ nValid++;
294
+ }
295
+ else if (g === 1) {
296
+ nHet++;
297
+ nValid++;
298
+ }
299
+ else if (g === 2) {
300
+ nHomAlt++;
301
+ nValid++;
302
+ }
303
+ }
304
+ if (callRateFilterEnabled) {
305
+ const callRate = nValid / samples.length;
306
+ if (callRate < callRateFilter) {
307
+ filteredByCallRate++;
308
+ continue;
309
+ }
310
+ }
311
+ if (hweFilterEnabled && nValid > 0) {
312
+ const p = (2 * nHomRef + nHet) / (2 * nValid);
313
+ const q = 1 - p;
314
+ const expectedHomRef = p * p * nValid;
315
+ const expectedHet = 2 * p * q * nValid;
316
+ const expectedHomAlt = q * q * nValid;
317
+ let chiSq = 0;
318
+ if (expectedHomRef > 0) {
319
+ chiSq += (nHomRef - expectedHomRef) ** 2 / expectedHomRef;
320
+ }
321
+ if (expectedHet > 0) {
322
+ chiSq += (nHet - expectedHet) ** 2 / expectedHet;
323
+ }
324
+ if (expectedHomAlt > 0) {
325
+ chiSq += (nHomAlt - expectedHomAlt) ** 2 / expectedHomAlt;
326
+ }
327
+ if (chiSq > chiSqCritical) {
328
+ filteredByHwe++;
329
+ continue;
330
+ }
331
+ }
332
+ snps.push({
333
+ id: feature.get('name') || feature.id(),
334
+ refName: feature.get('refName'),
335
+ start: feature.get('start'),
336
+ end: feature.get('end'),
337
+ });
338
+ encodedGenotypes.push(encoded);
339
+ if (dataIsPhased) {
340
+ phasedHaplotypes.push(encodePhasedHaplotypes(genotypes, samples));
341
+ }
342
+ checkStopToken2(lastCheck);
343
+ }
344
+ const n = snps.length;
345
+ const ldSize = (n * (n - 1)) / 2;
346
+ const ldValues = new Float32Array(ldSize);
347
+ let idx = 0;
348
+ for (let i = 1; i < n; i++) {
349
+ for (let j = 0; j < i; j++) {
350
+ const stats = dataIsPhased
351
+ ? calculateLDStatsPhased(phasedHaplotypes[i], phasedHaplotypes[j], signedLD)
352
+ : calculateLDStats(encodedGenotypes[i], encodedGenotypes[j], signedLD);
353
+ ldValues[idx++] = ldMetric === 'dprime' ? stats.dprime : stats.r2;
354
+ }
355
+ checkStopToken2(lastCheck);
356
+ }
357
+ const filterStats = {
358
+ totalVariants,
359
+ passedVariants: snps.length,
360
+ filteredByMaf,
361
+ filteredByLength,
362
+ filteredByMultiallelic,
363
+ filteredByHwe,
364
+ filteredByCallRate,
365
+ };
366
+ const recombValues = new Float32Array(Math.max(0, n - 1));
367
+ const recombPositions = [];
368
+ for (let i = 0; i < n - 1; i++) {
369
+ let r2;
370
+ if (dataIsPhased) {
371
+ const stats = calculateLDStatsPhased(phasedHaplotypes[i], phasedHaplotypes[i + 1]);
372
+ r2 = stats.r2;
373
+ }
374
+ else {
375
+ const stats = calculateLDStats(encodedGenotypes[i], encodedGenotypes[i + 1]);
376
+ r2 = stats.r2;
377
+ }
378
+ recombValues[i] = 1 - r2;
379
+ const pos1 = snps[i].start;
380
+ const pos2 = snps[i + 1].start;
381
+ recombPositions.push((pos1 + pos2) / 2);
382
+ }
383
+ const recombination = {
384
+ values: recombValues,
385
+ positions: recombPositions,
386
+ };
387
+ return { snps, ldValues, metric: ldMetric, filterStats, recombination };
388
+ }