@milaboratories/graph-maker 1.1.159 → 1.1.162
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/dist/GraphMaker/index.vue.js +33 -33
- package/dist/GraphMaker/index.vue.js.map +1 -1
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/components/Chart.js +6 -6
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/components/Dendrograms.js +51 -51
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/components/Dendrograms.js.map +1 -1
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/getClusters.js +135 -46
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/getClusters.js.map +1 -1
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/getDendrograms.js +35 -37
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/getDendrograms.js.map +1 -1
- package/dist/node_modules/@milaboratories/miplots4/dist/heatmap/index.js +3 -3
- package/dist/node_modules/@milaboratories/miplots4/dist/node_modules/react/index.js +1 -1
- package/dist/node_modules/@milaboratories/miplots4/dist/node_modules/react-dom/index.js +1 -1
- package/dist/node_modules/@milaboratories/pf-plots/dist/pframe/ColumnsProvider.js +141 -110
- package/dist/node_modules/@milaboratories/pf-plots/dist/pframe/ColumnsProvider.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getClusters.js","sources":["../../../../../../node_modules/@milaboratories/miplots4/src/heatmap/getClusters.ts"],"sourcesContent":["import type {Cluster} from './components/types';\nimport type {DendrogramDistance, DendrogramLinkage} from '../types';\n\n/* distance - between 2 vectors */\nexport function squaredEuclidean(p: number[], q: number[]) {\n let d = 0;\n for (let i = 0; i < p.length; i++) {\n d += (p[i] - q[i]) * (p[i] - q[i]);\n }\n return d;\n}\n\nexport function euclidean(p: number[], q: number[]) {\n return Math.sqrt(squaredEuclidean(p, q));\n}\n\n/* linkage - between 2 clusters */\n// get average distance between sets of indexes, given distance matrix\nexport const averageLinkage = (setA: number[], setB: number[], distances: number[][]) => {\n let distance = 0;\n for (const a of setA) {\n for (const b of setB) distance += distances[a][b];\n }\n\n return distance / setA.length / setB.length;\n};\nexport const completeLinkage = (setA: number[], setB: number[], distances: number[][]) => {\n let distance = 0;\n for (const a of setA) {\n for (const b of setB) distance = Math.max(distance, distances[a][b]);\n }\n\n return distance;\n};\nexport const singleLinkage = (setA: number[], setB: number[], distances: number[][]) => {\n let distance = Infinity;\n for (const a of setA) {\n for (const b of setB) distance = Math.min(distance, distances[a][b]);\n }\n\n return distance;\n};\n\nfunction getLinkageFn (linkage:DendrogramLinkage) {\n switch (linkage) {\n case 'average': return averageLinkage;\n case 'complete': return completeLinkage;\n case 'single': return singleLinkage;\n default: return averageLinkage;\n }\n}\nfunction getDistanceFn(distance:DendrogramDistance) {\n switch (distance) {\n case 'euclidean': return euclidean;\n case 'squaredEuclidean': return squaredEuclidean;\n default: return euclidean;\n }\n}\n\n// the main clustering function\nexport function getClusters(dataKeys:string[] = [], dataValues:number[][], distanceType:DendrogramDistance = 'euclidean', linkageType:DendrogramLinkage = 'average'):Cluster {\n const distanceFn = getDistanceFn(distanceType);\n const linkageFn = getLinkageFn(linkageType);\n // compute distance between each data point and every other data point\n // N x N matrix where N = data.length\n const distances = dataValues.map(datum => {\n // get distance between datum and other datum\n return dataValues.map(otherDatum => distanceFn(datum, otherDatum));\n });\n\n // initialize clusters to match data\n const clusters:Cluster[] = dataKeys.map((datum, index) => ({\n height: 0,\n keys: [datum],\n indexes: [index],\n children: []\n }));\n\n // iterate through data\n for (let iteration = 0; iteration < dataKeys.length; iteration++) {\n // dont find clusters to merge when only one cluster left\n if (iteration >= dataKeys.length - 1) break;\n\n // initialize smallest distance\n let nearestDistance = Infinity;\n let nearestRow = 0;\n let nearestCol = 0;\n\n // upper triangular matrix of clusters\n for (let row = 0; row < clusters.length; row++) {\n for (let col = row + 1; col < clusters.length; col++) {\n // calculate distance between clusters\n const distance = linkageFn(clusters[row].indexes, clusters[col].indexes, distances);\n // update smallest distance\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearestRow = row;\n nearestCol = col;\n }\n }\n }\n\n // merge nearestRow and nearestCol clusters together\n const newCluster = {\n keys: [...clusters[nearestRow].keys, ...clusters[nearestCol].keys],\n indexes: [...clusters[nearestRow].indexes, ...clusters[nearestCol].indexes],\n height: nearestDistance,\n children: [clusters[nearestRow], clusters[nearestCol]],\n };\n\n // remove nearestRow and nearestCol clusters\n // splice higher index first so it doesn't affect second splice\n clusters.splice(Math.max(nearestRow, nearestCol), 1);\n clusters.splice(Math.min(nearestRow, nearestCol), 1);\n\n // add new merged cluster\n clusters.push(newCluster);\n }\n\n // return root\n return clusters[0];\n}\n"],"names":["squaredEuclidean","p","q","d","i","euclidean","averageLinkage","setA","setB","distances","distance","a","b","completeLinkage","singleLinkage","getLinkageFn","linkage","getDistanceFn","getClusters","dataKeys","dataValues","distanceType","linkageType","distanceFn","linkageFn","datum","otherDatum","clusters","index","iteration","nearestDistance","nearestRow","nearestCol","row","col","newCluster"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"getClusters.js","sources":["../../../../../../node_modules/@milaboratories/miplots4/src/heatmap/getClusters.ts"],"sourcesContent":["import type { Cluster } from './components/types';\nimport type { DendrogramDistance, DendrogramLinkage } from '../types';\nimport { randomLcg } from 'd3-random';\n\n/* DISTANCE FUNCTIONS - Calculate similarity between two data points */\n\n/**\n * Calculate squared Euclidean distance between two vectors\n * This is faster than regular Euclidean distance since it avoids the square root\n * Formula: Σ(p[i] - q[i])²\n * @param p First vector\n * @param q Second vector\n * @returns Squared distance (always positive)\n */\nexport function squaredEuclidean(p: number[], q: number[]) {\n let d = 0;\n // Sum the squared differences for each dimension\n for (let i = 0; i < p.length; i++) {\n d += (p[i] - q[i]) * (p[i] - q[i]);\n }\n return d;\n}\n\n/**\n * Calculate Euclidean distance between two vectors\n * This is the standard geometric distance in n-dimensional space\n * Formula: √(Σ(p[i] - q[i])²)\n * @param p First vector\n * @param q Second vector\n * @returns Euclidean distance\n */\nexport function euclidean(p: number[], q: number[]) {\n return Math.sqrt(squaredEuclidean(p, q));\n}\n\n/* LINKAGE FUNCTIONS - Define how to measure distance between two clusters */\n\n/**\n * Average Linkage\n * Calculates the average distance between all pairs of points in the two clusters\n * This is the most commonly used linkage method as it balances between single and complete linkage\n * @param setA Indexes of points in first cluster\n * @param setB Indexes of points in second cluster \n * @param getDistance Function to get distance between two point indexes\n * @returns Average distance between clusters\n */\nexport const averageLinkage = (setA: number[], setB: number[], getDistance: (i: number, j: number) => number) => {\n let distance = 0;\n // Sum all pairwise distances between clusters\n for (const a of setA) {\n for (const b of setB) distance += getDistance(a, b);\n }\n\n // Return average distance\n return distance / setA.length / setB.length;\n};\n\n/**\n * Complete Linkage (Maximum Linkage)\n * Uses the maximum distance between any pair of points in the two clusters\n * Tends to create compact, well-separated clusters\n * Formula: max{d(a,b)} for all a in A, b in B\n * @param setA Indexes of points in first cluster\n * @param setB Indexes of points in second cluster\n * @param getDistance Function to get distance between two point indexes\n * @returns Maximum distance between clusters\n */\nexport const completeLinkage = (setA: number[], setB: number[], getDistance: (i: number, j: number) => number) => {\n let distance = 0;\n // Find maximum distance between any pair of points\n for (const a of setA) {\n for (const b of setB) distance = Math.max(distance, getDistance(a, b));\n }\n\n return distance;\n};\n\n/**\n * Single Linkage (Minimum Linkage)\n * Uses the minimum distance between any pair of points in the two clusters\n * Tends to create elongated, chain-like clusters\n * Formula: min{d(a,b)} for all a in A, b in B\n * @param setA Indexes of points in first cluster\n * @param setB Indexes of points in second cluster\n * @param getDistance Function to get distance between two point indexes\n * @returns Minimum distance between clusters\n */\nexport const singleLinkage = (setA: number[], setB: number[], getDistance: (i: number, j: number) => number) => {\n let distance = Infinity;\n // Find minimum distance between any pair of points\n for (const a of setA) {\n for (const b of setB) distance = Math.min(distance, getDistance(a, b));\n }\n\n return distance;\n};\n\n/* HELPER FUNCTIONS - Factory functions for distance and linkage methods */\n\n/**\n * Get the appropriate linkage function based on the linkage type\n * @param linkage Type of linkage method to use\n * @returns Function that calculates distance between two clusters\n */\nfunction getLinkageFn(linkage: DendrogramLinkage) {\n switch (linkage) {\n case 'average': return averageLinkage;\n case 'complete': return completeLinkage;\n case 'single': return singleLinkage;\n default: return averageLinkage; // Default to average linkage\n }\n}\n\n/**\n * Get the appropriate distance function based on the distance type\n * @param distance Type of distance metric to use\n * @returns Function that calculates distance between two points\n */\nfunction getDistanceFn(distance: DendrogramDistance) {\n switch (distance) {\n case 'euclidean': return euclidean;\n case 'squaredEuclidean': return squaredEuclidean;\n default: return euclidean; // Default to euclidean distance\n }\n}\n\n/* UTILITY FUNCTIONS - Helper functions for cluster manipulation and sorting */\n\n/**\n * Extract indexes from a cluster hierarchy in the correct hierarchical order\n * This function traverses the cluster tree and collects all indexes in the order\n * they appear in the dendrogram (left-to-right traversal)\n * @param cluster The root cluster to extract indexes from\n * @returns Array of indexes in hierarchical order\n */\nexport function extractOrderedIndexes(cluster: Cluster): number[] {\n const orderedIndexes: number[] = [];\n \n const traverse = (c: Cluster) => {\n if (c.children.length === 0) {\n // Leaf node - add all indexes from this cluster\n orderedIndexes.push(...c.indexes);\n } else {\n // Internal node - traverse children in order (left to right)\n for (const child of c.children) {\n traverse(child);\n }\n }\n };\n \n traverse(cluster);\n return orderedIndexes;\n}\n\nconst MAX_CLUSTER_SIZE = 2000;\n\n// Approximate clustering using sampling for very large datasets\nexport function getClustersApproximate(\n dataValues: number[][], \n distanceType: DendrogramDistance = 'euclidean', \n linkageType: DendrogramLinkage = 'average',\n): Cluster { \n if (dataValues.length <= MAX_CLUSTER_SIZE) {\n return getClusters(dataValues, distanceType, linkageType);\n }\n \n const N = dataValues.length;\n const getRandom = randomLcg(1);\n // Step 1: Random sampling\n const sampleIndices = new Set<number>();\n while (sampleIndices.size < MAX_CLUSTER_SIZE) {\n sampleIndices.add(Math.floor(getRandom() * N));\n }\n \n const sampleIndicesArray = Array.from(sampleIndices);\n const sampleValues = sampleIndicesArray.map(i => dataValues[i]);\n \n // Step 2: Cluster the sample\n const sampleCluster = getClusters(sampleValues, distanceType, linkageType);\n \n // Step 3: Assign remaining points to nearest cluster\n return assignRemainingPoints(sampleCluster, dataValues, sampleIndicesArray, distanceType);\n}\n\n// Assign remaining points to the nearest cluster from the sample\nfunction assignRemainingPoints(\n sampleCluster: Cluster, \n dataValues: number[][], \n sampleIndices: number[],\n distanceType: DendrogramDistance\n): Cluster {\n // console.log('assignRemainingPoints');\n\n const distanceFn = getDistanceFn(distanceType);\n const sampleSet = new Set(sampleIndices);\n \n // Assign each non-sample point to nearest sample point\n const assignments = new Map<number, number>(); // point -> sample point\n for (let i = 0; i < dataValues.length; i++) {\n if (sampleSet.has(i)) continue;\n \n let minDistance = Infinity;\n let nearestSample = -1;\n \n for (const sampleIdx of sampleIndices) {\n const distance = distanceFn(dataValues[i], dataValues[sampleIdx]);\n if (distance < minDistance) {\n minDistance = distance;\n nearestSample = sampleIdx;\n }\n }\n \n assignments.set(i, nearestSample);\n }\n // Reconstruct cluster with all points\n return reconstructWithAssignments(sampleCluster, assignments, sampleIndices);\n}\n\n// Reconstruct cluster hierarchy with assigned points\nfunction reconstructWithAssignments(\n sampleCluster: Cluster, \n assignments: Map<number, number>, \n sampleIndices: number[]\n): Cluster {\n // Create a map of sample indices to their clusters\n const sampleToCluster = new Map<number, Cluster>();\n const findSampleClusters = (cluster: Cluster) => {\n if (cluster.indexes.length === 1) {\n sampleToCluster.set(sampleIndices[cluster.indexes[0]], cluster);\n }\n for (const child of cluster.children) {\n findSampleClusters(child);\n }\n };\n findSampleClusters(sampleCluster);\n \n // Group assigned points by their sample cluster\n const assignedPointsByCluster = new Map<Cluster, number[]>();\n for (const [pointIdx, sampleIdx] of assignments) {\n const sampleCluster = sampleToCluster.get(sampleIdx);\n if (sampleCluster) {\n if (!assignedPointsByCluster.has(sampleCluster)) {\n assignedPointsByCluster.set(sampleCluster, []);\n }\n const cluster = assignedPointsByCluster.get(sampleCluster);\n if (cluster) {\n cluster.push(pointIdx);\n }\n }\n }\n\n const fillCorrectIndexes = (c: Cluster) => {\n c.indexes = c.indexes.map(i => sampleIndices[i]);\n for (const child of c.children) {\n fillCorrectIndexes(child);\n }\n };\n fillCorrectIndexes(sampleCluster);\n for (const [cluster, assignedPoints] of assignedPointsByCluster) {\n cluster.indexes = [...cluster.indexes, ...assignedPoints];\n }\n \n return {\n ...sampleCluster,\n indexes: extractOrderedIndexes(sampleCluster)\n };\n}\n\n/* MAIN HIERARCHICAL CLUSTERING FUNCTION - Standard agglomerative clustering */\n\n/**\n * Main hierarchical clustering function using agglomerative approach\n * \n * This implements the standard agglomerative hierarchical clustering algorithm:\n * 1. Start with each point as its own cluster\n * 2. Compute pairwise distances between all points\n * 3. Iteratively merge the two closest clusters\n * 4. Continue until only one cluster remains\n * \n * Algorithm complexity:\n * - Time: O(n³) - for each of n-1 iterations, scan all n² cluster pairs\n * - Space: O(n²) - store upper triangular distance matrix\n * \n * Memory optimization:\n * - Uses upper triangular matrix storage (50% memory reduction)\n * - Only stores distances where i < j since distance matrix is symmetric\n * \n * @param dataKeys Labels for each data point\n * @param dataValues Feature vectors for each data point (n×d matrix)\n * @param distanceType Distance metric to use between points\n * @param linkageType Linkage method to use between clusters\n * @returns Root cluster containing the complete hierarchy\n */\nexport function getClusters(dataValues: number[][], distanceType: DendrogramDistance = 'euclidean', linkageType: DendrogramLinkage = 'average'): Cluster {\n const distanceFn = getDistanceFn(distanceType);\n const linkageFn = getLinkageFn(linkageType);\n \n const N = dataValues.length;\n \n // STEP 1: Precompute pairwise distances using upper triangular matrix\n // This reduces memory usage from O(n²) to O(n²/2) by only storing upper triangle\n // Since distance matrix is symmetric: d(i,j) = d(j,i)\n const distances: number[] = [];\n \n // Helper function to access distances in upper triangular matrix\n // Maps 2D coordinates (i,j) to 1D array index\n const getDistance = (i: number, j: number): number => {\n if (i === j) return 0; // Distance to self is always 0\n if (i > j) [i, j] = [j, i]; // Swap to ensure i < j (upper triangular)\n const index = i * N - (i * (i + 1)) / 2 + (j - i - 1);\n return distances[index];\n };\n \n // Helper function to set distances in upper triangular matrix\n const setDistance = (i: number, j: number, value: number): void => {\n if (i > j) [i, j] = [j, i]; // Swap to ensure i < j (upper triangular)\n const index = i * N - (i * (i + 1)) / 2 + (j - i - 1);\n distances[index] = value;\n };\n \n // Calculate all pairwise distances (only upper triangular)\n // This is the most expensive step: O(n²) distance calculations\n for (let i1 = 0; i1 < N; i1++) {\n for (let i2 = i1 + 1; i2 < N; i2++) {\n const v = distanceFn(dataValues[i1], dataValues[i2]);\n setDistance(i1, i2, v);\n } \n }\n\n // STEP 2: Initialize clusters - each point starts as its own cluster\n // This creates the leaf nodes of the dendrogram\n const clusters: Cluster[] = dataValues.map((_, index) => ({\n height: 0, // Height 0 for leaf nodes\n indexes: [index], // Single index per cluster initially\n children: [] // No children for leaf nodes\n }));\n\n // STEP 3: Agglomerative clustering - merge clusters iteratively\n // This is the main clustering loop that builds the hierarchy\n for (let iteration = 0; iteration < N; iteration++) {\n // Stop when only one cluster remains (root of dendrogram)\n if (iteration >= N - 1) break;\n\n // Find the two closest clusters using the selected linkage method\n let nearestDistance = Infinity;\n let nearest1 = 0;\n let nearest2 = 0;\n\n // Check all pairs of clusters (upper triangular to avoid duplicates)\n for (let clusterIdx1 = 0; clusterIdx1 < clusters.length; clusterIdx1++) {\n for (let clusterIdx2 = clusterIdx1 + 1; clusterIdx2 < clusters.length; clusterIdx2++) {\n // Calculate distance between clusters using linkage method\n const distance = linkageFn(clusters[clusterIdx1].indexes, clusters[clusterIdx2].indexes, getDistance);\n \n // Keep track of the closest pair\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearest1 = clusterIdx1;\n nearest2 = clusterIdx2;\n }\n }\n }\n\n // STEP 4: Merge the two closest clusters\n // Create a new cluster that combines the two closest clusters\n const newCluster = {\n indexes: [...clusters[nearest1].indexes, ...clusters[nearest2].indexes], // Combine all indexes\n height: nearestDistance, // Height is the distance at which clusters were merged\n children: [clusters[nearest1], clusters[nearest2]], // Store the two merged clusters as children\n };\n\n // STEP 5: Update cluster list\n // Remove the two merged clusters and add the new merged cluster\n // Remove higher index first to avoid index shifting issues\n clusters.splice(Math.max(nearest1, nearest2), 1);\n clusters.splice(Math.min(nearest1, nearest2), 1);\n clusters.push(newCluster);\n }\n\n // STEP 6: Return the root cluster with properly ordered keys and indexes\n // The root cluster contains the complete hierarchy\n const rootCluster = clusters[0];\n return {\n ...rootCluster,\n indexes: extractOrderedIndexes(rootCluster) // Ensure indexes are in hierarchical order\n };\n}\n"],"names":["squaredEuclidean","p","q","d","i","euclidean","averageLinkage","setA","setB","getDistance","distance","a","b","completeLinkage","singleLinkage","getLinkageFn","linkage","getDistanceFn","extractOrderedIndexes","cluster","orderedIndexes","traverse","c","child","MAX_CLUSTER_SIZE","getClustersApproximate","dataValues","distanceType","linkageType","getClusters","N","getRandom","randomLcg","sampleIndices","sampleIndicesArray","sampleValues","sampleCluster","assignRemainingPoints","distanceFn","sampleSet","assignments","minDistance","nearestSample","sampleIdx","reconstructWithAssignments","sampleToCluster","findSampleClusters","assignedPointsByCluster","pointIdx","fillCorrectIndexes","assignedPoints","linkageFn","distances","j","index","setDistance","value","i1","i2","v","clusters","_","iteration","nearestDistance","nearest1","nearest2","clusterIdx1","clusterIdx2","newCluster","rootCluster"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1,48 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
function
|
|
4
|
-
const { facetKeys: K, xGroupKeys:
|
|
5
|
-
if (
|
|
1
|
+
import { getClustersApproximate as d } from "./getClusters.js";
|
|
2
|
+
import x from "../node_modules/d3-hierarchy/src/hierarchy/index.js";
|
|
3
|
+
function X(a, i, c, G) {
|
|
4
|
+
const { facetKeys: K, xGroupKeys: B, yGroupKeys: g } = a.meta, m = {};
|
|
5
|
+
if (i && G.sharedX)
|
|
6
6
|
throw Error("Dendrogram on X axis is not available with shared by facets X axis");
|
|
7
|
-
return K.forEach((
|
|
8
|
-
const
|
|
7
|
+
return K.forEach((s) => {
|
|
8
|
+
const f = {
|
|
9
9
|
hierarchyByGroupX: {},
|
|
10
10
|
hierarchyByGroupY: {}
|
|
11
|
-
}, o =
|
|
12
|
-
if (
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return ((c = o.cells[p][h]) == null ? void 0 : c.normalizedValue) ?? t.fillNA;
|
|
11
|
+
}, o = a.facets[s];
|
|
12
|
+
if (i) {
|
|
13
|
+
const n = [];
|
|
14
|
+
B.forEach((e) => {
|
|
15
|
+
const t = o.xKeysByGroups[e], p = o.yKeys, y = d(
|
|
16
|
+
t.map((r) => p.map((h) => {
|
|
17
|
+
var l;
|
|
18
|
+
return ((l = o.cells[r][h]) == null ? void 0 : l.normalizedValue) ?? i.fillNA;
|
|
20
19
|
})),
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
}),
|
|
20
|
+
i.distance,
|
|
21
|
+
i.linkage
|
|
22
|
+
), u = y.indexes.map((r) => t[r]);
|
|
23
|
+
a.facets[s].xKeysByGroups[e] = u, n.push(...u), f.hierarchyByGroupX[e] = x(y);
|
|
24
|
+
}), a.facets[s].xKeys = n;
|
|
26
25
|
}
|
|
27
|
-
if (
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return ((c = o.cells[h][p]) == null ? void 0 : c.normalizedValue) ?? n.fillNA;
|
|
26
|
+
if (c) {
|
|
27
|
+
const n = [];
|
|
28
|
+
g.forEach((e) => {
|
|
29
|
+
const t = o.yKeysByGroups[e], p = o.xKeys, y = d(
|
|
30
|
+
t.map((r) => p.map((h) => {
|
|
31
|
+
var l;
|
|
32
|
+
return ((l = o.cells[h][r]) == null ? void 0 : l.normalizedValue) ?? c.fillNA;
|
|
35
33
|
})),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
}),
|
|
34
|
+
c.distance,
|
|
35
|
+
c.linkage
|
|
36
|
+
), u = y.indexes.map((r) => t[r]);
|
|
37
|
+
a.facets[s].yKeysByGroups[e] = u, n.push(...u), f.hierarchyByGroupY[e] = x(y);
|
|
38
|
+
}), a.facets[s].yKeys = n;
|
|
41
39
|
}
|
|
42
|
-
|
|
43
|
-
}),
|
|
40
|
+
m[s] = f;
|
|
41
|
+
}), m;
|
|
44
42
|
}
|
|
45
43
|
export {
|
|
46
|
-
|
|
44
|
+
X as getDendrograms
|
|
47
45
|
};
|
|
48
46
|
//# sourceMappingURL=getDendrograms.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDendrograms.js","sources":["../../../../../../node_modules/@milaboratories/miplots4/src/heatmap/getDendrograms.ts"],"sourcesContent":["import type {Cluster} from './components/types';\nimport type {GroupedCellsData} from './getCells';\nimport type {HeatmapSettingsImpl} from './HeatmapSettingsImpl';\nimport {
|
|
1
|
+
{"version":3,"file":"getDendrograms.js","sources":["../../../../../../node_modules/@milaboratories/miplots4/src/heatmap/getDendrograms.ts"],"sourcesContent":["import type { Cluster } from './components/types';\nimport type { GroupedCellsData } from './getCells';\nimport type { HeatmapSettingsImpl } from './HeatmapSettingsImpl';\nimport { getClustersApproximate } from './getClusters';\nimport type { HierarchyNode } from 'd3-hierarchy';\nimport { hierarchy } from 'd3-hierarchy';\n\nexport type DendrogramsData = Record<string, {\n hierarchyByGroupX: Record<string, HierarchyNode<Cluster>>;\n hierarchyByGroupY: Record<string, HierarchyNode<Cluster>>;\n}>\n\nconst DEBUG = false;\n\nexport function getDendrograms(\n groupedCellsData: GroupedCellsData,\n dendrogramX: HeatmapSettingsImpl['dendrogramX'],\n dendrogramY: HeatmapSettingsImpl['dendrogramY'],\n facetSettings: HeatmapSettingsImpl['facetSettings']\n): DendrogramsData {\n const { facetKeys, xGroupKeys, yGroupKeys } = groupedCellsData.meta;\n const result: DendrogramsData = {};\n\n if (dendrogramX && facetSettings.sharedX) {\n throw Error('Dendrogram on X axis is not available with shared by facets X axis');\n }\n\n facetKeys.forEach(facetKey => {\n const facetResult: DendrogramsData[string] = {\n hierarchyByGroupX: {},\n hierarchyByGroupY: {},\n };\n const cellsGroup = groupedCellsData.facets[facetKey];\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.time('start x');\n }\n if (dendrogramX) {\n const updatedXKeys: string[] = [];\n xGroupKeys.forEach(xGroupKey => {\n const xKeys = cellsGroup.xKeysByGroups[xGroupKey];\n const yKeys = cellsGroup.yKeys;\n if(DEBUG) {\n // eslint-disable-next-line no-console\n console.log('xKeys source', xKeys.length);\n }\n const rootCluster = getClustersApproximate(\n xKeys.map(x => yKeys.map(y => (cellsGroup.cells[x][y]?.normalizedValue ?? dendrogramX.fillNA) as number)),\n dendrogramX.distance,\n dendrogramX.linkage\n );\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log('rootCluster', rootCluster);\n }\n //set sorted group keys\n const newXKeys = rootCluster.indexes.map(i => xKeys[i]);\n groupedCellsData.facets[facetKey].xKeysByGroups[xGroupKey] = newXKeys;\n updatedXKeys.push(...newXKeys);\n facetResult.hierarchyByGroupX[xGroupKey] = hierarchy(rootCluster);\n });\n // set sorted group keys of all groups\n groupedCellsData.facets[facetKey].xKeys = updatedXKeys;\n }\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.timeEnd('start x');\n }\n if (dendrogramY) {\n const updatedYKeys: string[] = [];\n yGroupKeys.forEach(yGroupKey => {\n const yKeys = cellsGroup.yKeysByGroups[yGroupKey];\n const xKeys = cellsGroup.xKeys;\n const rootCluster = getClustersApproximate(\n yKeys.map(y => xKeys.map(x => (cellsGroup.cells[x][y]?.normalizedValue ?? dendrogramY.fillNA) as number)),\n dendrogramY.distance,\n dendrogramY.linkage\n );\n const newYKeys = rootCluster.indexes.map(i => yKeys[i]);\n //set sorted group keys\n groupedCellsData.facets[facetKey].yKeysByGroups[yGroupKey] = newYKeys;\n updatedYKeys.push(...newYKeys);\n facetResult.hierarchyByGroupY[yGroupKey] = hierarchy(rootCluster);\n });\n // set sorted group keys of all groups\n groupedCellsData.facets[facetKey].yKeys = updatedYKeys;\n }\n result[facetKey] = facetResult;\n });\n\n return result;\n}\n"],"names":["getDendrograms","groupedCellsData","dendrogramX","dendrogramY","facetSettings","facetKeys","xGroupKeys","yGroupKeys","result","facetKey","facetResult","cellsGroup","updatedXKeys","xGroupKey","xKeys","yKeys","rootCluster","getClustersApproximate","x","y","_a","newXKeys","i","hierarchy","updatedYKeys","yGroupKey","newYKeys"],"mappings":";;AAcO,SAASA,EACZC,GACAC,GACAC,GACAC,GACe;AACf,QAAM,EAAE,WAAAC,GAAW,YAAAC,GAAY,YAAAC,MAAeN,EAAiB,MACzDO,IAA0B,CAAA;AAEhC,MAAIN,KAAeE,EAAc;AAC7B,UAAM,MAAM,oEAAoE;AAGpF,SAAAC,EAAU,QAAQ,CAAAI,MAAY;AAC1B,UAAMC,IAAuC;AAAA,MACzC,mBAAmB,CAAA;AAAA,MACnB,mBAAmB,CAAA;AAAA,IAAA,GAEjBC,IAAaV,EAAiB,OAAOQ,CAAQ;AAKnD,QAAIP,GAAa;AACb,YAAMU,IAAyB,CAAA;AAC/BN,MAAAA,EAAW,QAAQ,CAAAO,MAAa;AAC5B,cAAMC,IAAQH,EAAW,cAAcE,CAAS,GAC1CE,IAAQJ,EAAW,OAKnBK,IAAcC;AAAAA,UAChBH,EAAM,IAAI,CAAAI,MAAKH,EAAM,IAAI,CAAAI,MAAA;;AAAM,qBAAAC,IAAAT,EAAW,MAAMO,CAAC,EAAEC,CAAC,MAArB,OAAA,SAAAC,EAAwB,oBAAmBlB,EAAY;AAAA,UAAA,CAAiB,CAAC;AAAA,UACxGA,EAAY;AAAA,UACZA,EAAY;AAAA,QAAA,GAOVmB,IAAWL,EAAY,QAAQ,IAAI,CAAAM,MAAKR,EAAMQ,CAAC,CAAC;AACtDrB,QAAAA,EAAiB,OAAOQ,CAAQ,EAAE,cAAcI,CAAS,IAAIQ,GAC7DT,EAAa,KAAK,GAAGS,CAAQ,GAC7BX,EAAY,kBAAkBG,CAAS,IAAIU,EAAUP,CAAW;AAAA,MACpE,CAAC,GAEDf,EAAiB,OAAOQ,CAAQ,EAAE,QAAQG;AAAAA,IAC9C;AAKA,QAAIT,GAAa;AACb,YAAMqB,IAAyB,CAAA;AAC/BjB,MAAAA,EAAW,QAAQ,CAAAkB,MAAa;AAC5B,cAAMV,IAAQJ,EAAW,cAAcc,CAAS,GAC1CX,IAAQH,EAAW,OACnBK,IAAcC;AAAAA,UAChBF,EAAM,IAAI,CAAAI,MAAKL,EAAM,IAAI,CAAAI,MAAA;;AAAM,qBAAAE,IAAAT,EAAW,MAAMO,CAAC,EAAEC,CAAC,MAArB,OAAA,SAAAC,EAAwB,oBAAmBjB,EAAY;AAAA,UAAA,CAAiB,CAAC;AAAA,UACxGA,EAAY;AAAA,UACZA,EAAY;AAAA,QAAA,GAEVuB,IAAWV,EAAY,QAAQ,IAAI,CAAAM,MAAKP,EAAMO,CAAC,CAAC;AAEtDrB,QAAAA,EAAiB,OAAOQ,CAAQ,EAAE,cAAcgB,CAAS,IAAIC,GAC7DF,EAAa,KAAK,GAAGE,CAAQ,GAC7BhB,EAAY,kBAAkBe,CAAS,IAAIF,EAAUP,CAAW;AAAA,MACpE,CAAC,GAEDf,EAAiB,OAAOQ,CAAQ,EAAE,QAAQe;AAAAA,IAC9C;AACAhB,IAAAA,EAAOC,CAAQ,IAAIC;AAAAA,EACvB,CAAC,GAEMF;AACX;","x_google_ignoreList":[0]}
|
|
@@ -51,8 +51,8 @@ class at extends H {
|
|
|
51
51
|
dendrogramX: Y,
|
|
52
52
|
dendrogramY: E,
|
|
53
53
|
normalization: _,
|
|
54
|
-
NAValueAs:
|
|
55
|
-
} = e,
|
|
54
|
+
NAValueAs: k
|
|
55
|
+
} = e, w = Object.values(((a = t.dendrogramX) == null ? void 0 : a.aes) || {}).filter(
|
|
56
56
|
x
|
|
57
57
|
), N = Object.values((Y == null ? void 0 : Y.aes) || {}).filter(x), T = Object.values(((r = t.dendrogramY) == null ? void 0 : r.aes) || {}).filter(
|
|
58
58
|
x
|
|
@@ -63,7 +63,7 @@ class at extends H {
|
|
|
63
63
|
return t.xColumn.value !== p.value || t.yColumn.value !== D.value || t.valueColumn.value !== f.value || y(t.xGroupBy, A) || y(t.yGroupBy, X) || y(t.facetBy, b) || y(
|
|
64
64
|
t.annotations.map((C) => C.valueColumn),
|
|
65
65
|
S.map((C) => C.valueColumn)
|
|
66
|
-
) || y(
|
|
66
|
+
) || y(w, N) || y(T, U) || (e.dendrogramX || t.dendrogramX) && (((n = t.dendrogramX) == null ? void 0 : n.distance) !== ((o = e.dendrogramX) == null ? void 0 : o.distance) || ((s = t.dendrogramX) == null ? void 0 : s.linkage) !== ((m = e.dendrogramX) == null ? void 0 : m.linkage)) || (e.dendrogramY || t.dendrogramY) && (((c = t.dendrogramY) == null ? void 0 : c.distance) !== ((i = e.dendrogramY) == null ? void 0 : i.distance) || ((d = t.dendrogramY) == null ? void 0 : d.linkage) !== ((g = e.dendrogramY) == null ? void 0 : g.linkage)) || t.chartSettings.valueType !== u.valueType || t.facetSettings.sharedX !== G.sharedX || t.facetSettings.sharedY !== G.sharedY || ((l = t.normalization) == null ? void 0 : l.method) !== (_ == null ? void 0 : _.method) || ((v = t.normalization) == null ? void 0 : v.direction) !== (_ == null ? void 0 : _.direction) || t.NAValueAs !== k;
|
|
67
67
|
}
|
|
68
68
|
_needUpdateCalculatedDataByData(t, e) {
|
|
69
69
|
const a = Object.keys(t.data), r = Object.keys(e.data);
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { AxisId as
|
|
2
|
-
import
|
|
1
|
+
import { AxisId as g, isColumnId as D, ColumnId as I } from "../spec.js";
|
|
2
|
+
import E from "../_virtual/lodash.js";
|
|
3
3
|
import "../node_modules/@milaboratories/pl-model-common/dist/drivers/blob.js";
|
|
4
|
-
import { pTableValue as
|
|
4
|
+
import { pTableValue as M } from "../node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/data_types.js";
|
|
5
5
|
import "../node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/spec.js";
|
|
6
6
|
import "../_virtual/canonicalize.js";
|
|
7
7
|
import "../node_modules/@milaboratories/pl-model-common/dist/plid.js";
|
|
8
8
|
import "../node_modules/@milaboratories/pl-model-common/dist/ref.js";
|
|
9
|
-
import { IS_DENSE_AXIS as
|
|
10
|
-
var
|
|
11
|
-
const
|
|
12
|
-
function
|
|
13
|
-
if (
|
|
9
|
+
import { IS_DENSE_AXIS as T, TREAT_ABSENT_VALUE_AS as V } from "../constants.js";
|
|
10
|
+
var N = Object.defineProperty, R = (S, e, t) => e in S ? N(S, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : S[e] = t, H = (S, e, t) => R(S, typeof e != "symbol" ? e + "" : e, t);
|
|
11
|
+
const B = 1e6;
|
|
12
|
+
function A(S, e, t = null) {
|
|
13
|
+
if (S === "String")
|
|
14
14
|
return e.data;
|
|
15
15
|
const a = new Array(e.data.length);
|
|
16
|
-
for (let
|
|
17
|
-
a[
|
|
16
|
+
for (let n = 0; n < e.data.length; n++)
|
|
17
|
+
a[n] = M(e, n, { absent: t, na: null });
|
|
18
18
|
return a;
|
|
19
19
|
}
|
|
20
20
|
class G {
|
|
21
21
|
constructor(e, t) {
|
|
22
|
-
|
|
22
|
+
H(this, "pframeHandle"), H(this, "pframeDriver"), this.pframeHandle = e, this.pframeDriver = t;
|
|
23
23
|
}
|
|
24
24
|
async isColumnExisted(e) {
|
|
25
25
|
return !!await this.getColumnSpecById(e);
|
|
@@ -31,70 +31,70 @@ class G {
|
|
|
31
31
|
data: []
|
|
32
32
|
};
|
|
33
33
|
try {
|
|
34
|
-
const a = Date.now(),
|
|
34
|
+
const a = Date.now(), n = await this.pframeDriver.calculateTableData(this.pframeHandle, {
|
|
35
35
|
src: {
|
|
36
36
|
type: "column",
|
|
37
37
|
column: e.name
|
|
38
38
|
},
|
|
39
39
|
filters: t,
|
|
40
40
|
sorting: []
|
|
41
|
-
}),
|
|
41
|
+
}), r = n.filter((s) => s.spec.type === "axis"), o = n.filter((s) => s.spec.type === "column");
|
|
42
42
|
return {
|
|
43
|
-
axesData:
|
|
44
|
-
const c =
|
|
45
|
-
return s[c.toCanonicalString()] =
|
|
43
|
+
axesData: r.reduce((s, u) => {
|
|
44
|
+
const c = g.fromAxisSpec(u.spec.spec);
|
|
45
|
+
return s[c.toCanonicalString()] = A(c.type, u.data), s;
|
|
46
46
|
}, {}),
|
|
47
|
-
data: o.length ?
|
|
47
|
+
data: o.length ? A(e.type, o[0].data) : []
|
|
48
48
|
};
|
|
49
49
|
} catch (a) {
|
|
50
50
|
throw console.error("PFrame: calculateTableData error"), a;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
async getColumnUniqueValues(e, t =
|
|
53
|
+
async getColumnUniqueValues(e, t = B, a = []) {
|
|
54
54
|
if (!await this.isColumnExisted(e))
|
|
55
55
|
return { values: [], overflow: !1 };
|
|
56
|
-
const
|
|
56
|
+
const n = {
|
|
57
57
|
columnId: e.name,
|
|
58
58
|
filters: a,
|
|
59
59
|
limit: t
|
|
60
60
|
};
|
|
61
61
|
try {
|
|
62
|
-
const
|
|
62
|
+
const r = Date.now(), o = await this.pframeDriver.getUniqueValues(this.pframeHandle, n);
|
|
63
63
|
let s = !1;
|
|
64
64
|
return o.overflow && (s = !0, console.warn(`More than ${t} values for ${e.name} column`)), {
|
|
65
65
|
values: Array.from(o.values.data).map(String),
|
|
66
66
|
overflow: s
|
|
67
67
|
};
|
|
68
|
-
} catch (
|
|
69
|
-
throw console.error("PFrame: getUniqueValues for column error"),
|
|
68
|
+
} catch (r) {
|
|
69
|
+
throw console.error("PFrame: getUniqueValues for column error"), r;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
async getAxisUniqueValues(e, t, a =
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
return
|
|
72
|
+
async getAxisUniqueValues(e, t, a = B, n = []) {
|
|
73
|
+
const r = t.filter(D), o = await Promise.all(r.map((c) => this.getColumnSpecById(c))), s = e.toCanonicalString(), u = r.filter((c, h) => {
|
|
74
|
+
const p = o[h];
|
|
75
|
+
return p !== null && p.axesSpec.some((f) => g.fromAxisSpec(f).toCanonicalString() === s);
|
|
76
76
|
});
|
|
77
|
-
if (
|
|
77
|
+
if (u.length === 0)
|
|
78
78
|
return console.warn("Axis unique values requested without parent columns"), { values: [], overflow: !1 };
|
|
79
79
|
try {
|
|
80
|
-
const c = Date.now(),
|
|
81
|
-
|
|
82
|
-
(
|
|
83
|
-
columnId:
|
|
80
|
+
const c = Date.now(), h = await Promise.all(
|
|
81
|
+
u.map(
|
|
82
|
+
(f) => this.pframeDriver.getUniqueValues(this.pframeHandle, {
|
|
83
|
+
columnId: f.name,
|
|
84
84
|
axis: e.toPFrameId(),
|
|
85
|
-
filters:
|
|
85
|
+
filters: n,
|
|
86
86
|
limit: a
|
|
87
87
|
})
|
|
88
88
|
)
|
|
89
89
|
);
|
|
90
|
-
let
|
|
91
|
-
return
|
|
92
|
-
|
|
90
|
+
let p = !1;
|
|
91
|
+
return h.forEach((f, F) => {
|
|
92
|
+
f.overflow && (p = !0, console.warn(`More than ${a} values for ${u[F].name} column`));
|
|
93
93
|
}), {
|
|
94
|
-
values:
|
|
95
|
-
|
|
94
|
+
values: E.uniq(
|
|
95
|
+
E.flatten(h.map((f) => Array.from(f.values.data).map(String)))
|
|
96
96
|
),
|
|
97
|
-
overflow:
|
|
97
|
+
overflow: p
|
|
98
98
|
};
|
|
99
99
|
} catch (c) {
|
|
100
100
|
return console.error("PFrame: getUniqueValues for axis error", c), { values: [], overflow: !1 };
|
|
@@ -102,80 +102,111 @@ class G {
|
|
|
102
102
|
}
|
|
103
103
|
// Special fake columns that are created basing on some existing column marked with special annotation;
|
|
104
104
|
// these columns contain all the combination of its axes by ids;
|
|
105
|
-
|
|
106
|
-
return
|
|
107
|
-
if (!
|
|
108
|
-
return
|
|
109
|
-
const
|
|
110
|
-
return
|
|
111
|
-
var
|
|
112
|
-
((
|
|
113
|
-
}),
|
|
105
|
+
getArtificialColumns(e, t) {
|
|
106
|
+
return t.reduce((a, n, r) => {
|
|
107
|
+
if (!n)
|
|
108
|
+
return a;
|
|
109
|
+
const o = [];
|
|
110
|
+
return n.axesSpec.forEach((s, u) => {
|
|
111
|
+
var c;
|
|
112
|
+
((c = s.annotations) == null ? void 0 : c[T]) === "true" && o.push(u);
|
|
113
|
+
}), o.length && a.push({
|
|
114
114
|
type: "artificialColumn",
|
|
115
115
|
column: e[r].name,
|
|
116
116
|
newId: e[r].name + "1",
|
|
117
|
-
axesIndices:
|
|
118
|
-
}),
|
|
117
|
+
axesIndices: o
|
|
118
|
+
}), a;
|
|
119
119
|
}, []);
|
|
120
120
|
}
|
|
121
|
-
async getTableOuterJoin(e, t, a = [],
|
|
122
|
-
|
|
121
|
+
async getTableOuterJoin(e, t, a = [], n = !0) {
|
|
122
|
+
var r;
|
|
123
|
+
const o = e.filter(D), s = t.filter(D), u = await Promise.all(e.map((l) => this.getColumnSpecById(l))), c = await Promise.all(t.map((l) => this.getColumnSpecById(l))), h = this.getArtificialColumns(o, u), p = [], f = {}, F = new Set(u.flatMap((l) => ((l == null ? void 0 : l.axesSpec) ?? []).map((v) => g.fromAxisSpec(v).toCanonicalString())));
|
|
124
|
+
for (const l of a) {
|
|
125
|
+
if (!(l.predicate.operator === "Or" && l.column.type === "axis")) {
|
|
126
|
+
p.push(l);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const v = new g(l.column.id).toCanonicalString();
|
|
130
|
+
if (F.has(v))
|
|
131
|
+
p.push(l);
|
|
132
|
+
else {
|
|
133
|
+
const y = s.findIndex((w, i) => {
|
|
134
|
+
var m;
|
|
135
|
+
return (m = c[i]) == null ? void 0 : m.axesSpec.some((d) => g.fromAxisSpec(d).toCanonicalString() === v);
|
|
136
|
+
});
|
|
137
|
+
if (y !== -1) {
|
|
138
|
+
const w = s[y].name;
|
|
139
|
+
f[w] || (f[w] = {
|
|
140
|
+
type: "slicedColumn",
|
|
141
|
+
newId: w,
|
|
142
|
+
column: w,
|
|
143
|
+
axisFilters: []
|
|
144
|
+
});
|
|
145
|
+
const i = (r = c[y]) == null ? void 0 : r.axesSpec.findIndex((m) => g.fromAxisSpec(m).toCanonicalString() === v);
|
|
146
|
+
l.predicate.operands[0].operator === "Equal" && l.predicate.operands[0].reference !== void 0 && f[w].axisFilters.push({
|
|
147
|
+
type: "constant",
|
|
148
|
+
axisIndex: i ?? 0,
|
|
149
|
+
constant: l.predicate.operands[0].reference
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const b = {
|
|
123
155
|
src: {
|
|
124
156
|
type: "outer",
|
|
125
157
|
primary: {
|
|
126
158
|
type: "full",
|
|
127
|
-
//primaryInnerJoin ? 'inner' : 'full',
|
|
128
159
|
entries: [
|
|
129
|
-
...
|
|
160
|
+
...o.map((l) => ({
|
|
130
161
|
type: "column",
|
|
131
|
-
column:
|
|
162
|
+
column: l.name
|
|
132
163
|
})),
|
|
133
|
-
...
|
|
164
|
+
...h
|
|
134
165
|
]
|
|
135
166
|
},
|
|
136
|
-
secondary:
|
|
137
|
-
|
|
167
|
+
secondary: s.map((l, v) => {
|
|
168
|
+
const y = f[l.name];
|
|
169
|
+
return y && y.axisFilters.length ? y : {
|
|
138
170
|
type: "column",
|
|
139
|
-
column:
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
]
|
|
171
|
+
column: l.name
|
|
172
|
+
};
|
|
173
|
+
})
|
|
143
174
|
},
|
|
144
|
-
filters:
|
|
175
|
+
filters: p,
|
|
145
176
|
sorting: []
|
|
146
177
|
};
|
|
147
178
|
try {
|
|
148
|
-
const
|
|
179
|
+
const l = Date.now(), v = await this.pframeDriver.calculateTableData(this.pframeHandle, b), y = v.filter((i) => i.spec.type === "axis"), w = v.filter((i) => i.spec.type === "column");
|
|
149
180
|
return {
|
|
150
|
-
axesData:
|
|
151
|
-
const d =
|
|
152
|
-
return
|
|
181
|
+
axesData: y.reduce((i, m) => {
|
|
182
|
+
const d = g.fromAxisSpec(m.spec.spec);
|
|
183
|
+
return i[d.toCanonicalString()] = A(d.type, m.data), i;
|
|
153
184
|
}, {}),
|
|
154
|
-
columnsData:
|
|
155
|
-
var d,
|
|
156
|
-
const
|
|
157
|
-
name:
|
|
158
|
-
type:
|
|
159
|
-
}),
|
|
160
|
-
var
|
|
161
|
-
return ((
|
|
162
|
-
}),
|
|
163
|
-
return
|
|
185
|
+
columnsData: w.reduce((i, m) => {
|
|
186
|
+
var d, C;
|
|
187
|
+
const x = m.spec.spec, P = new I({
|
|
188
|
+
name: m.spec.id,
|
|
189
|
+
type: x.valueType
|
|
190
|
+
}), $ = x.axesSpec.some((_) => {
|
|
191
|
+
var q;
|
|
192
|
+
return ((q = _.annotations) == null ? void 0 : q[T]) === "true";
|
|
193
|
+
}), U = (d = x.annotations) != null && d[V] ? Number((C = x.annotations) == null ? void 0 : C[V]) : $ ? 0 : null;
|
|
194
|
+
return i[P.toCanonicalString()] = A(P.type, m.data, U), i;
|
|
164
195
|
}, {}),
|
|
165
|
-
columnSpecs:
|
|
166
|
-
const d =
|
|
167
|
-
name:
|
|
196
|
+
columnSpecs: w.reduce((i, m) => {
|
|
197
|
+
const d = m.spec.spec, C = new I({
|
|
198
|
+
name: m.spec.id,
|
|
168
199
|
type: d.valueType
|
|
169
200
|
});
|
|
170
|
-
return
|
|
201
|
+
return i[C.toCanonicalString()] = d, i;
|
|
171
202
|
}, {}),
|
|
172
|
-
axesSpecs:
|
|
173
|
-
const d =
|
|
174
|
-
return
|
|
203
|
+
axesSpecs: y.reduce((i, m) => {
|
|
204
|
+
const d = m.spec.spec, C = g.fromAxisSpec(d);
|
|
205
|
+
return i[C.toCanonicalString()] = d, i;
|
|
175
206
|
}, {})
|
|
176
207
|
};
|
|
177
|
-
} catch (
|
|
178
|
-
throw console.error("PFrame: table outer join error"), console.info("error with request: ",
|
|
208
|
+
} catch (l) {
|
|
209
|
+
throw console.error("PFrame: table outer join error"), console.info("error with request: ", b), l;
|
|
179
210
|
}
|
|
180
211
|
}
|
|
181
212
|
async getColumnSpecById(e) {
|
|
@@ -188,66 +219,66 @@ class G {
|
|
|
188
219
|
}
|
|
189
220
|
async getColumnAxesIds(e) {
|
|
190
221
|
var t;
|
|
191
|
-
return ((t = await this.getColumnSpecById(e)) == null ? void 0 : t.axesSpec.map((a) =>
|
|
222
|
+
return ((t = await this.getColumnSpecById(e)) == null ? void 0 : t.axesSpec.map((a) => g.fromAxisSpec(a))) ?? [];
|
|
192
223
|
}
|
|
193
224
|
async getRequestColumnsFromSelectedSources(e) {
|
|
194
225
|
var t;
|
|
195
226
|
const a = [];
|
|
196
|
-
for (const
|
|
197
|
-
if (
|
|
198
|
-
const
|
|
199
|
-
a.push(...
|
|
227
|
+
for (const n of e)
|
|
228
|
+
if (D(n)) {
|
|
229
|
+
const r = ((t = await this.getColumnSpecById(n)) == null ? void 0 : t.axesSpec) ?? [];
|
|
230
|
+
a.push(...r.map((o) => g.fromAxisSpec(o).toPFrameId()));
|
|
200
231
|
}
|
|
201
232
|
return a;
|
|
202
233
|
}
|
|
203
234
|
async getColumnsList() {
|
|
204
235
|
return this.pframeDriver.listColumns(this.pframeHandle);
|
|
205
236
|
}
|
|
206
|
-
async getColumnsFull(e, t, a,
|
|
237
|
+
async getColumnsFull(e, t, a, n, r, o) {
|
|
207
238
|
try {
|
|
208
239
|
const s = {
|
|
209
240
|
columnFilter: {
|
|
210
241
|
type: a,
|
|
211
|
-
name:
|
|
212
|
-
annotationValue:
|
|
213
|
-
annotationPattern: o == null ? void 0 : o.reduce((
|
|
242
|
+
name: n,
|
|
243
|
+
annotationValue: r,
|
|
244
|
+
annotationPattern: o == null ? void 0 : o.reduce((p, f) => (p[f] = ".+", p), {})
|
|
214
245
|
},
|
|
215
246
|
compatibleWith: await this.getRequestColumnsFromSelectedSources(e),
|
|
216
247
|
strictlyCompatible: t
|
|
217
248
|
// should be true if we want to get meta and false if X/Y
|
|
218
|
-
},
|
|
219
|
-
return c.hits.forEach((
|
|
220
|
-
|
|
221
|
-
}),
|
|
249
|
+
}, u = Date.now(), c = await this.pframeDriver.findColumns(this.pframeHandle, s), h = [];
|
|
250
|
+
return c.hits.forEach((p) => {
|
|
251
|
+
h.push(p);
|
|
252
|
+
}), h;
|
|
222
253
|
} catch (s) {
|
|
223
254
|
throw console.error("PFrame: findColumns error"), s;
|
|
224
255
|
}
|
|
225
256
|
}
|
|
226
|
-
async getColumns(e, t, a,
|
|
227
|
-
return (await this.getColumnsFull(e, t, a,
|
|
228
|
-
(o) => new
|
|
257
|
+
async getColumns(e, t, a, n, r) {
|
|
258
|
+
return (await this.getColumnsFull(e, t, a, n, r)).map(
|
|
259
|
+
(o) => new I({
|
|
229
260
|
name: o.columnId,
|
|
230
261
|
type: o.spec.valueType
|
|
231
262
|
})
|
|
232
263
|
);
|
|
233
264
|
}
|
|
234
|
-
async findColumnBy(e, t, a,
|
|
265
|
+
async findColumnBy(e, t, a, n) {
|
|
235
266
|
try {
|
|
236
|
-
const
|
|
267
|
+
const r = {
|
|
237
268
|
columnFilter: {
|
|
238
269
|
name: [e],
|
|
239
270
|
...t ? { type: [t] } : {},
|
|
240
271
|
...a ? { annotationValue: a } : {},
|
|
241
|
-
...
|
|
272
|
+
...n ? { domainValue: n } : {}
|
|
242
273
|
},
|
|
243
274
|
compatibleWith: [],
|
|
244
275
|
strictlyCompatible: !1
|
|
245
|
-
}, o = Date.now(), s = await this.pframeDriver.findColumns(this.pframeHandle,
|
|
246
|
-
return s.hits.forEach(({ columnId: c, spec:
|
|
247
|
-
|
|
248
|
-
}),
|
|
249
|
-
} catch (
|
|
250
|
-
throw console.error("PFrame: findColumns error"),
|
|
276
|
+
}, o = Date.now(), s = await this.pframeDriver.findColumns(this.pframeHandle, r), u = [];
|
|
277
|
+
return s.hits.forEach(({ columnId: c, spec: h }) => {
|
|
278
|
+
u.push(new I({ name: c, type: h.valueType }));
|
|
279
|
+
}), u.length > 1 && console.warn(`More than 1 column found for ${e}}`), u.length === 0 ? (console.warn(`No columns found for ${e}}`), null) : u[0];
|
|
280
|
+
} catch (r) {
|
|
281
|
+
throw console.error("PFrame: findColumns error"), r;
|
|
251
282
|
}
|
|
252
283
|
}
|
|
253
284
|
}
|