@datagrok/proteomics 1.2.0 → 1.3.0
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.
- package/CHANGELOG.md +16 -0
- package/README.md +18 -5
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/docs/personas-and-capabilities.md +16 -0
- package/docs/publishing-design-rationale.md +140 -0
- package/package.json +20 -5
- package/package.png +0 -0
- package/src/analysis/imputation.ts +3 -1
- package/src/analysis/normalization.ts +9 -4
- package/src/analysis/spc-storage.ts +1 -1
- package/src/menu.ts +5 -2
- package/src/package-api.ts +10 -2
- package/src/package-test.ts +3 -1
- package/src/package.g.ts +17 -3
- package/src/package.ts +31 -15
- package/src/panels/published-analysis-panel.ts +2 -2
- package/src/publishing/assert-published-shape.ts +2 -2
- package/src/publishing/package-settings-editor.ts +125 -0
- package/src/publishing/publish-project.ts +8 -8
- package/src/publishing/publish-settings.ts +57 -3
- package/src/publishing/publish-state.ts +45 -16
- package/src/publishing/reviewer-groups.ts +24 -0
- package/src/publishing/share-dialog.ts +42 -27
- package/src/publishing/trim-dataframe.ts +8 -1
- package/src/tests/abundance-correlation.ts +135 -0
- package/src/tests/analysis.ts +63 -0
- package/src/tests/enrichment-visualization.ts +95 -9
- package/src/tests/enrichment.ts +2 -1
- package/src/tests/project-vocabulary.ts +39 -0
- package/src/tests/publish-roundtrip.ts +40 -40
- package/src/tests/publish-spike.ts +2 -2
- package/src/tests/rank-abundance.ts +85 -0
- package/src/utils/abundance-detection.ts +145 -0
- package/src/viewers/abundance-correlation.ts +208 -0
- package/src/viewers/enrichment-viewers.ts +58 -24
- package/src/viewers/rank-abundance.ts +153 -0
- package/src/viewers/volcano.ts +3 -0
- package/test-console-output-1.log +1145 -1090
- package/test-record-1.mp4 +0 -0
- package/src/tests/group-mean-correlation.ts +0 -139
- package/src/viewers/group-mean-correlation.ts +0 -218
package/test-record-1.mp4
CHANGED
|
Binary file
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {category, test, expect} from '@datagrok-libraries/test/src/test';
|
|
3
|
-
import {
|
|
4
|
-
createGroupMeanCorrelation,
|
|
5
|
-
computeGroupMeans,
|
|
6
|
-
pearson,
|
|
7
|
-
spearman,
|
|
8
|
-
} from '../viewers/group-mean-correlation';
|
|
9
|
-
import {setGroups} from '../analysis/experiment-setup';
|
|
10
|
-
import {SEMTYPE} from '../utils/proteomics-types';
|
|
11
|
-
|
|
12
|
-
/** Builds a 5-row, 4-sample fixture DataFrame with a `direction` column so the
|
|
13
|
-
* correlation viewer's color binding has something to point at. */
|
|
14
|
-
function makeFixtureDf(): DG.DataFrame {
|
|
15
|
-
const cols: DG.Column[] = [];
|
|
16
|
-
const pid = DG.Column.fromStrings('Primary Protein ID',
|
|
17
|
-
['P1', 'P2', 'P3', 'P4', 'P5']);
|
|
18
|
-
pid.semType = SEMTYPE.PROTEIN_ID;
|
|
19
|
-
cols.push(pid);
|
|
20
|
-
|
|
21
|
-
const display = DG.Column.fromStrings('Display Name',
|
|
22
|
-
['Gene1', 'Gene2', 'Gene3', 'Gene4', 'Gene5']);
|
|
23
|
-
display.semType = SEMTYPE.DISPLAY_NAME;
|
|
24
|
-
cols.push(display);
|
|
25
|
-
|
|
26
|
-
// Intensities — known per-row mean of [g1,g2] is monotonic for groupMeanColumnsCreated.
|
|
27
|
-
// Row 0: g1={1,3}→mean=2, g2={5,7}→mean=6
|
|
28
|
-
// Row 1: g1={2,4}→mean=3, g2={6,8}→mean=7
|
|
29
|
-
// Row 2: g1={3,5}→mean=4, g2={7,9}→mean=8
|
|
30
|
-
// Row 3: g1={4,6}→mean=5, g2={8,10}→mean=9
|
|
31
|
-
// Row 4: g1={5,7}→mean=6, g2={9,11}→mean=10
|
|
32
|
-
cols.push(DG.Column.fromList('double' as DG.ColumnType, 'S1', [1, 2, 3, 4, 5]));
|
|
33
|
-
cols.push(DG.Column.fromList('double' as DG.ColumnType, 'S2', [3, 4, 5, 6, 7]));
|
|
34
|
-
cols.push(DG.Column.fromList('double' as DG.ColumnType, 'S3', [5, 6, 7, 8, 9]));
|
|
35
|
-
cols.push(DG.Column.fromList('double' as DG.ColumnType, 'S4', [7, 8, 9, 10, 11]));
|
|
36
|
-
|
|
37
|
-
// direction column — the viewer reads this for color binding (set by Plan 14-02 ensureDirectionColumn).
|
|
38
|
-
cols.push(DG.Column.fromStrings('direction',
|
|
39
|
-
['Enriched in Control', 'Enriched in Treatment', 'Not significant',
|
|
40
|
-
'Enriched in Control', 'Enriched in Treatment']));
|
|
41
|
-
|
|
42
|
-
const df = DG.DataFrame.fromColumns(cols);
|
|
43
|
-
setGroups(df, {
|
|
44
|
-
group1: {name: 'Control', columns: ['S1', 'S2']},
|
|
45
|
-
group2: {name: 'Treatment', columns: ['S3', 'S4']},
|
|
46
|
-
});
|
|
47
|
-
df.name = `correlation-fixture-${Math.random().toString(36).slice(2, 8)}`;
|
|
48
|
-
return df;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
category('Proteomics: 14-04', () => {
|
|
52
|
-
test('groupMeanColumnsCreated', async () => {
|
|
53
|
-
const df = makeFixtureDf();
|
|
54
|
-
computeGroupMeans(df, {
|
|
55
|
-
group1: {name: 'Control', columns: ['S1', 'S2']},
|
|
56
|
-
group2: {name: 'Treatment', columns: ['S3', 'S4']},
|
|
57
|
-
});
|
|
58
|
-
const num = df.col('Numerator Mean');
|
|
59
|
-
const den = df.col('Denominator Mean');
|
|
60
|
-
expect(num !== null, true);
|
|
61
|
-
expect(den !== null, true);
|
|
62
|
-
expect(num!.semType, SEMTYPE.NUMERATOR_MEAN);
|
|
63
|
-
expect(den!.semType, SEMTYPE.DENOMINATOR_MEAN);
|
|
64
|
-
expect(num!.length, df.rowCount);
|
|
65
|
-
expect(den!.length, df.rowCount);
|
|
66
|
-
// Row 0 means: g1=(1+3)/2=2, g2=(5+7)/2=6.
|
|
67
|
-
expect(Math.abs((num!.get(0) as number) - 2) < 1e-6, true);
|
|
68
|
-
expect(Math.abs((den!.get(0) as number) - 6) < 1e-6, true);
|
|
69
|
-
// Row 4 means: g1=(5+7)/2=6, g2=(9+11)/2=10.
|
|
70
|
-
expect(Math.abs((num!.get(4) as number) - 6) < 1e-6, true);
|
|
71
|
-
expect(Math.abs((den!.get(4) as number) - 10) < 1e-6, true);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('groupMeanColumnsReRunSafe', async () => {
|
|
75
|
-
const df = makeFixtureDf();
|
|
76
|
-
const groups = {
|
|
77
|
-
group1: {name: 'Control', columns: ['S1', 'S2']},
|
|
78
|
-
group2: {name: 'Treatment', columns: ['S3', 'S4']},
|
|
79
|
-
};
|
|
80
|
-
computeGroupMeans(df, groups);
|
|
81
|
-
computeGroupMeans(df, groups);
|
|
82
|
-
// No duplicates after a second invocation.
|
|
83
|
-
const numCount = df.columns.toList().filter((c) => c.name === 'Numerator Mean').length;
|
|
84
|
-
const denCount = df.columns.toList().filter((c) => c.name === 'Denominator Mean').length;
|
|
85
|
-
expect(numCount, 1);
|
|
86
|
-
expect(denCount, 1);
|
|
87
|
-
// Values still correct.
|
|
88
|
-
const num = df.col('Numerator Mean')!;
|
|
89
|
-
expect(Math.abs((num.get(0) as number) - 2) < 1e-6, true);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('pearsonPerfectCorrelation', async () => {
|
|
93
|
-
const r = pearson([1, 2, 3, 4, 5], [2, 4, 6, 8, 10]);
|
|
94
|
-
expect(Math.abs(r - 1) < 1e-9, true);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('pearsonNoCorrelation', async () => {
|
|
98
|
-
const r = pearson([1, 2, 3, 4, 5], [3, 1, 4, 1, 5]);
|
|
99
|
-
// Not exactly 0 — just within a reasonable tolerance away from 1.
|
|
100
|
-
expect(Math.abs(r) < 0.6, true);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('spearmanRankTies', async () => {
|
|
104
|
-
const rho = spearman([1, 2, 2, 3, 4], [1, 2, 2, 3, 4]);
|
|
105
|
-
expect(Math.abs(rho - 1) < 1e-9, true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('createGroupMeanCorrelationFactory', async () => {
|
|
109
|
-
const df = makeFixtureDf();
|
|
110
|
-
const sp = createGroupMeanCorrelation(df);
|
|
111
|
-
expect(sp !== null, true);
|
|
112
|
-
const opts: any = (sp as any).getOptions?.();
|
|
113
|
-
const title: string = opts?.look?.title ?? '';
|
|
114
|
-
expect(title.includes('r='), true);
|
|
115
|
-
expect(title.includes('ρ='), true);
|
|
116
|
-
expect(sp.props.xColumnName, 'Numerator Mean');
|
|
117
|
-
expect(sp.props.yColumnName, 'Denominator Mean');
|
|
118
|
-
expect(sp.props.colorColumnName, 'direction');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('correlationDiagonalLine', async () => {
|
|
122
|
-
const df = makeFixtureDf();
|
|
123
|
-
createGroupMeanCorrelation(df);
|
|
124
|
-
const lines = df.meta.formulaLines.items;
|
|
125
|
-
const diagonal = lines.find((l: any) => {
|
|
126
|
-
const f = (l.formula ?? '') as string;
|
|
127
|
-
return typeof f === 'string' &&
|
|
128
|
-
f.includes('Numerator Mean') && f.includes('Denominator Mean');
|
|
129
|
-
});
|
|
130
|
-
expect(diagonal !== undefined, true);
|
|
131
|
-
expect((diagonal as any).color, '#888888');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('correlationLabelBindsToDisplayName', async () => {
|
|
135
|
-
const df = makeFixtureDf();
|
|
136
|
-
const sp = createGroupMeanCorrelation(df);
|
|
137
|
-
expect((sp.props.labelColumnNames as string[])?.includes('Display Name'), true);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {findColumn} from '../utils/column-detection';
|
|
3
|
-
import {SEMTYPE} from '../utils/proteomics-types';
|
|
4
|
-
import {getGroups, GroupAssignment} from '../analysis/experiment-setup';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* R4 / D-12 — Group-Mean Correlation viewer.
|
|
8
|
-
*
|
|
9
|
-
* Adds two derived columns to the host DataFrame (Numerator Mean, Denominator
|
|
10
|
-
* Mean — means of the group1/group2 intensity columns per row) and renders
|
|
11
|
-
* them as a scatter colored by the existing `direction` column (magenta/cyan/
|
|
12
|
-
* gray per D-04). The viewer title carries inline Pearson r and Spearman ρ
|
|
13
|
-
* over the current filter; a y=x diagonal reference line is drawn in #888888.
|
|
14
|
-
*
|
|
15
|
-
* The derived columns are re-run-safe via `ensureFreshFloat` — a second
|
|
16
|
-
* invocation replaces the columns instead of duplicating them.
|
|
17
|
-
*
|
|
18
|
-
* Statistics: `@datagrok-libraries/statistics` exports only Kendall's tau,
|
|
19
|
-
* so Pearson and Spearman are implemented inline (Pearson over raw values,
|
|
20
|
-
* Spearman = Pearson over fractional ranks).
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const NUMERATOR_MEAN_COL = 'Numerator Mean';
|
|
24
|
-
const DENOMINATOR_MEAN_COL = 'Denominator Mean';
|
|
25
|
-
const DIRECTION_COL = 'direction';
|
|
26
|
-
const DIAGONAL_FORMULA = `\${${NUMERATOR_MEAN_COL}} = \${${DENOMINATOR_MEAN_COL}}`;
|
|
27
|
-
const DIAGONAL_COLOR = '#888888';
|
|
28
|
-
|
|
29
|
-
/** Bulk-init pattern — removes an existing column first to avoid duplicates on
|
|
30
|
-
* re-run. Mirrors src/viewers/qc-computations.ts:28-32. */
|
|
31
|
-
function ensureFreshFloat(df: DG.DataFrame, name: string): DG.Column {
|
|
32
|
-
if (df.columns.contains(name))
|
|
33
|
-
df.columns.remove(name);
|
|
34
|
-
return df.columns.addNewFloat(name);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Mean of non-null values across `cols` at `rowIdx`. Returns NaN if every
|
|
38
|
-
* value is null. Mirrors qc-computations.ts:15-25. */
|
|
39
|
-
function groupMean(cols: DG.Column[], rowIdx: number): number {
|
|
40
|
-
let sum = 0;
|
|
41
|
-
let n = 0;
|
|
42
|
-
for (const c of cols) {
|
|
43
|
-
if (c.isNone(rowIdx)) continue;
|
|
44
|
-
sum += c.get(rowIdx) as number;
|
|
45
|
-
n++;
|
|
46
|
-
}
|
|
47
|
-
return n > 0 ? sum / n : NaN;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Derives Numerator Mean (group1) and Denominator Mean (group2) columns on
|
|
52
|
-
* `df`. The join key is the protein row index — never a derived key like
|
|
53
|
-
* gene name (project memory project_proteomics_deqms_result_misalignment).
|
|
54
|
-
*
|
|
55
|
-
* The columns carry the dedicated SEMTYPE.NUMERATOR_MEAN / SEMTYPE.DENOMINATOR_MEAN
|
|
56
|
-
* tags so downstream tooling can find them via `findColumn`.
|
|
57
|
-
*/
|
|
58
|
-
export function computeGroupMeans(df: DG.DataFrame, groups: GroupAssignment): void {
|
|
59
|
-
const g1Cols = groups.group1.columns
|
|
60
|
-
.map((n) => df.col(n))
|
|
61
|
-
.filter((c): c is DG.Column => c != null);
|
|
62
|
-
const g2Cols = groups.group2.columns
|
|
63
|
-
.map((n) => df.col(n))
|
|
64
|
-
.filter((c): c is DG.Column => c != null);
|
|
65
|
-
|
|
66
|
-
const nRows = df.rowCount;
|
|
67
|
-
const numArr = new Float32Array(nRows);
|
|
68
|
-
const denArr = new Float32Array(nRows);
|
|
69
|
-
for (let i = 0; i < nRows; i++) {
|
|
70
|
-
const m1 = groupMean(g1Cols, i);
|
|
71
|
-
const m2 = groupMean(g2Cols, i);
|
|
72
|
-
numArr[i] = isNaN(m1) ? DG.FLOAT_NULL : m1;
|
|
73
|
-
denArr[i] = isNaN(m2) ? DG.FLOAT_NULL : m2;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const numCol = ensureFreshFloat(df, NUMERATOR_MEAN_COL);
|
|
77
|
-
numCol.init((i) => numArr[i]);
|
|
78
|
-
numCol.semType = SEMTYPE.NUMERATOR_MEAN;
|
|
79
|
-
|
|
80
|
-
const denCol = ensureFreshFloat(df, DENOMINATOR_MEAN_COL);
|
|
81
|
-
denCol.init((i) => denArr[i]);
|
|
82
|
-
denCol.semType = SEMTYPE.DENOMINATOR_MEAN;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Pearson correlation. Skips paired entries containing NaN or FLOAT_NULL. */
|
|
86
|
-
export function pearson(xs: number[], ys: number[]): number {
|
|
87
|
-
let n = 0;
|
|
88
|
-
let sumX = 0;
|
|
89
|
-
let sumY = 0;
|
|
90
|
-
for (let i = 0; i < xs.length; i++) {
|
|
91
|
-
const x = xs[i];
|
|
92
|
-
const y = ys[i];
|
|
93
|
-
if (!Number.isFinite(x) || !Number.isFinite(y) || x === DG.FLOAT_NULL || y === DG.FLOAT_NULL) continue;
|
|
94
|
-
sumX += x;
|
|
95
|
-
sumY += y;
|
|
96
|
-
n++;
|
|
97
|
-
}
|
|
98
|
-
if (n === 0) return NaN;
|
|
99
|
-
const meanX = sumX / n;
|
|
100
|
-
const meanY = sumY / n;
|
|
101
|
-
let num = 0;
|
|
102
|
-
let denX = 0;
|
|
103
|
-
let denY = 0;
|
|
104
|
-
for (let i = 0; i < xs.length; i++) {
|
|
105
|
-
const x = xs[i];
|
|
106
|
-
const y = ys[i];
|
|
107
|
-
if (!Number.isFinite(x) || !Number.isFinite(y) || x === DG.FLOAT_NULL || y === DG.FLOAT_NULL) continue;
|
|
108
|
-
const dx = x - meanX;
|
|
109
|
-
const dy = y - meanY;
|
|
110
|
-
num += dx * dy;
|
|
111
|
-
denX += dx * dx;
|
|
112
|
-
denY += dy * dy;
|
|
113
|
-
}
|
|
114
|
-
const den = Math.sqrt(denX * denY);
|
|
115
|
-
return den === 0 ? NaN : num / den;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** Fractional ranks (1-based). Ties get the average of the positions they span. */
|
|
119
|
-
function rank(arr: number[]): number[] {
|
|
120
|
-
const indexed = arr.map((v, i) => ({v, i}));
|
|
121
|
-
indexed.sort((a, b) => a.v - b.v);
|
|
122
|
-
const ranks = new Array<number>(arr.length);
|
|
123
|
-
let i = 0;
|
|
124
|
-
while (i < indexed.length) {
|
|
125
|
-
let j = i;
|
|
126
|
-
while (j + 1 < indexed.length && indexed[j + 1].v === indexed[i].v) j++;
|
|
127
|
-
const avg = (i + j) / 2 + 1; // 1-based average of positions [i..j]
|
|
128
|
-
for (let k = i; k <= j; k++) ranks[indexed[k].i] = avg;
|
|
129
|
-
i = j + 1;
|
|
130
|
-
}
|
|
131
|
-
return ranks;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Spearman = Pearson over fractional ranks. */
|
|
135
|
-
export function spearman(xs: number[], ys: number[]): number {
|
|
136
|
-
return pearson(rank(xs), rank(ys));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Computes Pearson + Spearman over the filtered subset of two columns. */
|
|
140
|
-
function computePearsonSpearman(
|
|
141
|
-
df: DG.DataFrame, xName: string, yName: string,
|
|
142
|
-
): {r: number; rho: number} {
|
|
143
|
-
const xCol = df.col(xName);
|
|
144
|
-
const yCol = df.col(yName);
|
|
145
|
-
if (!xCol || !yCol) return {r: NaN, rho: NaN};
|
|
146
|
-
const xRaw = xCol.getRawData() as Float32Array | Float64Array;
|
|
147
|
-
const yRaw = yCol.getRawData() as Float32Array | Float64Array;
|
|
148
|
-
|
|
149
|
-
const xs: number[] = [];
|
|
150
|
-
const ys: number[] = [];
|
|
151
|
-
for (let i = 0; i < df.rowCount; i++) {
|
|
152
|
-
if (!df.filter.get(i)) continue;
|
|
153
|
-
const x = xRaw[i];
|
|
154
|
-
const y = yRaw[i];
|
|
155
|
-
if (x === DG.FLOAT_NULL || y === DG.FLOAT_NULL) continue;
|
|
156
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
|
|
157
|
-
xs.push(x);
|
|
158
|
-
ys.push(y);
|
|
159
|
-
}
|
|
160
|
-
return {r: pearson(xs, ys), rho: spearman(xs, ys)};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Main factory — derives Numerator/Denominator Mean, plots them as a scatter
|
|
165
|
-
* colored by direction, draws the y=x diagonal, and writes an inline
|
|
166
|
-
* Pearson/Spearman annotation into the title.
|
|
167
|
-
*/
|
|
168
|
-
export function createGroupMeanCorrelation(
|
|
169
|
-
df: DG.DataFrame,
|
|
170
|
-
options?: {title?: string},
|
|
171
|
-
): DG.ScatterPlotViewer {
|
|
172
|
-
const groups = getGroups(df);
|
|
173
|
-
if (!groups)
|
|
174
|
-
throw new Error('Annotate experimental groups first (Proteomics | Annotate Experiment)');
|
|
175
|
-
|
|
176
|
-
computeGroupMeans(df, groups);
|
|
177
|
-
|
|
178
|
-
const sp = df.plot.scatter({
|
|
179
|
-
x: NUMERATOR_MEAN_COL,
|
|
180
|
-
y: DENOMINATOR_MEAN_COL,
|
|
181
|
-
color: DIRECTION_COL,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Display Name primary, Gene name fallback (Plan 02 pattern).
|
|
185
|
-
const labelCol = findColumn(df, SEMTYPE.DISPLAY_NAME, ['display name']) ??
|
|
186
|
-
findColumn(df, SEMTYPE.GENE_SYMBOL, ['gene name', 'gene symbol']);
|
|
187
|
-
if (labelCol) {
|
|
188
|
-
sp.props.labelColumnNames = [labelCol.name];
|
|
189
|
-
sp.props.showLabelsFor = 'MouseOverRow';
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// y = x diagonal reference — replace any prior diagonal so re-run doesn't stack.
|
|
193
|
-
df.meta.formulaLines.items = df.meta.formulaLines.items.filter((line) => {
|
|
194
|
-
const f = (line.formula ?? '') as string;
|
|
195
|
-
return !(typeof f === 'string' &&
|
|
196
|
-
f.includes(NUMERATOR_MEAN_COL) && f.includes(DENOMINATOR_MEAN_COL));
|
|
197
|
-
});
|
|
198
|
-
df.meta.formulaLines.addLine({
|
|
199
|
-
formula: DIAGONAL_FORMULA,
|
|
200
|
-
color: DIAGONAL_COLOR,
|
|
201
|
-
width: 1,
|
|
202
|
-
visible: true,
|
|
203
|
-
});
|
|
204
|
-
sp.props.showViewerFormulaLines = true;
|
|
205
|
-
|
|
206
|
-
// Inline correlation annotation over the current filter.
|
|
207
|
-
const {r, rho} = computePearsonSpearman(df, NUMERATOR_MEAN_COL, DENOMINATOR_MEAN_COL);
|
|
208
|
-
const baseTitle = options?.title ?? 'Group-Mean Correlation';
|
|
209
|
-
const rStr = Number.isFinite(r) ? r.toFixed(2) : 'NA';
|
|
210
|
-
const rhoStr = Number.isFinite(rho) ? rho.toFixed(2) : 'NA';
|
|
211
|
-
sp.setOptions({
|
|
212
|
-
title: `${baseTitle} — r=${rStr} (Pearson), ρ=${rhoStr} (Spearman)`,
|
|
213
|
-
xColumnLabel: `${groups.group1.name} mean (log2 intensity)`,
|
|
214
|
-
yColumnLabel: `${groups.group2.name} mean (log2 intensity)`,
|
|
215
|
-
} as any);
|
|
216
|
-
|
|
217
|
-
return sp;
|
|
218
|
-
}
|