@sjcrh/proteinpaint-server 2.110.0 → 2.111.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/package.json +4 -4
- package/routes/correlationVolcano.js +11 -1
- package/routes/termdb.DE.js +19 -11
- package/routes/termdb.violin.js +45 -7
- package/src/app.js +4762 -4707
- package/utils/density.R +29 -0
- package/utils/edge.R +25 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjcrh/proteinpaint-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.111.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
|
|
6
6
|
"main": "src/app.js",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@sjcrh/augen": "2.109.1-0",
|
|
63
|
-
"@sjcrh/proteinpaint-rust": "2.
|
|
64
|
-
"@sjcrh/proteinpaint-shared": "2.
|
|
65
|
-
"@sjcrh/proteinpaint-types": "2.
|
|
63
|
+
"@sjcrh/proteinpaint-rust": "2.111.0",
|
|
64
|
+
"@sjcrh/proteinpaint-shared": "2.111.0",
|
|
65
|
+
"@sjcrh/proteinpaint-types": "2.111.0",
|
|
66
66
|
"@types/express": "^5.0.0",
|
|
67
67
|
"@types/express-session": "^1.18.1",
|
|
68
68
|
"better-sqlite3": "^9.4.1",
|
|
@@ -3,7 +3,6 @@ import { getData } from "../src/termdb.matrix.js";
|
|
|
3
3
|
import run_R from "../src/run_R.js";
|
|
4
4
|
import serverconfig from "../src/serverconfig.js";
|
|
5
5
|
import { mayLog } from "#src/helpers.ts";
|
|
6
|
-
import { stdDev } from "#shared/violin.bins.js";
|
|
7
6
|
import path from "path";
|
|
8
7
|
const minArrayLength = 3;
|
|
9
8
|
const minSD = 0.05;
|
|
@@ -134,7 +133,18 @@ function validate_correlationVolcano(ds) {
|
|
|
134
133
|
throw "unknown cv.variables.type";
|
|
135
134
|
}
|
|
136
135
|
}
|
|
136
|
+
function mean(data) {
|
|
137
|
+
return data.reduce((sum, value) => sum + value, 0) / data.length;
|
|
138
|
+
}
|
|
139
|
+
function stdDev(data) {
|
|
140
|
+
const meanValue = mean(data);
|
|
141
|
+
const squaredDifferences = data.map((value) => Math.pow(value - meanValue, 2));
|
|
142
|
+
const variance = squaredDifferences.reduce((sum, value) => sum + value, 0) / data.length;
|
|
143
|
+
return Math.sqrt(variance);
|
|
144
|
+
}
|
|
137
145
|
export {
|
|
138
146
|
api,
|
|
147
|
+
mean,
|
|
148
|
+
stdDev,
|
|
139
149
|
validate_correlationVolcano
|
|
140
150
|
};
|
package/routes/termdb.DE.js
CHANGED
|
@@ -7,6 +7,7 @@ import { get_ds_tdb } from "../src/termdb.js";
|
|
|
7
7
|
import run_R from "../src/run_R.js";
|
|
8
8
|
import { mayLog } from "#src/helpers.ts";
|
|
9
9
|
import serverconfig from "../src/serverconfig.js";
|
|
10
|
+
import imagesize from "image-size";
|
|
10
11
|
const api = {
|
|
11
12
|
endpoint: "DEanalysis",
|
|
12
13
|
methods: {
|
|
@@ -59,6 +60,8 @@ function init({ genomes }) {
|
|
|
59
60
|
throw term_results2.error;
|
|
60
61
|
}
|
|
61
62
|
const results = await run_DE(req.query, ds, term_results, term_results2);
|
|
63
|
+
if (!results || !results.data)
|
|
64
|
+
throw "No data [termdb.DE.ts init()]";
|
|
62
65
|
res.send(results);
|
|
63
66
|
} catch (e) {
|
|
64
67
|
res.send({ status: "error", error: e.message || e });
|
|
@@ -210,9 +213,6 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
210
213
|
}
|
|
211
214
|
const sample_size_limit = 8;
|
|
212
215
|
if (group1names.length <= sample_size_limit && group2names.length <= sample_size_limit || param.method == "edgeR") {
|
|
213
|
-
if (param.method == "edgeR") {
|
|
214
|
-
expression_input.VarGenes = param.VarGenes;
|
|
215
|
-
}
|
|
216
216
|
const time12 = (/* @__PURE__ */ new Date()).valueOf();
|
|
217
217
|
const result2 = JSON.parse(
|
|
218
218
|
await run_R(path.join(serverconfig.binpath, "utils", "edge.R"), JSON.stringify(expression_input))
|
|
@@ -223,16 +223,16 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
223
223
|
mayLog("ql_imagePath:", ql_imagePath);
|
|
224
224
|
const mds_imagePath = path.join(serverconfig.cachedir, result2.edgeR_mds_image_name[0]);
|
|
225
225
|
mayLog("mds_imagePath:", mds_imagePath);
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
await readFileAndDelete(mds_imagePath, "mds_image", result2);
|
|
227
|
+
await readFileAndDelete(ql_imagePath, "ql_image", result2);
|
|
228
228
|
return {
|
|
229
229
|
data: result2.gene_data,
|
|
230
230
|
sample_size1,
|
|
231
231
|
sample_size2,
|
|
232
232
|
method: param.method,
|
|
233
|
-
ql_image:
|
|
233
|
+
ql_image: result2.ql_image,
|
|
234
234
|
// QL fit image
|
|
235
|
-
mds_image:
|
|
235
|
+
mds_image: result2.mds_image
|
|
236
236
|
// MDS image
|
|
237
237
|
};
|
|
238
238
|
}
|
|
@@ -242,11 +242,19 @@ async function run_DE(param, ds, term_results, term_results2) {
|
|
|
242
242
|
param.method = "wilcoxon";
|
|
243
243
|
return { data: result, sample_size1, sample_size2, method: param.method };
|
|
244
244
|
}
|
|
245
|
-
async function readFileAndDelete(file) {
|
|
246
|
-
const
|
|
247
|
-
|
|
245
|
+
async function readFileAndDelete(file, key, response) {
|
|
246
|
+
const plot = await fs.promises.readFile(file);
|
|
247
|
+
const plotBuffer = Buffer.from(plot).toString("base64");
|
|
248
|
+
const obj = {
|
|
249
|
+
src: `data:image/png;base64,${plotBuffer}`,
|
|
250
|
+
size: imagesize(file),
|
|
251
|
+
key
|
|
252
|
+
};
|
|
253
|
+
response[key] = obj;
|
|
254
|
+
fs.unlink(file, (err) => {
|
|
255
|
+
if (err)
|
|
256
|
+
throw err;
|
|
248
257
|
});
|
|
249
|
-
return Buffer.from(data).toString("base64");
|
|
250
258
|
}
|
|
251
259
|
export {
|
|
252
260
|
api
|
package/routes/termdb.violin.js
CHANGED
|
@@ -6,8 +6,10 @@ import { createCanvas } from "canvas";
|
|
|
6
6
|
import { getOrderedLabels } from "../src/termdb.barchart.js";
|
|
7
7
|
import summaryStats from "#shared/descriptive.stats.js";
|
|
8
8
|
import { isNumericTerm } from "#shared/terms.js";
|
|
9
|
-
import { getBinsDensity } from "#shared/violin.bins.js";
|
|
10
9
|
import { numericBins, parseValues } from "./termdb.boxplot.ts";
|
|
10
|
+
import serverconfig from "../src/serverconfig.js";
|
|
11
|
+
import run_R from "../src/run_R.js";
|
|
12
|
+
import path from "path";
|
|
11
13
|
const minSampleSize = 5;
|
|
12
14
|
const api = {
|
|
13
15
|
endpoint: "termdb/violin",
|
|
@@ -74,7 +76,7 @@ async function trigger_getViolinPlotData(q, ds, genome) {
|
|
|
74
76
|
const valuesObject = divideValues(q, data, sampleType);
|
|
75
77
|
const result = setResponse(valuesObject, data, q, sampleType);
|
|
76
78
|
await getWilcoxonData(q.divideTw, result);
|
|
77
|
-
createCanvasImg(q, result, ds);
|
|
79
|
+
await createCanvasImg(q, result, ds);
|
|
78
80
|
return result;
|
|
79
81
|
}
|
|
80
82
|
async function getWilcoxonData(divideTw, result) {
|
|
@@ -168,7 +170,7 @@ function setResponse(valuesObject, data, q, sampleType) {
|
|
|
168
170
|
};
|
|
169
171
|
return result;
|
|
170
172
|
}
|
|
171
|
-
function createCanvasImg(q, result, ds) {
|
|
173
|
+
async function createCanvasImg(q, result, ds) {
|
|
172
174
|
if (!q.radius)
|
|
173
175
|
q.radius = 5;
|
|
174
176
|
if (q.radius <= 0)
|
|
@@ -178,6 +180,10 @@ function createCanvasImg(q, result, ds) {
|
|
|
178
180
|
if (!q.strokeWidth)
|
|
179
181
|
q.strokeWidth = 0.2;
|
|
180
182
|
const refSize = q.radius * 4;
|
|
183
|
+
const plot2Values = {};
|
|
184
|
+
for (const plot of result.plots)
|
|
185
|
+
plot2Values[plot.label] = plot.values;
|
|
186
|
+
const densities = await getDensities(plot2Values);
|
|
181
187
|
let axisScale;
|
|
182
188
|
const useLog = q.unit == "log";
|
|
183
189
|
if (useLog) {
|
|
@@ -189,11 +195,13 @@ function createCanvasImg(q, result, ds) {
|
|
|
189
195
|
const scaledRadius = q.radius / q.devicePixelRatio;
|
|
190
196
|
const arcEndAngle = scaledRadius * Math.PI;
|
|
191
197
|
for (const plot of result.plots) {
|
|
198
|
+
plot.density = densities[plot.label];
|
|
192
199
|
const canvas = createCanvas(width, height);
|
|
193
200
|
const ctx = canvas.getContext("2d");
|
|
194
|
-
|
|
201
|
+
const symbolOpacity = plot.density.densityMax == 0 ? 1 : 0.8;
|
|
202
|
+
ctx.strokeStyle = `rgba(0,0,0,${symbolOpacity})`;
|
|
195
203
|
ctx.lineWidth = q.strokeWidth / q.devicePixelRatio;
|
|
196
|
-
ctx.globalAlpha =
|
|
204
|
+
ctx.globalAlpha = symbolOpacity;
|
|
197
205
|
ctx.fillStyle = plot.values.length <= minSampleSize ? "black" : "#ffe6e6";
|
|
198
206
|
if (q.devicePixelRatio != 1) {
|
|
199
207
|
ctx.scale(q.devicePixelRatio, q.devicePixelRatio);
|
|
@@ -221,13 +229,43 @@ function createCanvasImg(q, result, ds) {
|
|
|
221
229
|
ctx.stroke();
|
|
222
230
|
});
|
|
223
231
|
plot.src = canvas.toDataURL();
|
|
224
|
-
plot.density = getBinsDensity(plot, q.isKDE, q.ticks);
|
|
225
232
|
plot.summaryStats = summaryStats(plot.values).values;
|
|
226
|
-
delete plot.values;
|
|
227
233
|
}
|
|
228
234
|
}
|
|
235
|
+
async function getDensity(values) {
|
|
236
|
+
const result = await getDensities({ plot: values });
|
|
237
|
+
return result.plot;
|
|
238
|
+
}
|
|
239
|
+
async function getDensities(plot2Values) {
|
|
240
|
+
const densityScript = path.join(serverconfig.binpath, "utils", "density.R");
|
|
241
|
+
const plot2Density = JSON.parse(await run_R(densityScript, JSON.stringify(plot2Values)));
|
|
242
|
+
const densities = {};
|
|
243
|
+
for (const plot in plot2Density) {
|
|
244
|
+
const result = plot2Density[plot];
|
|
245
|
+
const bins = [];
|
|
246
|
+
let densityMin = Infinity;
|
|
247
|
+
let densityMax = -Infinity;
|
|
248
|
+
let xMin = Infinity;
|
|
249
|
+
let xMax = -Infinity;
|
|
250
|
+
for (const [i, x] of Object.entries(result.x)) {
|
|
251
|
+
const density2 = result.y[i];
|
|
252
|
+
xMin = Math.min(xMin, x);
|
|
253
|
+
xMax = Math.max(xMax, x);
|
|
254
|
+
densityMin = Math.min(densityMin, density2);
|
|
255
|
+
densityMax = Math.max(densityMax, density2);
|
|
256
|
+
bins.push({ x0: x, density: density2 });
|
|
257
|
+
}
|
|
258
|
+
bins.unshift({ x0: xMin, density: densityMin });
|
|
259
|
+
bins.push({ x0: xMax, density: densityMin });
|
|
260
|
+
const density = { bins, densityMin, densityMax, minvalue: xMin, maxvalue: xMax };
|
|
261
|
+
densities[plot] = density;
|
|
262
|
+
}
|
|
263
|
+
return densities;
|
|
264
|
+
}
|
|
229
265
|
export {
|
|
230
266
|
api,
|
|
267
|
+
getDensities,
|
|
268
|
+
getDensity,
|
|
231
269
|
getWilcoxonData,
|
|
232
270
|
sortKey2values,
|
|
233
271
|
trigger_getViolinPlotData
|