mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-24 03:28:12 +08:00
* revert all commits to before Typescript migration * update compat workflow to match latest deps (#2335) * update compat workflow to match latest deps * attempt to debug * attempt to debug * remove debugging code * typo * update deps to match desktop (#2340) * fix: don't run lint with `--fix` on push tests (#2273) * fix: don't run lint with `--fix` on push tests * npx Co-authored-by: maniarathi <mani.arathi@gmail.com> Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com> * rename X_approx_distribution to X_approximate_distribution (#2337) * Correctly handle non-finite numbers in heuristic determination of X distribution (#2342) * handle non-finites explicitly * improve and test edge case handling for distribution estimation * revert debugging changes * code readability * clean up type inferencing (#2332) * unit tests for 64 bit conversion * clean up type handling * type inference tests * more type inference fixes * use schema to determine user intent for data typing * stop using deprecated API * fbs type encoding test * add missing test * add more tests * correctly infer X type for CXG adaptor * lint * fix typo * ts migration * cleanup from PR review * lint * PR review changes * remove unused packages from client (#2359) * remove unused packages from client * add missing peer dep * fix: disable FE auth testing on compatibility tests (#2377) * update: release process (#2277) Co-authored-by: maniarathi <mani.arathi@gmail.com> * fix: remove spaces in param setup (#2380) * delete deploy workflow (#2396) * undo reformatting which now does not pass lint * fix snapshots which changed due to npm dep changes * add missing quoting to snapshot * another snapshot typo fix * TS Revert (2) - replay PR #2347 and #2354 (#2403) * replay edits from PR 2347 * TS Revert (3) - replay edits in PR #2327 (#2404) * replay edits in PR 2327 * TS Revert (4) - replay PR #2355 (#2405) * replay edits in PR 2355 * add additional babel config * reformat with new prettier config Co-authored-by: Severiano Badajoz <sbadajoz@chanzuckerberg.com> Co-authored-by: maniarathi <mani.arathi@gmail.com> Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>
176 lines
5.2 KiB
JavaScript
176 lines
5.2 KiB
JavaScript
/*
|
|
The annoMatrix view stack has a set of conventions which are assumed elsewhere in the
|
|
application. These helper functions make it simple for action creators to manage
|
|
the stack.
|
|
|
|
The annoMatrix module does not care about this order, but we maintain it as
|
|
a convention to make it simpler to manipulate the views.
|
|
|
|
Terminology:
|
|
- clip view: AnnoMatrixClipView
|
|
- subset view: AnnoMatrixRowSubsetView
|
|
- user subset view: create by the user explicitly subsetting by selection
|
|
- embedding subset view: implicitly created by switching the current embedding
|
|
- loader, or base annoMatrix: the root, which loads data
|
|
|
|
Rules:
|
|
1. there will be zero or one clip view
|
|
2. there will be zero or more subset views
|
|
3. there will be zero or one embedding view
|
|
4. there will be one loader/base, which is always the bottom view
|
|
5. the view ordering MUST be (top to bottom):
|
|
|
|
[clip] -> [user subset] -> [embedding subset] -> loader
|
|
|
|
There is code elsewhere in the app (eg, menubar/clip.js) which assumes this order.
|
|
|
|
Views can be interogated for their type with the following:
|
|
|
|
* is a view: annoMatrix.isView
|
|
* is the loader: !anonMatrix.isView (or annoMatrix === annoMatrix.base())
|
|
* is a clip view: annoMatrix.isClipped (or annoMatrix.clipRange)
|
|
* is a subset view: (annoMatrix.isView && !annoMatrix.isClipped)
|
|
* is a user subset view: annoMatrix.userFlags?.isUserSubsetView
|
|
* is an embedding subset view: annomatrix.userFlags?.isEmbSubsetView
|
|
|
|
*/
|
|
|
|
import { clip, isubsetMask, isubset } from "../../annoMatrix";
|
|
import { memoize } from "../dataframe/util";
|
|
|
|
export function _clipAnnoMatrix(annoMatrix, min, max) {
|
|
/*
|
|
clip the annoMatrix.
|
|
*/
|
|
return annoMatrix.isClipped
|
|
? clip(annoMatrix.viewOf, min, max)
|
|
: clip(annoMatrix, min, max);
|
|
}
|
|
|
|
export function _userSubsetAnnoMatrix(annoMatrix, mask) {
|
|
/*
|
|
user-requested row subset of annoMatrix, to be added on top of any
|
|
other previous row subsets.
|
|
*/
|
|
const { clipRange } = annoMatrix;
|
|
if (clipRange) {
|
|
annoMatrix = annoMatrix.viewOf;
|
|
}
|
|
|
|
annoMatrix = isubsetMask(annoMatrix, mask);
|
|
annoMatrix.userFlags.isUserSubsetView = true;
|
|
|
|
if (clipRange) {
|
|
annoMatrix = clip(annoMatrix, ...clipRange);
|
|
}
|
|
|
|
return annoMatrix;
|
|
}
|
|
|
|
export function _userResetSubsetAnnoMatrix(annoMatrix) {
|
|
/*
|
|
Reset/remove all user-requested subsets. Do not remove clip or embedding subset.
|
|
*/
|
|
|
|
/* stash clipping info, if any */
|
|
const { clipRange } = annoMatrix;
|
|
if (clipRange) {
|
|
annoMatrix = annoMatrix.viewOf;
|
|
}
|
|
|
|
/* pop all views except embedding subset and loader */
|
|
while (annoMatrix.isView && annoMatrix.userFlags.isUserSubsetView) {
|
|
annoMatrix = annoMatrix.viewOf;
|
|
}
|
|
|
|
/* re-apply the clip, if any */
|
|
if (clipRange) {
|
|
annoMatrix = clip(annoMatrix, ...clipRange);
|
|
}
|
|
|
|
return annoMatrix;
|
|
}
|
|
|
|
export function _setEmbeddingSubset(annoMatrix, embeddingDf) {
|
|
/*
|
|
Set the embedding subset view. Only create a subset view for the embedding
|
|
when it is needed, ie, there are NaN values in the embeddings.
|
|
*/
|
|
const embRowOffsets = _getEmbeddingRowOffsets(
|
|
annoMatrix.rowIndex,
|
|
embeddingDf
|
|
);
|
|
|
|
const curEmbSubsetView = getEmbSubsetView(annoMatrix);
|
|
|
|
/* if no current embedding subset, and no new embedding subset, just noop */
|
|
if (!embRowOffsets && !curEmbSubsetView) return annoMatrix;
|
|
|
|
// ... otherwise, do the work
|
|
|
|
/* stash clipping info, if any */
|
|
const clipRange = annoMatrix.isClipped ? annoMatrix.clipRange : null;
|
|
|
|
/* pop all subsets, user or embedding */
|
|
while (annoMatrix.isView) {
|
|
annoMatrix = annoMatrix.viewOf;
|
|
}
|
|
|
|
/* apply new embedding row index, if needed */
|
|
if (embRowOffsets) {
|
|
annoMatrix = isubset(annoMatrix, embRowOffsets);
|
|
annoMatrix.userFlags.isEmbSubsetView = true;
|
|
}
|
|
|
|
/* re-apply clip, if needed */
|
|
if (clipRange) {
|
|
annoMatrix = clip(annoMatrix, ...clipRange);
|
|
}
|
|
|
|
return annoMatrix;
|
|
}
|
|
|
|
function _getEmbeddingRowOffsets(baseRowIndex, embeddingDf) {
|
|
/*
|
|
given a dataframe containing an embedding:
|
|
- if the embedding contains no NaN coordinates, return null
|
|
- if the embedding contains NaN coordinates, return a rowIndex
|
|
that contains only the rows with discrete valued coordinates.
|
|
|
|
Currently assumes that there will be onl two dimensions in the embedding.
|
|
*/
|
|
const X = embeddingDf.icol(0).asArray();
|
|
const Y = embeddingDf.icol(1).asArray();
|
|
const offsets = new Int32Array(X.length);
|
|
let numOffsets = 0;
|
|
|
|
for (let i = 0, l = X.length; i < l; i += 1) {
|
|
if (!Number.isNaN(X[i]) && !Number.isNaN(Y[i])) {
|
|
offsets[numOffsets] = i;
|
|
numOffsets += 1;
|
|
}
|
|
}
|
|
|
|
if (numOffsets === X.length) return null;
|
|
return offsets.subarray(0, numOffsets);
|
|
}
|
|
|
|
export function _getDiscreteCellEmbeddingRowIndex(embeddingDf) {
|
|
const idx = _getEmbeddingRowOffsets(embeddingDf.rowIndex, embeddingDf);
|
|
if (idx === null) return embeddingDf.rowIndex;
|
|
return embeddingDf.rowIndex.isubset(idx);
|
|
}
|
|
export const getDiscreteCellEmbeddingRowIndex = memoize(
|
|
_getDiscreteCellEmbeddingRowIndex,
|
|
(df) => df.__id
|
|
);
|
|
|
|
export function getEmbSubsetView(annoMatrix) {
|
|
/* if there is an embedding subset in the view stack, return it. Falsish if not. */
|
|
while (annoMatrix.isView) {
|
|
if (annoMatrix.userFlags.isEmbSubsetView) return annoMatrix;
|
|
annoMatrix = annoMatrix.viewOf;
|
|
}
|
|
return undefined;
|
|
}
|