mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-08 03:48:12 +08:00
Add user-defined category-label colors (#1402)
* Add user-defined category-label colors Fixes https://github.com/chanzuckerberg/cellxgene/issues/1152 As described in https://github.com/chanzuckerberg/cellxgene/issues/1307 * Respond to feedback from @bkmartinjr in nodejs * Respond to feedback from @bkmartinjr in python * Add tests to the server module * Autoformat python, run linter * Make colors_get error handling specific * Respond to feedback from @bkmartinjr * Respond to feedback from @bkmartinjr * Fix whitespace * Fix python lint errrors * Update documentation * Add --disable-user-colors option to launch and cxgtool.py * Fix python formatting * Rename '--disable-user-colors' to '--disable-custom-colors'
This commit is contained in:
+60
-68
@@ -9,12 +9,13 @@ import {
|
|||||||
} from "../util/actionHelpers";
|
} from "../util/actionHelpers";
|
||||||
import { PromiseLimit } from "../util/promiseLimit";
|
import { PromiseLimit } from "../util/promiseLimit";
|
||||||
import { requestReembed, reembedResetWorldToUniverse } from "./reembed";
|
import { requestReembed, reembedResetWorldToUniverse } from "./reembed";
|
||||||
|
import { loadUserColorConfig } from "../util/stateManager/colorHelpers";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
return promise to fetch the OBS annotations we need to load. Omit anything
|
return promise to fetch the OBS annotations we need to load. Omit anything
|
||||||
we don't need.
|
we don't need.
|
||||||
*/
|
*/
|
||||||
function obsAnnotationFetchAndLoad(dispatch, schema) {
|
async function obsAnnotationFetchAndLoad(dispatch, schema) {
|
||||||
const obsAnnotations = schema?.schema?.annotations?.obs ?? {};
|
const obsAnnotations = schema?.schema?.annotations?.obs ?? {};
|
||||||
const index = obsAnnotations.index ?? false;
|
const index = obsAnnotations.index ?? false;
|
||||||
const columns = (obsAnnotations.columns ?? []).filter(
|
const columns = (obsAnnotations.columns ?? []).filter(
|
||||||
@@ -23,21 +24,18 @@ function obsAnnotationFetchAndLoad(dispatch, schema) {
|
|||||||
|
|
||||||
const plimit = new PromiseLimit(5);
|
const plimit = new PromiseLimit(5);
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
columns.map((col) =>
|
columns.map(col =>
|
||||||
plimit.add(() => {
|
plimit.add(() =>
|
||||||
const path = `annotations/obs?annotation-name=${encodeURIComponent(
|
fetchBinary(`annotations/obs?annotation-name=${encodeURIComponent(col.name)}`)
|
||||||
col.name
|
.then(buffer => Universe.matrixFBSToDataframe(buffer))
|
||||||
)}`;
|
.then(df =>
|
||||||
const url = `${globals.API.prefix}${globals.API.version}${path}`;
|
dispatch({
|
||||||
return doBinaryRequest(url).then((buffer) => {
|
type: "universe: column load success",
|
||||||
const df = Universe.matrixFBSToDataframe(buffer);
|
dim: "obsAnnotations",
|
||||||
dispatch({
|
dataframe: df
|
||||||
type: "universe: column load success",
|
})
|
||||||
dim: "obsAnnotations",
|
)
|
||||||
dataframe: df,
|
)
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -45,31 +43,22 @@ function obsAnnotationFetchAndLoad(dispatch, schema) {
|
|||||||
/*
|
/*
|
||||||
return promise fetching VAR annotations we need to load. Only index is currently used.
|
return promise fetching VAR annotations we need to load. Only index is currently used.
|
||||||
*/
|
*/
|
||||||
function varAnnotationFetchAndLoad(dispatch, schema) {
|
async function varAnnotationFetchAndLoad(dispatch, schema) {
|
||||||
const varAnnotations = schema?.schema?.annotations?.var ?? {};
|
const varAnnotations = schema?.schema?.annotations?.var ?? {};
|
||||||
const index = varAnnotations.index ?? false;
|
const index = varAnnotations.index ?? false;
|
||||||
const names = index ? [index] : [];
|
const names = index ? [index] : [];
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
names
|
names.map(name =>
|
||||||
.map((name) => {
|
fetchBinary(`annotations/var?annotation-name=${encodeURIComponent(name)}`)
|
||||||
const path = `annotations/var?annotation-name=${encodeURIComponent(
|
.then(buffer => Universe.matrixFBSToDataframe(buffer))
|
||||||
name
|
.then(df =>
|
||||||
)}`;
|
|
||||||
const url = `${globals.API.prefix}${globals.API.version}${path}`;
|
|
||||||
return doBinaryRequest(url);
|
|
||||||
})
|
|
||||||
.map((rqst) =>
|
|
||||||
rqst.then((buffer) => Universe.matrixFBSToDataframe(buffer))
|
|
||||||
)
|
|
||||||
.map((resp) =>
|
|
||||||
resp.then((df) =>
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "universe: column load success",
|
type: "universe: column load success",
|
||||||
dim: "varAnnotations",
|
dim: "varAnnotations",
|
||||||
dataframe: df,
|
dataframe: df
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,26 +68,35 @@ return promise fetching layout we need
|
|||||||
function layoutFetchAndLoad(dispatch, schema) {
|
function layoutFetchAndLoad(dispatch, schema) {
|
||||||
const embeddings = schema?.schema?.layout?.obs ?? [];
|
const embeddings = schema?.schema?.layout?.obs ?? [];
|
||||||
const embNames = embeddings.map((e) => e.name);
|
const embNames = embeddings.map((e) => e.name);
|
||||||
const baseURL = `${globals.API.prefix}${globals.API.version}layout/obs`;
|
|
||||||
|
|
||||||
const plimit = new PromiseLimit(5);
|
const plimit = new PromiseLimit(5);
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
embNames.map((e) =>
|
embNames.map(e =>
|
||||||
plimit.add(() => {
|
plimit.add(() =>
|
||||||
const url = `${baseURL}?layout-name=${encodeURIComponent(e)}`;
|
fetchBinary(`layout/obs?layout-name=${encodeURIComponent(e)}`)
|
||||||
return doBinaryRequest(url).then((buffer) =>
|
.then(buffer => Universe.matrixFBSToDataframe(buffer))
|
||||||
Universe.matrixFBSToDataframe(buffer)
|
)
|
||||||
);
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
).then((dfs) => {
|
).then(dfs =>
|
||||||
const df = Dataframe.Dataframe.empty().withColsFromAll(dfs);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "universe: column load success",
|
type: "universe: column load success",
|
||||||
dim: "obsLayout",
|
dim: "obsLayout",
|
||||||
dataframe: df,
|
dataframe: Dataframe.Dataframe.empty().withColsFromAll(dfs)
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
return promise fetching user-configured colors
|
||||||
|
*/
|
||||||
|
async function userColorsFetchAndLoad(dispatch) {
|
||||||
|
return fetchJson("colors")
|
||||||
|
.then(response =>
|
||||||
|
dispatch({
|
||||||
|
type: "universe: user color load success",
|
||||||
|
userColors: loadUserColorConfig(response)
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -116,13 +114,10 @@ const doInitialDataLoad = () =>
|
|||||||
/*
|
/*
|
||||||
Step 1 - config & schema, all JSON
|
Step 1 - config & schema, all JSON
|
||||||
*/
|
*/
|
||||||
const requestJson = ["config", "schema"]
|
const requestJson = ["config", "schema"].map(fetchJson);
|
||||||
.map((r) => `${globals.API.prefix}${globals.API.version}${r}`)
|
const [responseConfig, schema] = await Promise.all(requestJson);
|
||||||
.map((url) => doJsonRequest(url));
|
|
||||||
const stepOneResults = await Promise.all(requestJson);
|
|
||||||
/* set config defaults */
|
/* set config defaults */
|
||||||
const config = { ...globals.configDefaults, ...stepOneResults[0].config };
|
const config = { ...globals.configDefaults, ...responseConfig.config };
|
||||||
const schema = stepOneResults[1];
|
|
||||||
const universe = Universe.createUniverseFromResponse(config, schema);
|
const universe = Universe.createUniverseFromResponse(config, schema);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "universe exists, but loading is still in progress",
|
type: "universe exists, but loading is still in progress",
|
||||||
@@ -137,6 +132,7 @@ const doInitialDataLoad = () =>
|
|||||||
Step 2 - load the minimum stuff required to display.
|
Step 2 - load the minimum stuff required to display.
|
||||||
*/
|
*/
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
userColorsFetchAndLoad(dispatch),
|
||||||
layoutFetchAndLoad(dispatch, schema),
|
layoutFetchAndLoad(dispatch, schema),
|
||||||
varAnnotationFetchAndLoad(dispatch, schema),
|
varAnnotationFetchAndLoad(dispatch, schema),
|
||||||
]);
|
]);
|
||||||
@@ -169,13 +165,6 @@ const setWorldToSelection = () => (dispatch, getState) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Throws
|
|
||||||
const dispatchExpressionErrors = (dispatch, res) => {
|
|
||||||
const msg = `Unexpected HTTP response while fetching expression data ${res.status}, ${res.statusText}`;
|
|
||||||
dispatchNetworkErrorMessageToUser(msg);
|
|
||||||
throw new Error(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* double URI encode - needed for query-param filters */
|
/* double URI encode - needed for query-param filters */
|
||||||
function dubEncURIComponent(s) {
|
function dubEncURIComponent(s) {
|
||||||
return encodeURIComponent(encodeURIComponent(s));
|
return encodeURIComponent(encodeURIComponent(s));
|
||||||
@@ -196,16 +185,11 @@ async function _doRequestExpressionData(dispatch, getState, genes) {
|
|||||||
/* helper for this function only */
|
/* helper for this function only */
|
||||||
const fetchData = async (geneNames) => {
|
const fetchData = async (geneNames) => {
|
||||||
const query = geneNames
|
const query = geneNames
|
||||||
.map(
|
.map(g => `var:${dubEncURIComponent(varIndexName)}=${dubEncURIComponent(g)}`)
|
||||||
(g) =>
|
|
||||||
`var:${dubEncURIComponent(varIndexName)}=${dubEncURIComponent(g)}`
|
|
||||||
)
|
|
||||||
.join("&");
|
.join("&");
|
||||||
const url = `${globals.API.prefix}${globals.API.version}data/var?${query}`;
|
// TODO: why convert to an Object and not a Dataframe?
|
||||||
return doBinaryRequest(url).then((buffer) =>
|
return fetchBinary(`data/var?${query}`)
|
||||||
// TODO: why convert to an Object and not a Dataframe?
|
.then(buffer => Universe.convertDataFBStoObject(universe, buffer));
|
||||||
Universe.convertDataFBStoObject(universe, buffer)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* preload data already in cache */
|
/* preload data already in cache */
|
||||||
@@ -367,7 +351,7 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async (
|
|||||||
*/
|
*/
|
||||||
const plimit = new PromiseLimit(5);
|
const plimit = new PromiseLimit(5);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
topNGenes.map((gene) =>
|
topNGenes.map(gene =>
|
||||||
plimit.add(() => _doRequestExpressionData(dispatch, getState, [gene]))
|
plimit.add(() => _doRequestExpressionData(dispatch, getState, [gene]))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -447,6 +431,14 @@ const saveObsAnnotations = () => async (dispatch, getState) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function fetchJson(pathAndQuery) {
|
||||||
|
return doJsonRequest(`${globals.API.prefix}${globals.API.version}${pathAndQuery}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchBinary(pathAndQuery) {
|
||||||
|
return doBinaryRequest(`${globals.API.prefix}${globals.API.version}${pathAndQuery}`);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
doInitialDataLoad,
|
doInitialDataLoad,
|
||||||
requestDifferentialExpression,
|
requestDifferentialExpression,
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ class Category extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
||||||
<span style={{ fontWeight: 700 }}>
|
<span style={{ fontWeight: 700 }}>
|
||||||
{truncatedString ? truncatedString : metadataField}
|
{truncatedString || metadataField}
|
||||||
</span>
|
</span>
|
||||||
: {schema.annotations.obsByName[metadataField].categories[0]}
|
: {schema.annotations.obsByName[metadataField].categories[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ const ColorsReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "universe: user color load success": {
|
||||||
|
const { userColors } = action;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
userColors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
case "reset World to eq Universe": {
|
case "reset World to eq Universe": {
|
||||||
/* need to rebuild colors as world may have changed, but don't switch modes */
|
/* need to rebuild colors as world may have changed, but don't switch modes */
|
||||||
const { world } = nextSharedState;
|
const { world } = nextSharedState;
|
||||||
@@ -90,7 +98,7 @@ const ColorsReducer = (
|
|||||||
|
|
||||||
case "color by categorical metadata":
|
case "color by categorical metadata":
|
||||||
case "color by continuous metadata": {
|
case "color by continuous metadata": {
|
||||||
const { world } = prevSharedState;
|
const { world, colors } = prevSharedState;
|
||||||
|
|
||||||
/* toggle between this mode and reset */
|
/* toggle between this mode and reset */
|
||||||
const resetCurrent =
|
const resetCurrent =
|
||||||
@@ -99,11 +107,7 @@ const ColorsReducer = (
|
|||||||
const colorMode = !resetCurrent ? action.type : null;
|
const colorMode = !resetCurrent ? action.type : null;
|
||||||
const colorAccessor = !resetCurrent ? action.colorAccessor : null;
|
const colorAccessor = !resetCurrent ? action.colorAccessor : null;
|
||||||
|
|
||||||
const { rgb, scale } = ColorHelpers.createColors(
|
const { rgb, scale } = ColorHelpers.createColors(world, colorMode, colorAccessor, colors.userColors);
|
||||||
world,
|
|
||||||
colorMode,
|
|
||||||
colorAccessor
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
colorMode,
|
colorMode,
|
||||||
@@ -141,7 +145,7 @@ const ColorsReducer = (
|
|||||||
case "annotation: delete label": {
|
case "annotation: delete label": {
|
||||||
const { world } = nextSharedState;
|
const { world } = nextSharedState;
|
||||||
const { colorMode, colorAccessor } = state;
|
const { colorMode, colorAccessor } = state;
|
||||||
const { metadataField } = action;
|
const { metadataField, colors } = action;
|
||||||
if (
|
if (
|
||||||
colorMode !== "color by categorical metadata" ||
|
colorMode !== "color by categorical metadata" ||
|
||||||
colorAccessor !== metadataField
|
colorAccessor !== metadataField
|
||||||
@@ -149,11 +153,7 @@ const ColorsReducer = (
|
|||||||
return state;
|
return state;
|
||||||
|
|
||||||
/* else, we need to rebuild colors as labels have changed! */
|
/* else, we need to rebuild colors as labels have changed! */
|
||||||
const { rgb, scale } = ColorHelpers.createColors(
|
const { rgb, scale } = ColorHelpers.createColors(world, colorMode, colorAccessor);
|
||||||
world,
|
|
||||||
colorMode,
|
|
||||||
colorAccessor
|
|
||||||
);
|
|
||||||
return { ...state, rgb, scale };
|
return { ...state, rgb, scale };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const skipOnActions = new Set([
|
|||||||
"interface reset started",
|
"interface reset started",
|
||||||
"initial data load start",
|
"initial data load start",
|
||||||
"universe: column load success",
|
"universe: column load success",
|
||||||
|
"universe: user color load success",
|
||||||
"universe exists, but loading is still in progress",
|
"universe exists, but loading is still in progress",
|
||||||
"configuration load complete",
|
"configuration load complete",
|
||||||
"increment graph render counter",
|
"increment graph render counter",
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ import { range } from "../range";
|
|||||||
/*
|
/*
|
||||||
create new colors state object. Paramters:
|
create new colors state object. Paramters:
|
||||||
- world - current world object
|
- world - current world object
|
||||||
- mode - color-by mode. One of: null, "color by expression",
|
- colorMode - color-by mode. One of {null, "color by expression", "color by continuous metadata",
|
||||||
"color by continuous metadata", "color by categorical metadata"
|
"color by categorical metadata"}
|
||||||
-
|
- colorAccessor - the obs annotations used for color-by
|
||||||
*/
|
*/
|
||||||
export function createColors(world, colorMode = null, colorAccessor = null) {
|
export function createColors(world, colorMode = null, colorAccessor = null, userColors = null) {
|
||||||
switch (colorMode) {
|
switch (colorMode) {
|
||||||
case "color by categorical metadata": {
|
case "color by categorical metadata": {
|
||||||
|
if (userColors && colorAccessor in userColors) {
|
||||||
|
return createUserColors(world, colorAccessor, userColors);
|
||||||
|
}
|
||||||
return createColorsByCategoricalMetadata(world, colorAccessor);
|
return createColorsByCategoricalMetadata(world, colorAccessor);
|
||||||
}
|
}
|
||||||
case "color by continuous metadata": {
|
case "color by continuous metadata": {
|
||||||
@@ -36,8 +39,29 @@ export function createColors(world, colorMode = null, colorAccessor = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createColorsByCategoricalMetadata(world, accessor) {
|
export function loadUserColorConfig(userColors) {
|
||||||
const { categories } = world.schema.annotations.obsByName[accessor];
|
const convertedUserColors = {};
|
||||||
|
Object.keys(userColors).forEach(category => {
|
||||||
|
const [colors, scaleMap] = Object.keys(userColors[category]).reduce((acc, label, i) => {
|
||||||
|
const color = parseRGB(userColors[category][label]);
|
||||||
|
acc[0][label] = color;
|
||||||
|
acc[1][i] = d3.rgb(255 * color[0], 255 * color[1], 255 * color[2]);
|
||||||
|
return acc;
|
||||||
|
}, [{}, {}]);
|
||||||
|
const scale = i => scaleMap[i];
|
||||||
|
convertedUserColors[category] = { colors, scale };
|
||||||
|
});
|
||||||
|
return convertedUserColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUserColors(world, colorAccessor, userColors) {
|
||||||
|
const { colors, scale } = userColors[colorAccessor];
|
||||||
|
const rgb = createRgbArray(world, colors, colorAccessor);
|
||||||
|
return { rgb, scale };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createColorsByCategoricalMetadata(world, colorAccessor) {
|
||||||
|
const { categories } = world.schema.annotations.obsByName[colorAccessor];
|
||||||
|
|
||||||
const scale = d3
|
const scale = d3
|
||||||
.scaleSequential(interpolateRainbow)
|
.scaleSequential(interpolateRainbow)
|
||||||
@@ -49,14 +73,19 @@ function createColorsByCategoricalMetadata(world, accessor) {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
const rgb = createRgbArray(world, colors, colorAccessor);
|
||||||
|
return { rgb, scale };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRgbArray(world, colors, colorAccessor) {
|
||||||
const rgb = new Array(world.nObs);
|
const rgb = new Array(world.nObs);
|
||||||
const df = world.obsAnnotations;
|
const df = world.obsAnnotations;
|
||||||
const data = df.col(accessor).asArray();
|
const data = df.col(colorAccessor).asArray();
|
||||||
for (let i = 0, len = df.length; i < len; i += 1) {
|
for (let i = 0, len = df.length; i < len; i += 1) {
|
||||||
const cat = data[i];
|
const label = data[i];
|
||||||
rgb[i] = colors[cat];
|
rgb[i] = colors[label];
|
||||||
}
|
}
|
||||||
return { rgb, scale };
|
return rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createColorsByContinuousMetadata(world, accessor) {
|
function createColorsByContinuousMetadata(world, accessor) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
theme: jekyll-theme-minimal
|
theme: jekyll-theme-minimal
|
||||||
show_downloads: false
|
show_downloads: false
|
||||||
|
url: "https://chanzuckerberg.github.io"
|
||||||
baseurl: "/cellxgene"
|
baseurl: "/cellxgene"
|
||||||
|
|
||||||
logo: cellxgene-logo.png
|
logo: cellxgene-logo.png
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Index | cellxgene</title>
|
<title>Index | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Index" />
|
<meta property="og:title" content="Index" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebSite","headline":"Index","url":"http://localhost:4000/cellxgene/","name":"cellxgene","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebSite","url":"https://chanzuckerberg.github.io/cellxgene/","headline":"Index","name":"cellxgene","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>annotations | cellxgene</title>
|
<title>annotations | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="annotations" />
|
<meta property="og:title" content="annotations" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Creating annotations" />
|
<meta name="description" content="Creating annotations" />
|
||||||
<meta property="og:description" content="Creating annotations" />
|
<meta property="og:description" content="Creating annotations" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/annotations.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/annotations.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/annotations.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/annotations.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Creating annotations","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"annotations","url":"http://localhost:4000/cellxgene/posts/annotations.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Creating annotations","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/annotations.html","headline":"annotations","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Contact | cellxgene</title>
|
<title>Contact | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Contact" />
|
<meta property="og:title" content="Contact" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Contact" />
|
<meta name="description" content="Contact" />
|
||||||
<meta property="og:description" content="Contact" />
|
<meta property="og:description" content="Contact" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/contact.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/contact.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/contact.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/contact.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Contact","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Contact","url":"http://localhost:4000/cellxgene/posts/contact.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Contact","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/contact.html","headline":"Contact","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Code of conduct | cellxgene</title>
|
<title>Code of conduct | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Code of conduct" />
|
<meta property="og:title" content="Code of conduct" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/contribute.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/contribute.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/contribute.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/contribute.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Code of conduct","url":"http://localhost:4000/cellxgene/posts/contribute.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/contribute.html","headline":"Code of conduct","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>demo-data | cellxgene</title>
|
<title>demo-data | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="demo-data" />
|
<meta property="og:title" content="demo-data" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Demo datasets" />
|
<meta name="description" content="Demo datasets" />
|
||||||
<meta property="og:description" content="Demo datasets" />
|
<meta property="og:description" content="Demo datasets" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/demo-data.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/demo-data.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/demo-data.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/demo-data.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Demo datasets","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"demo-data","url":"http://localhost:4000/cellxgene/posts/demo-data.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Demo datasets","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/demo-data.html","headline":"demo-data","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Gallery | cellxgene</title>
|
<title>Gallery | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Gallery" />
|
<meta property="og:title" content="Gallery" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/gallery.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/gallery.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/gallery.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/gallery.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Gallery","url":"http://localhost:4000/cellxgene/posts/gallery.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/gallery.html","headline":"Gallery","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Hosting cellxgene on the web | cellxgene</title>
|
<title>Hosting cellxgene on the web | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Hosting cellxgene on the web" />
|
<meta property="og:title" content="Hosting cellxgene on the web" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/hosted.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/hosted.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/hosted.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/hosted.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Hosting cellxgene on the web","url":"http://localhost:4000/cellxgene/posts/hosted.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/hosted.html","headline":"Hosting cellxgene on the web","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Install | cellxgene</title>
|
<title>Install | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Install" />
|
<meta property="og:title" content="Install" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/install.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/install.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/install.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/install.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Install","url":"http://localhost:4000/cellxgene/posts/install.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/install.html","headline":"Install","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>demo-data | cellxgene</title>
|
<title>demo-data | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="demo-data" />
|
<meta property="og:title" content="demo-data" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Demo datasets" />
|
<meta name="description" content="Demo datasets" />
|
||||||
<meta property="og:description" content="Demo datasets" />
|
<meta property="og:description" content="Demo datasets" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/launch.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/launch.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/launch.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/launch.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Demo datasets","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"demo-data","url":"http://localhost:4000/cellxgene/posts/launch.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Demo datasets","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/launch.html","headline":"demo-data","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Methods | cellxgene</title>
|
<title>Methods | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Methods" />
|
<meta property="og:title" content="Methods" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta name="description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
<meta property="og:description" content="An interactive explorer for single-cell transcriptomics data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/methods.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/methods.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/methods.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/methods.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"An interactive explorer for single-cell transcriptomics data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Methods","url":"http://localhost:4000/cellxgene/posts/methods.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"An interactive explorer for single-cell transcriptomics data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/methods.html","headline":"Methods","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>prepare | cellxgene</title>
|
<title>prepare | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="prepare" />
|
<meta property="og:title" content="prepare" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Preparing your data" />
|
<meta name="description" content="Preparing your data" />
|
||||||
<meta property="og:description" content="Preparing your data" />
|
<meta property="og:description" content="Preparing your data" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/prepare.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/prepare.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/prepare.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/prepare.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Preparing your data","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"prepare","url":"http://localhost:4000/cellxgene/posts/prepare.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Preparing your data","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/prepare.html","headline":"prepare","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
@@ -102,8 +102,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>Expression values (raw or normalized) in <code class="language-plaintext highlighter-rouge">anndata.X</code></li>
|
<li>Expression values (raw or normalized) in <code class="language-plaintext highlighter-rouge">anndata.X</code></li>
|
||||||
<li>At least one embedding (e.g., tSNE, UMAP) in <code class="language-plaintext highlighter-rouge">anndata.obsm</code>, specified with the prefix <code class="language-plaintext highlighter-rouge">X_</code> (e.g., by default scanpy stores UMAP coordinates in <code class="language-plaintext highlighter-rouge">anndata.obsm['X_umap']</code>)</li>
|
<li>At least one embedding (e.g., tSNE, UMAP) in <code class="language-plaintext highlighter-rouge">anndata.obsm</code>, specified with the prefix <code class="language-plaintext highlighter-rouge">X_</code> (e.g., by default scanpy stores UMAP coordinates in <code class="language-plaintext highlighter-rouge">anndata.obsm['X_umap']</code>)</li>
|
||||||
<li>A unique identifier for every cell is available in an <code class="language-plaintext highlighter-rouge">anndata.obs</code> field (you can specify this with the <code class="language-plaintext highlighter-rouge">--obs-names</code> option)</li>
|
<li>A unique identifier is required for each cell, which by default will be pulled from the <code class="language-plaintext highlighter-rouge">obs</code> DataFrame index. If the index is not unique or does not contain the cell ID, an alternative column can be specified with <code class="language-plaintext highlighter-rouge">--obs-names</code></li>
|
||||||
<li>A unique identifier for every gene is available in an <code class="language-plaintext highlighter-rouge">anndata.var</code> field (you can specify which field to use with the <code class="language-plaintext highlighter-rouge">--var-names</code> option)</li>
|
<li>A unique identifier is required for each gene, which by default will be pulled from the <code class="language-plaintext highlighter-rouge">var</code> DataFrame index. If the index is not unique or does not contain the cell ID, an alternative column can be specified with <code class="language-plaintext highlighter-rouge">--var-names</code></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 id="what-about-r-objects-from-seurat--bioconductor">What about R objects from seurat / bioconductor!?</h4>
|
<h4 id="what-about-r-objects-from-seurat--bioconductor">What about R objects from seurat / bioconductor!?</h4>
|
||||||
@@ -112,6 +112,32 @@
|
|||||||
<h4 id="can-i-use-data-hosted-on-the-web-somewhere">Can I use data hosted on the web somewhere?</h4>
|
<h4 id="can-i-use-data-hosted-on-the-web-somewhere">Can I use data hosted on the web somewhere?</h4>
|
||||||
<p>Yes! You can launch from a URL instead of a filepath. The same data format requirements apply. Please see <a href="launch">here</a> for more details.</p>
|
<p>Yes! You can launch from a URL instead of a filepath. The same data format requirements apply. Please see <a href="launch">here</a> for more details.</p>
|
||||||
|
|
||||||
|
<h1 id="data-format-options">Data format options</h1>
|
||||||
|
|
||||||
|
<h4 id="category-colors">Category colors</h4>
|
||||||
|
<p><code class="language-plaintext highlighter-rouge">cellxgene</code> will display <a href="https://github.com/chanzuckerberg/cellxgene/issues/1152#issue-564361541">scanpy-style color
|
||||||
|
information</a>
|
||||||
|
for category-label pairs. An example of this format is shown below:</p>
|
||||||
|
|
||||||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>> category = "louvain"
|
||||||
|
>>> # colors stored in adata.uns must be matplotlib-compatible color information
|
||||||
|
>>> adata.uns[f"{category}_colors"]
|
||||||
|
array(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#bcbd22'], dtype='<U7')
|
||||||
|
>>> # there must be a matching category in adata.obs
|
||||||
|
>>> category in adata.obs
|
||||||
|
True
|
||||||
|
</code></pre></div></div>
|
||||||
|
|
||||||
|
<p>To test that you’ve done this properly, check that for your given <code class="language-plaintext highlighter-rouge">category</code> the number of colors match the number of category values and that the second command below results in a mapping from categories to colors.</p>
|
||||||
|
|
||||||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>> len(adata.obs[category].cat.categories) == len(adata.uns[f"{category}_colors"])
|
||||||
|
True
|
||||||
|
>>> dict(zip(adata.obs[category].cat.categories, adata.uns[f"{category}_colors"]))
|
||||||
|
{'CD4 T cells': '#1f77b4', 'CD14+ Monocytes': '#ff7f0e', 'B cells': '#2ca02c', 'CD8 T cells': '#d62728', 'NK cells': '#9467bd', 'FCGR3A+ Monocytes': '#8c564b', 'Dendritic cells': '#e377c2', 'Megakaryocytes': '#bcbd22'}
|
||||||
|
</code></pre></div></div>
|
||||||
|
|
||||||
|
<p>You can disable this feature using the <code class="language-plaintext highlighter-rouge">--disable-custom-colors</code> flag for <code class="language-plaintext highlighter-rouge">cellxgene launch</code>. cellxgene will then chose colors from its standard color palettes.</p>
|
||||||
|
|
||||||
<h1 id="using-cellxgene-prepare">Using <code class="language-plaintext highlighter-rouge">cellxgene prepare</code></h1>
|
<h1 id="using-cellxgene-prepare">Using <code class="language-plaintext highlighter-rouge">cellxgene prepare</code></h1>
|
||||||
|
|
||||||
<p>If your data is in a different format, and/or you still need to perform dimensionality reduction and/or clustering, <code class="language-plaintext highlighter-rouge">cellxgene</code> can do that for you with the <code class="language-plaintext highlighter-rouge">prepare</code> command.</p>
|
<p>If your data is in a different format, and/or you still need to perform dimensionality reduction and/or clustering, <code class="language-plaintext highlighter-rouge">cellxgene</code> can do that for you with the <code class="language-plaintext highlighter-rouge">prepare</code> command.</p>
|
||||||
@@ -136,7 +162,7 @@
|
|||||||
<p><code class="language-plaintext highlighter-rouge">cellxgene prepare</code> is not meant as a way to formally process or analyze your data. It’s simply a utility for quickly wrangling your data into cellxgene-compatible format and computing a “vanilla” embedding so you can try out <code class="language-plaintext highlighter-rouge">cellxgene</code> and get a general sense of a dataset.</p>
|
<p><code class="language-plaintext highlighter-rouge">cellxgene prepare</code> is not meant as a way to formally process or analyze your data. It’s simply a utility for quickly wrangling your data into cellxgene-compatible format and computing a “vanilla” embedding so you can try out <code class="language-plaintext highlighter-rouge">cellxgene</code> and get a general sense of a dataset.</p>
|
||||||
|
|
||||||
<h2 id="quickstart-for-cellxgene-prepare">Quickstart for <code class="language-plaintext highlighter-rouge">cellxgene prepare</code></h2>
|
<h2 id="quickstart-for-cellxgene-prepare">Quickstart for <code class="language-plaintext highlighter-rouge">cellxgene prepare</code></h2>
|
||||||
<p>To add <code class="language-plaintext highlighter-rouge">cellxgene prepare</code> to your <a href="install">cellxgene installation</a>, run<br />
|
<p>To add <code class="language-plaintext highlighter-rouge">cellxgene prepare</code> to your <a href="install">cellxgene installation</a>, run
|
||||||
<code class="language-plaintext highlighter-rouge">pip install cellxgene[prepare]</code></p>
|
<code class="language-plaintext highlighter-rouge">pip install cellxgene[prepare]</code></p>
|
||||||
|
|
||||||
<p>Then run <code class="language-plaintext highlighter-rouge">prepare</code> on your data with:</p>
|
<p>Then run <code class="language-plaintext highlighter-rouge">prepare</code> on your data with:</p>
|
||||||
@@ -162,36 +188,36 @@
|
|||||||
|
|
||||||
<p>Let’s look at what <code class="language-plaintext highlighter-rouge">prepare</code> is doing to our data, and how each step relates to the command above. You can see a walkthrough of what’s going on under the hood for this example in <a href="https://github.com/chanzuckerberg/cellxgene-vignettes/blob/master/dataset-processing/pbmc3k-prepare-example.ipynb">this notebook</a>.</p>
|
<p>Let’s look at what <code class="language-plaintext highlighter-rouge">prepare</code> is doing to our data, and how each step relates to the command above. You can see a walkthrough of what’s going on under the hood for this example in <a href="https://github.com/chanzuckerberg/cellxgene-vignettes/blob/master/dataset-processing/pbmc3k-prepare-example.ipynb">this notebook</a>.</p>
|
||||||
|
|
||||||
<p><strong>(A) - Compute quality control metrics and store this in our <code class="language-plaintext highlighter-rouge">AnnData</code> object for later inspection</strong> <br />
|
<p><strong>(A) - Compute quality control metrics and store this in our <code class="language-plaintext highlighter-rouge">AnnData</code> object for later inspection</strong>
|
||||||
<strong>(B) - Normalize the expression matrix using a basic preprocessing recipe</strong> <br />
|
<strong>(B) - Normalize the expression matrix using a basic preprocessing recipe</strong>
|
||||||
<strong>(auto) - Do some preprocessing to run PCA and compute the neighbor graph</strong><br />
|
<strong>(auto) - Do some preprocessing to run PCA and compute the neighbor graph</strong>
|
||||||
<strong>(auto) - Infer clusters with the Louvain algorithm and store these labels to visualize later</strong><br />
|
<strong>(auto) - Infer clusters with the Louvain algorithm and store these labels to visualize later</strong>
|
||||||
<strong>(C) - Compute and store UMAP and tSNE embeddings</strong><br />
|
<strong>(C) - Compute and store UMAP and tSNE embeddings</strong>
|
||||||
<strong>(D) - Write results to file</strong></p>
|
<strong>(D) - Write results to file</strong></p>
|
||||||
|
|
||||||
<h2 id="options-for-cellxgene-prepare">Options for cellxgene <code class="language-plaintext highlighter-rouge">prepare</code></h2>
|
<h2 id="options-for-cellxgene-prepare">Options for cellxgene <code class="language-plaintext highlighter-rouge">prepare</code></h2>
|
||||||
|
|
||||||
<p><strong>For the most up-to-date and comprehensive list of options, run <code class="language-plaintext highlighter-rouge">cellxgene prepare --help</code></strong></p>
|
<p><strong>For the most up-to-date and comprehensive list of options, run <code class="language-plaintext highlighter-rouge">cellxgene prepare --help</code></strong></p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--embedding</code> controls which dimensionality reduction algorithm is applies to your data.<br />
|
<p><code class="language-plaintext highlighter-rouge">--embedding</code> controls which dimensionality reduction algorithm is applies to your data.
|
||||||
Options are <code class="language-plaintext highlighter-rouge">umap</code> and/or <code class="language-plaintext highlighter-rouge">tsne</code>. Defaults to both.</p>
|
Options are <code class="language-plaintext highlighter-rouge">umap</code> and/or <code class="language-plaintext highlighter-rouge">tsne</code>. Defaults to both.</p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--recipe</code> controls which normalization steps to apply to your data, based on one of the preprocessing <code class="language-plaintext highlighter-rouge">recipes</code> included with <code class="language-plaintext highlighter-rouge">scanpy</code>.
|
<p><code class="language-plaintext highlighter-rouge">--recipe</code> controls which normalization steps to apply to your data, based on one of the preprocessing <code class="language-plaintext highlighter-rouge">recipes</code> included with <code class="language-plaintext highlighter-rouge">scanpy</code>.
|
||||||
These recipes include steps like cell filtering and gene selection; see the <code class="language-plaintext highlighter-rouge">scanpy</code> <a href="https://scanpy.readthedocs.io/en/latest/api/index.html#recipes">documentation</a> for more details. <br />
|
These recipes include steps like cell filtering and gene selection; see the <code class="language-plaintext highlighter-rouge">scanpy</code> <a href="https://scanpy.readthedocs.io/en/latest/api/index.html#recipes">documentation</a> for more details.
|
||||||
Options are <code class="language-plaintext highlighter-rouge">none</code>, <code class="language-plaintext highlighter-rouge">seurat</code>, or <code class="language-plaintext highlighter-rouge">zheng17</code>. Defaults to <code class="language-plaintext highlighter-rouge">none</code>.</p>
|
Options are <code class="language-plaintext highlighter-rouge">none</code>, <code class="language-plaintext highlighter-rouge">seurat</code>, or <code class="language-plaintext highlighter-rouge">zheng17</code>. Defaults to <code class="language-plaintext highlighter-rouge">none</code>.</p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--sparse</code> is a flag determines whether to enforce a sparse matrix. For large datasets, <code class="language-plaintext highlighter-rouge">prepare</code> can take a long time to run (a few minutes for datasets with 10-100k cells, up to an hour or more for datasets with >100k cells). If you want <code class="language-plaintext highlighter-rouge">prepare</code> to run faster we recommend using the <code class="language-plaintext highlighter-rouge">sparse</code> option.<br />
|
<p><code class="language-plaintext highlighter-rouge">--sparse</code> is a flag determines whether to enforce a sparse matrix. For large datasets, <code class="language-plaintext highlighter-rouge">prepare</code> can take a long time to run (a few minutes for datasets with 10-100k cells, up to an hour or more for datasets with >100k cells). If you want <code class="language-plaintext highlighter-rouge">prepare</code> to run faster we recommend using the <code class="language-plaintext highlighter-rouge">sparse</code> option.
|
||||||
If this flag is not included, default is <code class="language-plaintext highlighter-rouge">False</code></p>
|
If this flag is not included, default is <code class="language-plaintext highlighter-rouge">False</code></p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--skip-qc</code> by default, <code class="language-plaintext highlighter-rouge">cellxgene prepare</code> will compute quality control metrics (saved to <code class="language-plaintext highlighter-rouge">anndata.obs</code> and <code class="language-plaintext highlighter-rouge">anndata.var</code>) as described in the <code class="language-plaintext highlighter-rouge">scanpy</code> <a href="https://scanpy.readthedocs.io/en/stable/api/scanpy.pp.calculate_qc_metrics.html">documentation</a>. Pass this flag if you would like to skip this step.</p>
|
<p><code class="language-plaintext highlighter-rouge">--skip-qc</code> by default, <code class="language-plaintext highlighter-rouge">cellxgene prepare</code> will compute quality control metrics (saved to <code class="language-plaintext highlighter-rouge">anndata.obs</code> and <code class="language-plaintext highlighter-rouge">anndata.var</code>) as described in the <code class="language-plaintext highlighter-rouge">scanpy</code> <a href="https://scanpy.readthedocs.io/en/stable/api/scanpy.pp.calculate_qc_metrics.html">documentation</a>. Pass this flag if you would like to skip this step.</p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--make-obs-names-unique</code> / <code class="language-plaintext highlighter-rouge">--make-var-names-unique</code> determine whether to rename <code class="language-plaintext highlighter-rouge">obs</code> (cell) / <code class="language-plaintext highlighter-rouge">var</code> (gene) names, respectively, to be unique.<br />
|
<p><code class="language-plaintext highlighter-rouge">--make-obs-names-unique</code> / <code class="language-plaintext highlighter-rouge">--make-var-names-unique</code> determine whether to rename <code class="language-plaintext highlighter-rouge">obs</code> (cell) / <code class="language-plaintext highlighter-rouge">var</code> (gene) names, respectively, to be unique.
|
||||||
Default is <code class="language-plaintext highlighter-rouge">True</code>.</p>
|
Default is <code class="language-plaintext highlighter-rouge">True</code>.</p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--set-obs-names</code> controls which field in <code class="language-plaintext highlighter-rouge">anndata.obs</code> (cell metadata) is used as the <em>index</em> for cells (e.g., a cell ID column).<br />
|
<p><code class="language-plaintext highlighter-rouge">--set-obs-names</code> controls which field in <code class="language-plaintext highlighter-rouge">anndata.obs</code> (cell metadata) is used as the <em>index</em> for cells (e.g., a cell ID column).
|
||||||
Default is <code class="language-plaintext highlighter-rouge">anndata.obs.names</code></p>
|
Default is <code class="language-plaintext highlighter-rouge">anndata.obs.names</code></p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--set-var-names</code> controls which field in <code class="language-plaintext highlighter-rouge">anndata.var</code> (gene metadata) is used as the <em>index</em> for genes.<br />
|
<p><code class="language-plaintext highlighter-rouge">--set-var-names</code> controls which field in <code class="language-plaintext highlighter-rouge">anndata.var</code> (gene metadata) is used as the <em>index</em> for genes.
|
||||||
Default is <code class="language-plaintext highlighter-rouge">anndata.var.names</code></p>
|
Default is <code class="language-plaintext highlighter-rouge">anndata.var.names</code></p>
|
||||||
|
|
||||||
<p><code class="language-plaintext highlighter-rouge">--output</code> and <code class="language-plaintext highlighter-rouge">--overwrite</code> control where the processed data is saved.</p>
|
<p><code class="language-plaintext highlighter-rouge">--output</code> and <code class="language-plaintext highlighter-rouge">--overwrite</code> control where the processed data is saved.</p>
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>roadmap | cellxgene</title>
|
<title>roadmap | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="roadmap" />
|
<meta property="og:title" content="roadmap" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Roadmap" />
|
<meta name="description" content="Roadmap" />
|
||||||
<meta property="og:description" content="Roadmap" />
|
<meta property="og:description" content="Roadmap" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/roadmap.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/roadmap.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/roadmap.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/roadmap.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Roadmap","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"roadmap","url":"http://localhost:4000/cellxgene/posts/roadmap.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Roadmap","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/roadmap.html","headline":"roadmap","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
@@ -5,21 +5,21 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Begin Jekyll SEO tag v2.5.0 -->
|
<!-- Begin Jekyll SEO tag v2.6.1 -->
|
||||||
<title>Troubleshooting | cellxgene</title>
|
<title>Troubleshooting | cellxgene</title>
|
||||||
<meta name="generator" content="Jekyll v3.8.5" />
|
<meta name="generator" content="Jekyll v3.8.5" />
|
||||||
<meta property="og:title" content="Troubleshooting" />
|
<meta property="og:title" content="Troubleshooting" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="description" content="Troubleshooting" />
|
<meta name="description" content="Troubleshooting" />
|
||||||
<meta property="og:description" content="Troubleshooting" />
|
<meta property="og:description" content="Troubleshooting" />
|
||||||
<link rel="canonical" href="http://localhost:4000/cellxgene/posts/troubleshooting.html" />
|
<link rel="canonical" href="https://chanzuckerberg.github.io/cellxgene/posts/troubleshooting.html" />
|
||||||
<meta property="og:url" content="http://localhost:4000/cellxgene/posts/troubleshooting.html" />
|
<meta property="og:url" content="https://chanzuckerberg.github.io/cellxgene/posts/troubleshooting.html" />
|
||||||
<meta property="og:site_name" content="cellxgene" />
|
<meta property="og:site_name" content="cellxgene" />
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"description":"Troubleshooting","publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"http://localhost:4000/cellxgene/cellxgene-logo.png"}},"@type":"WebPage","headline":"Troubleshooting","url":"http://localhost:4000/cellxgene/posts/troubleshooting.html","@context":"http://schema.org"}</script>
|
{"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://chanzuckerberg.github.io/cellxgene/cellxgene-logo.png"}},"description":"Troubleshooting","@type":"WebPage","url":"https://chanzuckerberg.github.io/cellxgene/posts/troubleshooting.html","headline":"Troubleshooting","@context":"https://schema.org"}</script>
|
||||||
<!-- End Jekyll SEO tag -->
|
<!-- End Jekyll SEO tag -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=565a3f148df7473603c355a32536cb0eea068885">
|
<link rel="stylesheet" href="/cellxgene/assets/css/style.css?v=b45f000ecb36779dde84b689d463d17a077275d6">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|||||||
+30
-2
@@ -9,8 +9,8 @@ If your data is in `h5ad` file (from the [`anndata`](https://anndata.readthedocs
|
|||||||
|
|
||||||
- Expression values (raw or normalized) in `anndata.X`
|
- Expression values (raw or normalized) in `anndata.X`
|
||||||
- At least one embedding (e.g., tSNE, UMAP) in `anndata.obsm`, specified with the prefix `X_` (e.g., by default scanpy stores UMAP coordinates in `anndata.obsm['X_umap']`)
|
- At least one embedding (e.g., tSNE, UMAP) in `anndata.obsm`, specified with the prefix `X_` (e.g., by default scanpy stores UMAP coordinates in `anndata.obsm['X_umap']`)
|
||||||
- A unique identifier for every cell is available in an `anndata.obs` field (you can specify this with the `--obs-names` option)
|
- A unique identifier is required for each cell, which by default will be pulled from the `obs` DataFrame index. If the index is not unique or does not contain the cell ID, an alternative column can be specified with `--obs-names`
|
||||||
- A unique identifier for every gene is available in an `anndata.var` field (you can specify which field to use with the `--var-names` option)
|
- A unique identifier is required for each gene, which by default will be pulled from the `var` DataFrame index. If the index is not unique or does not contain the cell ID, an alternative column can be specified with `--var-names`
|
||||||
|
|
||||||
#### What about R objects from seurat / bioconductor!?
|
#### What about R objects from seurat / bioconductor!?
|
||||||
We hear you! We'd also love to be able to ingest these files directly. This isn't currently possible, but in the meantime, you can use [sceasy](https://bioconda.github.io/recipes/r-sceasy/README.html) ([docs](https://cellgeni.readthedocs.io/en/latest/visualisations.html)) to convert to `h5ad`. Seurat also has some [handy conversion tools](https://satijalab.org/seurat/v3.0/conversion_vignette.html) that you can try out.
|
We hear you! We'd also love to be able to ingest these files directly. This isn't currently possible, but in the meantime, you can use [sceasy](https://bioconda.github.io/recipes/r-sceasy/README.html) ([docs](https://cellgeni.readthedocs.io/en/latest/visualisations.html)) to convert to `h5ad`. Seurat also has some [handy conversion tools](https://satijalab.org/seurat/v3.0/conversion_vignette.html) that you can try out.
|
||||||
@@ -18,6 +18,34 @@ We hear you! We'd also love to be able to ingest these files directly. This isn'
|
|||||||
#### Can I use data hosted on the web somewhere?
|
#### Can I use data hosted on the web somewhere?
|
||||||
Yes! You can launch from a URL instead of a filepath. The same data format requirements apply. Please see [here](launch) for more details.
|
Yes! You can launch from a URL instead of a filepath. The same data format requirements apply. Please see [here](launch) for more details.
|
||||||
|
|
||||||
|
# Data format options
|
||||||
|
|
||||||
|
#### Category colors
|
||||||
|
`cellxgene` will display [scanpy-style color
|
||||||
|
information](https://github.com/chanzuckerberg/cellxgene/issues/1152#issue-564361541)
|
||||||
|
for category-label pairs. An example of this format is shown below:
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> category = "louvain"
|
||||||
|
>>> # colors stored in adata.uns must be matplotlib-compatible color information
|
||||||
|
>>> adata.uns[f"{category}_colors"]
|
||||||
|
array(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#bcbd22'], dtype='<U7')
|
||||||
|
>>> # there must be a matching category in adata.obs
|
||||||
|
>>> category in adata.obs
|
||||||
|
True
|
||||||
|
```
|
||||||
|
|
||||||
|
To test that you've done this properly, check that for your given `category` the number of colors match the number of category values and that the second command below results in a mapping from categories to colors.
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> len(adata.obs[category].cat.categories) == len(adata.uns[f"{category}_colors"])
|
||||||
|
True
|
||||||
|
>>> dict(zip(adata.obs[category].cat.categories, adata.uns[f"{category}_colors"]))
|
||||||
|
{'CD4 T cells': '#1f77b4', 'CD14+ Monocytes': '#ff7f0e', 'B cells': '#2ca02c', 'CD8 T cells': '#d62728', 'NK cells': '#9467bd', 'FCGR3A+ Monocytes': '#8c564b', 'Dendritic cells': '#e377c2', 'Megakaryocytes': '#bcbd22'}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can disable this feature using the `--disable-custom-colors` flag for `cellxgene launch`. cellxgene will then chose colors from its standard color palettes.
|
||||||
|
|
||||||
# Using `cellxgene prepare`
|
# Using `cellxgene prepare`
|
||||||
|
|
||||||
If your data is in a different format, and/or you still need to perform dimensionality reduction and/or clustering, `cellxgene` can do that for you with the `prepare` command.
|
If your data is in a different format, and/or you still need to perform dimensionality reduction and/or clustering, `cellxgene` can do that for you with the `prepare` command.
|
||||||
|
|||||||
@@ -207,6 +207,13 @@ class DataVarAPI(Resource):
|
|||||||
return common_rest.data_var_get(request, data_adaptor)
|
return common_rest.data_var_get(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
|
class ColorsAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
|
@rest_get_data_adaptor
|
||||||
|
def get(self, data_adaptor):
|
||||||
|
return common_rest.colors_get(data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class DiffExpObsAPI(Resource):
|
class DiffExpObsAPI(Resource):
|
||||||
@cache_control(no_store=True)
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
@@ -235,6 +242,8 @@ def get_api_resources(bp_api):
|
|||||||
api.add_resource(AnnotationsObsAPI, "/annotations/obs")
|
api.add_resource(AnnotationsObsAPI, "/annotations/obs")
|
||||||
api.add_resource(AnnotationsVarAPI, "/annotations/var")
|
api.add_resource(AnnotationsVarAPI, "/annotations/var")
|
||||||
api.add_resource(DataVarAPI, "/data/var")
|
api.add_resource(DataVarAPI, "/data/var")
|
||||||
|
# Display routes
|
||||||
|
api.add_resource(ColorsAPI, "/colors")
|
||||||
# Computation routes
|
# Computation routes
|
||||||
api.add_resource(DiffExpObsAPI, "/diffexp/obs")
|
api.add_resource(DiffExpObsAPI, "/diffexp/obs")
|
||||||
api.add_resource(LayoutObsAPI, "/layout/obs")
|
api.add_resource(LayoutObsAPI, "/layout/obs")
|
||||||
|
|||||||
+10
-1
@@ -73,6 +73,13 @@ def config_args(func):
|
|||||||
show_default=True,
|
show_default=True,
|
||||||
help="Will not display categories with more distinct values than specified.",
|
help="Will not display categories with more distinct values than specified.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--disable-custom-colors",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
show_default=False,
|
||||||
|
help="Disable user-defined category-label colors drawn from source data file.",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--diffexp-lfc-cutoff",
|
"--diffexp-lfc-cutoff",
|
||||||
"-de",
|
"-de",
|
||||||
@@ -146,7 +153,7 @@ def dataset_args(func):
|
|||||||
"--about",
|
"--about",
|
||||||
default=DEFAULT_CONFIG.single_dataset__about,
|
default=DEFAULT_CONFIG.single_dataset__about,
|
||||||
metavar="<URL>",
|
metavar="<URL>",
|
||||||
help="URL providing more information about the dataset " "(hint: must be a fully specified absolute URL).",
|
help="URL providing more information about the dataset (hint: must be a fully specified absolute URL).",
|
||||||
)
|
)
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
@@ -311,6 +318,7 @@ def launch(
|
|||||||
obs_names,
|
obs_names,
|
||||||
var_names,
|
var_names,
|
||||||
max_category_items,
|
max_category_items,
|
||||||
|
disable_custom_colors,
|
||||||
diffexp_lfc_cutoff,
|
diffexp_lfc_cutoff,
|
||||||
title,
|
title,
|
||||||
scripts,
|
scripts,
|
||||||
@@ -381,6 +389,7 @@ def launch(
|
|||||||
user_annotations__ontology__enable=experimental_annotations_ontology,
|
user_annotations__ontology__enable=experimental_annotations_ontology,
|
||||||
user_annotations__ontology__obo_location=experimental_annotations_ontology_obo,
|
user_annotations__ontology__obo_location=experimental_annotations_ontology_obo,
|
||||||
presentation__max_categories=max_category_items,
|
presentation__max_categories=max_category_items,
|
||||||
|
presentation__custom_colors=not disable_custom_colors,
|
||||||
embeddings__names=embedding,
|
embeddings__names=embedding,
|
||||||
embeddings__enable_reembedding=experimental_enable_reembedding,
|
embeddings__enable_reembedding=experimental_enable_reembedding,
|
||||||
diffexp__enable=not disable_diffexp,
|
diffexp__enable=not disable_diffexp,
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ def prepare(
|
|||||||
(h5ad, loom, or a 10x directory), runs dimensionality reduction,
|
(h5ad, loom, or a 10x directory), runs dimensionality reduction,
|
||||||
computes nearest neighbors, computes an embedding, performs clustering,
|
computes nearest neighbors, computes an embedding, performs clustering,
|
||||||
and saves the results. Includes additional options for naming annotations,
|
and saves the results. Includes additional options for naming annotations,
|
||||||
ensuring sparsity, and plotting results."""
|
ensuring sparsity, and plotting results.
|
||||||
|
"""
|
||||||
|
|
||||||
# collect slow imports here to make CLI startup more responsive
|
# collect slow imports here to make CLI startup more responsive
|
||||||
click.echo("[cellxgene] Starting CLI...")
|
click.echo("[cellxgene] Starting CLI...")
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class AppConfig(object):
|
|||||||
self.user_annotations__ontology__obo_location = dc["user_annotations"]["ontology"]["obo_location"]
|
self.user_annotations__ontology__obo_location = dc["user_annotations"]["ontology"]["obo_location"]
|
||||||
|
|
||||||
self.presentation__max_categories = dc["presentation"]["max_categories"]
|
self.presentation__max_categories = dc["presentation"]["max_categories"]
|
||||||
|
self.presentation__custom_colors = dc["presentation"]["custom_colors"]
|
||||||
|
|
||||||
self.embeddings__names = dc["embeddings"]["names"]
|
self.embeddings__names = dc["embeddings"]["names"]
|
||||||
self.embeddings__enable_reembedding = dc["embeddings"]["enable_reembedding"]
|
self.embeddings__enable_reembedding = dc["embeddings"]["enable_reembedding"]
|
||||||
@@ -275,6 +276,7 @@ class AppConfig(object):
|
|||||||
|
|
||||||
def handle_presentation(self, context):
|
def handle_presentation(self, context):
|
||||||
self.__check_attr("presentation__max_categories", int)
|
self.__check_attr("presentation__max_categories", int)
|
||||||
|
self.__check_attr("presentation__custom_colors", bool)
|
||||||
|
|
||||||
def handle_single_dataset(self, context):
|
def handle_single_dataset(self, context):
|
||||||
self.__check_attr("single_dataset__datapath", (str, type(None)))
|
self.__check_attr("single_dataset__datapath", (str, type(None)))
|
||||||
@@ -517,6 +519,7 @@ class AppConfig(object):
|
|||||||
"annotations_cell_ontology_enabled": False,
|
"annotations_cell_ontology_enabled": False,
|
||||||
"annotations_cell_ontology_obopath": None,
|
"annotations_cell_ontology_obopath": None,
|
||||||
"annotations_cell_ontology_terms": None,
|
"annotations_cell_ontology_terms": None,
|
||||||
|
"custom_colors": self.presentation__custom_colors,
|
||||||
"diffexp-may-be-slow": False,
|
"diffexp-may-be-slow": False,
|
||||||
"about_legal_tos": self.server__about_legal_tos,
|
"about_legal_tos": self.server__about_legal_tos,
|
||||||
"about_legal_privacy": self.server__about_legal_privacy,
|
"about_legal_privacy": self.server__about_legal_privacy,
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from server.common.errors import ColorFormatException
|
||||||
|
|
||||||
|
HEX_COLOR_FORMAT = re.compile("^#[a-fA-F0-9]{6,6}$")
|
||||||
|
|
||||||
|
# https://www.w3.org/TR/css-color-4/#named-colors
|
||||||
|
CSS4_NAMED_COLORS = dict(
|
||||||
|
aliceblue="#f0f8ff",
|
||||||
|
antiquewhite="#faebd7",
|
||||||
|
aqua="#00ffff",
|
||||||
|
aquamarine="#7fffd4",
|
||||||
|
azure="#f0ffff",
|
||||||
|
beige="#f5f5dc",
|
||||||
|
bisque="#ffe4c4",
|
||||||
|
black="#000000",
|
||||||
|
blanchedalmond="#ffebcd",
|
||||||
|
blue="#0000ff",
|
||||||
|
blueviolet="#8a2be2",
|
||||||
|
brown="#a52a2a",
|
||||||
|
burlywood="#deb887",
|
||||||
|
cadetblue="#5f9ea0",
|
||||||
|
chartreuse="#7fff00",
|
||||||
|
chocolate="#d2691e",
|
||||||
|
coral="#ff7f50",
|
||||||
|
cornflowerblue="#6495ed",
|
||||||
|
cornsilk="#fff8dc",
|
||||||
|
crimson="#dc143c",
|
||||||
|
cyan="#00ffff",
|
||||||
|
darkblue="#00008b",
|
||||||
|
darkcyan="#008b8b",
|
||||||
|
darkgoldenrod="#b8860b",
|
||||||
|
darkgray="#a9a9a9",
|
||||||
|
darkgreen="#006400",
|
||||||
|
darkgrey="#a9a9a9",
|
||||||
|
darkkhaki="#bdb76b",
|
||||||
|
darkmagenta="#8b008b",
|
||||||
|
darkolivegreen="#556b2f",
|
||||||
|
darkorange="#ff8c00",
|
||||||
|
darkorchid="#9932cc",
|
||||||
|
darkred="#8b0000",
|
||||||
|
darksalmon="#e9967a",
|
||||||
|
darkseagreen="#8fbc8f",
|
||||||
|
darkslateblue="#483d8b",
|
||||||
|
darkslategray="#2f4f4f",
|
||||||
|
darkslategrey="#2f4f4f",
|
||||||
|
darkturquoise="#00ced1",
|
||||||
|
darkviolet="#9400d3",
|
||||||
|
deeppink="#ff1493",
|
||||||
|
deepskyblue="#00bfff",
|
||||||
|
dimgray="#696969",
|
||||||
|
dimgrey="#696969",
|
||||||
|
dodgerblue="#1e90ff",
|
||||||
|
firebrick="#b22222",
|
||||||
|
floralwhite="#fffaf0",
|
||||||
|
forestgreen="#228b22",
|
||||||
|
fuchsia="#ff00ff",
|
||||||
|
gainsboro="#dcdcdc",
|
||||||
|
ghostwhite="#f8f8ff",
|
||||||
|
gold="#ffd700",
|
||||||
|
goldenrod="#daa520",
|
||||||
|
gray="#808080",
|
||||||
|
green="#008000",
|
||||||
|
greenyellow="#adff2f",
|
||||||
|
grey="#808080",
|
||||||
|
honeydew="#f0fff0",
|
||||||
|
hotpink="#ff69b4",
|
||||||
|
indianred="#cd5c5c",
|
||||||
|
indigo="#4b0082",
|
||||||
|
ivory="#fffff0",
|
||||||
|
khaki="#f0e68c",
|
||||||
|
lavender="#e6e6fa",
|
||||||
|
lavenderblush="#fff0f5",
|
||||||
|
lawngreen="#7cfc00",
|
||||||
|
lemonchiffon="#fffacd",
|
||||||
|
lightblue="#add8e6",
|
||||||
|
lightcoral="#f08080",
|
||||||
|
lightcyan="#e0ffff",
|
||||||
|
lightgoldenrodyellow="#fafad2",
|
||||||
|
lightgray="#d3d3d3",
|
||||||
|
lightgreen="#90ee90",
|
||||||
|
lightgrey="#d3d3d3",
|
||||||
|
lightpink="#ffb6c1",
|
||||||
|
lightsalmon="#ffa07a",
|
||||||
|
lightseagreen="#20b2aa",
|
||||||
|
lightskyblue="#87cefa",
|
||||||
|
lightslategray="#778899",
|
||||||
|
lightslategrey="#778899",
|
||||||
|
lightsteelblue="#b0c4de",
|
||||||
|
lightyellow="#ffffe0",
|
||||||
|
lime="#00ff00",
|
||||||
|
limegreen="#32cd32",
|
||||||
|
linen="#faf0e6",
|
||||||
|
magenta="#ff00ff",
|
||||||
|
maroon="#800000",
|
||||||
|
mediumaquamarine="#66cdaa",
|
||||||
|
mediumblue="#0000cd",
|
||||||
|
mediumorchid="#ba55d3",
|
||||||
|
mediumpurple="#9370db",
|
||||||
|
mediumseagreen="#3cb371",
|
||||||
|
mediumslateblue="#7b68ee",
|
||||||
|
mediumspringgreen="#00fa9a",
|
||||||
|
mediumturquoise="#48d1cc",
|
||||||
|
mediumvioletred="#c71585",
|
||||||
|
midnightblue="#191970",
|
||||||
|
mintcream="#f5fffa",
|
||||||
|
mistyrose="#ffe4e1",
|
||||||
|
moccasin="#ffe4b5",
|
||||||
|
navajowhite="#ffdead",
|
||||||
|
navy="#000080",
|
||||||
|
oldlace="#fdf5e6",
|
||||||
|
olive="#808000",
|
||||||
|
olivedrab="#6b8e23",
|
||||||
|
orange="#ffa500",
|
||||||
|
orangered="#ff4500",
|
||||||
|
orchid="#da70d6",
|
||||||
|
palegoldenrod="#eee8aa",
|
||||||
|
palegreen="#98fb98",
|
||||||
|
paleturquoise="#afeeee",
|
||||||
|
palevioletred="#db7093",
|
||||||
|
papayawhip="#ffefd5",
|
||||||
|
peachpuff="#ffdab9",
|
||||||
|
peru="#cd853f",
|
||||||
|
pink="#ffc0cb",
|
||||||
|
plum="#dda0dd",
|
||||||
|
powderblue="#b0e0e6",
|
||||||
|
purple="#800080",
|
||||||
|
rebeccapurple="#663399",
|
||||||
|
red="#ff0000",
|
||||||
|
rosybrown="#bc8f8f",
|
||||||
|
royalblue="#4169e1",
|
||||||
|
saddlebrown="#8b4513",
|
||||||
|
salmon="#fa8072",
|
||||||
|
sandybrown="#f4a460",
|
||||||
|
seagreen="#2e8b57",
|
||||||
|
seashell="#fff5ee",
|
||||||
|
sienna="#a0522d",
|
||||||
|
silver="#c0c0c0",
|
||||||
|
skyblue="#87ceeb",
|
||||||
|
slateblue="#6a5acd",
|
||||||
|
slategray="#708090",
|
||||||
|
slategrey="#708090",
|
||||||
|
snow="#fffafa",
|
||||||
|
springgreen="#00ff7f",
|
||||||
|
steelblue="#4682b4",
|
||||||
|
tan="#d2b48c",
|
||||||
|
teal="#008080",
|
||||||
|
thistle="#d8bfd8",
|
||||||
|
tomato="#ff6347",
|
||||||
|
turquoise="#40e0d0",
|
||||||
|
violet="#ee82ee",
|
||||||
|
wheat="#f5deb3",
|
||||||
|
white="#ffffff",
|
||||||
|
whitesmoke="#f5f5f5",
|
||||||
|
yellow="#ffff00",
|
||||||
|
yellowgreen="#9acd32",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_color_to_hex_format(unknown):
|
||||||
|
"""
|
||||||
|
Try to convert color info to a hex triplet string https://en.wikipedia.org/wiki/Web_colors#Hex_triplet.
|
||||||
|
|
||||||
|
The function accepts for the following formats:
|
||||||
|
- A CSS4 color name, as supported by matplotlib https://matplotlib.org/3.1.0/gallery/color/named_colors.html
|
||||||
|
- RGB tuple/list with values ranging from 0.0 to 1.0, as in [0.5, 0.75, 1.0]
|
||||||
|
- RFB tuple/list with values ranging from 0 to 255, as in [128, 192, 255]
|
||||||
|
- Hex triplet string, as in "#08c0ff"
|
||||||
|
|
||||||
|
:param unknown: color info of unknown format
|
||||||
|
:return: a hex triplet representing that color
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if type(unknown) in (list, tuple) and len(unknown) == 3:
|
||||||
|
if all(0.0 <= ele <= 1.0 for ele in unknown):
|
||||||
|
tup = tuple(int(ele * 255) for ele in unknown)
|
||||||
|
elif all(0 <= ele <= 255 and isinstance(ele, int) for ele in unknown):
|
||||||
|
tup = tuple(unknown)
|
||||||
|
else:
|
||||||
|
raise ColorFormatException("Unknown color iterable format!")
|
||||||
|
return "#%02x%02x%02x" % tup
|
||||||
|
elif isinstance(unknown, str) and unknown.lower() in CSS4_NAMED_COLORS:
|
||||||
|
return CSS4_NAMED_COLORS[unknown.lower()]
|
||||||
|
elif isinstance(unknown, str) and HEX_COLOR_FORMAT.match(unknown):
|
||||||
|
return unknown.lower()
|
||||||
|
else:
|
||||||
|
raise ColorFormatException("Unknown color format type!")
|
||||||
|
except Exception as e:
|
||||||
|
raise ColorFormatException(e)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_anndata_category_colors_to_cxg_category_colors(data):
|
||||||
|
"""
|
||||||
|
Convert color information from anndata files to the cellxgene color data format as described below:
|
||||||
|
{
|
||||||
|
"<category_name>": {
|
||||||
|
"<label_name>": "<color_hex_code>",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
For more on the cxg color data structure, see https://github.com/chanzuckerberg/cellxgene/issues/1307.
|
||||||
|
|
||||||
|
For more on the anndata color data structure, see
|
||||||
|
https://github.com/chanzuckerberg/cellxgene/issues/1152#issuecomment-587276178.
|
||||||
|
|
||||||
|
Handling of malformed data:
|
||||||
|
- For any color info in a adata.uns[f"{category}_colors"] color array that convert_color_to_hex_format cannot
|
||||||
|
convert to a hex triplet string, a ColorFormatException is raised
|
||||||
|
- No category_name key group is returned for adata.uns[f"{category}_colors"] keys for which there is no
|
||||||
|
adata.obs[f"{category}"] key
|
||||||
|
|
||||||
|
:param data: the anndata file
|
||||||
|
:return: cellxgene color data structure as described above
|
||||||
|
"""
|
||||||
|
cxg_colors = dict()
|
||||||
|
color_key_suffix = "_colors"
|
||||||
|
for uns_key in data.uns.keys():
|
||||||
|
# find uns array that describes colors for a category
|
||||||
|
if not uns_key.endswith(color_key_suffix):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check to see if we actually have observations for that category
|
||||||
|
category_name = uns_key[: -len(color_key_suffix)]
|
||||||
|
if category_name not in data.obs.keys():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# create the cellxgene color entry for this category
|
||||||
|
cxg_colors[category_name] = dict(
|
||||||
|
zip(data.obs[category_name].cat.categories, [convert_color_to_hex_format(c) for c in data.uns[uns_key]])
|
||||||
|
)
|
||||||
|
return cxg_colors
|
||||||
@@ -19,6 +19,7 @@ server:
|
|||||||
|
|
||||||
presentation:
|
presentation:
|
||||||
max_categories: 1000
|
max_categories: 1000
|
||||||
|
custom_colors: true
|
||||||
|
|
||||||
multi_dataset:
|
multi_dataset:
|
||||||
dataroot: null
|
dataroot: null
|
||||||
|
|||||||
@@ -84,3 +84,9 @@ class ComputeError(Exception):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ColorFormatException(Exception):
|
||||||
|
"""Raised when color helper functions encounter an unknown color format"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|||||||
+14
-2
@@ -1,9 +1,11 @@
|
|||||||
import sys
|
|
||||||
from http import HTTPStatus
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
from flask import make_response, jsonify, current_app, abort
|
from flask import make_response, jsonify, current_app, abort
|
||||||
from werkzeug.urls import url_unquote
|
from werkzeug.urls import url_unquote
|
||||||
|
|
||||||
from server.common.constants import Axis, DiffExpMode, JSON_NaN_to_num_warning_msg
|
from server.common.constants import Axis, DiffExpMode, JSON_NaN_to_num_warning_msg
|
||||||
from server.common.errors import (
|
from server.common.errors import (
|
||||||
FilterError,
|
FilterError,
|
||||||
@@ -12,6 +14,7 @@ from server.common.errors import (
|
|||||||
DisabledFeatureError,
|
DisabledFeatureError,
|
||||||
ExceedsLimitError,
|
ExceedsLimitError,
|
||||||
DatasetAccessError,
|
DatasetAccessError,
|
||||||
|
ColorFormatException,
|
||||||
)
|
)
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -222,6 +225,15 @@ def data_var_get(request, data_adaptor):
|
|||||||
return abort_and_log(HTTPStatus.BAD_REQUEST, str(e), include_exc_info=True)
|
return abort_and_log(HTTPStatus.BAD_REQUEST, str(e), include_exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
|
def colors_get(data_adaptor):
|
||||||
|
if not data_adaptor.config.presentation__custom_colors:
|
||||||
|
return make_response(jsonify({}), HTTPStatus.OK)
|
||||||
|
try:
|
||||||
|
return make_response(jsonify(data_adaptor.get_colors()), HTTPStatus.OK)
|
||||||
|
except ColorFormatException as e:
|
||||||
|
return abort_and_log(HTTPStatus.NOT_FOUND, str(e), include_exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
def diffexp_obs_post(request, data_adaptor):
|
def diffexp_obs_post(request, data_adaptor):
|
||||||
if not data_adaptor.config.diffexp__enable:
|
if not data_adaptor.config.diffexp__enable:
|
||||||
return abort(HTTPStatus.NOT_IMPLEMENTED)
|
return abort(HTTPStatus.NOT_IMPLEMENTED)
|
||||||
|
|||||||
@@ -4,28 +4,36 @@ into a cellxgene TileDB structure, aka a 'CXG'.
|
|||||||
|
|
||||||
The organization of the TileDB structure is:
|
The organization of the TileDB structure is:
|
||||||
|
|
||||||
the.cxg TileDB Group
|
the.cxg TileDB Group
|
||||||
|-- obs TileDB array containing cell (row) attributes, one attribute per
|
├─ obs TileDB array containing cell (row) attributes, one attribute per
|
||||||
| dataframe columm, shape (n_obs,)
|
│ dataframe column, shape (n_obs,)
|
||||||
|-- var TileDB array containing gene (column) attributes, with one attribute per
|
├─ var TileDB array containing gene (column) attributes, with one attribute per
|
||||||
| dataframe column, shape (n_var,)
|
│ dataframe column, shape (n_obs,)
|
||||||
|-- X Main count matrix as a 2D TileDB array, single unnanmed numeric attribute
|
├─ X Main count matrix as a 2D TileDB array, single unnamed numeric attribute
|
||||||
|-- emb TileDB group, storing optional embeddings (group may be empty)
|
├─ emb TileDB group, storing optional embeddings (group may be empty)
|
||||||
| |-- <name1> TileDB Array, single anon attribute, ND numeric array, shape (n_obs, N)
|
│ └─ <name1> TileDB Array, single anon attribute, ND numeric array, shape (n_obs, N)
|
||||||
|-- cxg_group_metadata Empty array used only to stash metadata about the overall object.
|
└─ cxg_group_metadata Empty array used only to stash metadata about the overall object.
|
||||||
|
└─ cxg_category_colors CXG colors object as described below:
|
||||||
|
{
|
||||||
|
"<category_name>": {
|
||||||
|
"<label_name>": "<color_hex_code>",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
...
|
||||||
|
}
|
||||||
...
|
...
|
||||||
|
|
||||||
All arrays are defined to have a uint32 domain, zero based. All X counds and embedding
|
All arrays are defined to have a uint32 domain, zero based. All X counts and embedding
|
||||||
coordinates are coerced to float32, which is ample precision for visualization purposes.
|
coordinates are coerced to float32, which is ample precision for visualization purposes.
|
||||||
Dataframe (metadata) types are generally preserved, or where that is not possible,
|
Dataframe (metadata) types are generally preserved, or where that is not possible,
|
||||||
converted to somemthing with equal representative value in the cellxgene application
|
converted to something with equal representative value in the cellxgene application
|
||||||
(eg, categorical types are converted to string, bools to uint8, etc).
|
(eg, categorical types are converted to string, bools to uint8, etc).
|
||||||
|
|
||||||
The following objects are also decorated with auxilliary metadata using TileDB
|
The following objects are also decorated with auxiliary metadata using TileDB
|
||||||
array metadata:
|
array metadata:
|
||||||
|
|
||||||
* cxg_group_metadata: minimally, will contain a 'cxg_version' field, which
|
* cxg_group_metadata: minimally, will contain a 'cxg_version' field, which
|
||||||
is a semver string identifing the version number of the CXG layout.
|
is a semver string identifying the version number of the CXG layout.
|
||||||
It may also contain 'cxg_parameters', a JSON-encoded parameter list
|
It may also contain 'cxg_parameters', a JSON-encoded parameter list
|
||||||
describing CXG-wide dataset parameters.
|
describing CXG-wide dataset parameters.
|
||||||
|
|
||||||
@@ -40,6 +48,14 @@ including the global data layout, spatial tile size, and the like. The CXG is
|
|||||||
self-describing in these areas, and the actual values (eg, tile size) are empirically
|
self-describing in these areas, and the actual values (eg, tile size) are empirically
|
||||||
derived from benchmarking. They may change in the future.
|
derived from benchmarking. They may change in the future.
|
||||||
|
|
||||||
|
cxgtool.py will extract color information stored in arrays in the 'uns' anndata
|
||||||
|
property with the key "{category_name}_colors". For this to work, the following
|
||||||
|
command must result in a mapping from category names to matplotlib-compatible colors:
|
||||||
|
|
||||||
|
```
|
||||||
|
dict(zip(adata.obs[cat].cat.categories, adata.uns[f"{cat}_colors"]))
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
TODO/ISSUES:
|
TODO/ISSUES:
|
||||||
@@ -55,10 +71,16 @@ import numpy as np
|
|||||||
from os.path import splitext, basename
|
from os.path import splitext, basename
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from server.common.colors import convert_anndata_category_colors_to_cxg_category_colors
|
||||||
|
from server.common.errors import ColorFormatException
|
||||||
|
|
||||||
|
|
||||||
# the CXG container version number. Must be a semver string.
|
# the CXG container version number. Must be a semver string.
|
||||||
CXG_VERSION = "0.1"
|
CXG_VERSION = "0.1"
|
||||||
|
|
||||||
|
# log_level must have a default
|
||||||
|
log_level = 3
|
||||||
|
|
||||||
|
|
||||||
def log(level, *args):
|
def log(level, *args):
|
||||||
global log_level
|
global log_level
|
||||||
@@ -72,6 +94,12 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--backed", action="store_true", help="loaded in file backed mode. Will be slower, but use less memory."
|
"--backed", action="store_true", help="loaded in file backed mode. Will be slower, but use less memory."
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--disable-custom-colors",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Do not extract scanpy-compatible category colors from h5ad file.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--obs-names", help="Name of annotation to use for observations. If not specified, will use the obs index."
|
"--obs-names", help="Name of annotation to use for observations. If not specified, will use the obs index."
|
||||||
)
|
)
|
||||||
@@ -99,12 +127,20 @@ def main():
|
|||||||
container = out if splitext(out)[1] == ".cxg" else out + ".cxg"
|
container = out if splitext(out)[1] == ".cxg" else out + ".cxg"
|
||||||
title = args.title if args.title is not None else basefname
|
title = args.title if args.title is not None else basefname
|
||||||
|
|
||||||
write_cxg(adata, container, title, var_names=args.var_names, obs_names=args.obs_names, about=args.about)
|
write_cxg(
|
||||||
|
adata,
|
||||||
|
container,
|
||||||
|
title,
|
||||||
|
var_names=args.var_names,
|
||||||
|
obs_names=args.obs_names,
|
||||||
|
about=args.about,
|
||||||
|
extract_colors=not args.disable_custom_colors,
|
||||||
|
)
|
||||||
|
|
||||||
log(1, "done")
|
log(1, "done")
|
||||||
|
|
||||||
|
|
||||||
def write_cxg(adata, container, title, var_names=None, obs_names=None, about=None):
|
def write_cxg(adata, container, title, var_names=None, obs_names=None, about=None, extract_colors=False):
|
||||||
if not adata.var.index.is_unique:
|
if not adata.var.index.is_unique:
|
||||||
raise ValueError("Variable index is not unique - unable to convert.")
|
raise ValueError("Variable index is not unique - unable to convert.")
|
||||||
if not adata.obs.index.is_unique:
|
if not adata.obs.index.is_unique:
|
||||||
@@ -129,7 +165,19 @@ def write_cxg(adata, container, title, var_names=None, obs_names=None, about=Non
|
|||||||
log(1, f"\t...group created, with name {container}")
|
log(1, f"\t...group created, with name {container}")
|
||||||
|
|
||||||
# dataset metadata
|
# dataset metadata
|
||||||
save_metadata(container, {"title": title, "about": about})
|
metadata_dict = dict(cxg_version=CXG_VERSION, cxg_properties=json.dumps({"title": title, "about": about}))
|
||||||
|
if extract_colors:
|
||||||
|
try:
|
||||||
|
metadata_dict["cxg_category_colors"] = json.dumps(
|
||||||
|
convert_anndata_category_colors_to_cxg_category_colors(adata)
|
||||||
|
)
|
||||||
|
except ColorFormatException:
|
||||||
|
log(
|
||||||
|
0,
|
||||||
|
"Warning: failed to extract colors from h5ad file! "
|
||||||
|
"Fix the h5ad file or rerun with --disable-custom-colors. See help for details.",
|
||||||
|
)
|
||||||
|
save_metadata(container, metadata_dict)
|
||||||
log(1, "\t...dataset metadata saved")
|
log(1, "\t...dataset metadata saved")
|
||||||
|
|
||||||
# var/gene dataframe
|
# var/gene dataframe
|
||||||
@@ -392,7 +440,7 @@ def save_X(container, adata, ctx):
|
|||||||
tiledb.consolidate(X_name, ctx=ctx)
|
tiledb.consolidate(X_name, ctx=ctx)
|
||||||
|
|
||||||
|
|
||||||
def save_metadata(container, metadata):
|
def save_metadata(container, metadata_dict):
|
||||||
"""
|
"""
|
||||||
Save all dataset-wide metadata. This includes:
|
Save all dataset-wide metadata. This includes:
|
||||||
* CXG version
|
* CXG version
|
||||||
@@ -407,8 +455,8 @@ def save_metadata(container, metadata):
|
|||||||
with tiledb.from_numpy(a_name, np.zeros((1,))) as A:
|
with tiledb.from_numpy(a_name, np.zeros((1,))) as A:
|
||||||
pass
|
pass
|
||||||
with tiledb.DenseArray(a_name, mode="w") as A:
|
with tiledb.DenseArray(a_name, mode="w") as A:
|
||||||
A.meta["cxg_version"] = CXG_VERSION
|
for k, v in metadata_dict.items():
|
||||||
A.meta["cxg_properties"] = json.dumps(metadata)
|
A.meta[k] = v
|
||||||
|
|
||||||
|
|
||||||
def sanitize_keys(keys):
|
def sanitize_keys(keys):
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from server_timing import Timing as ServerTiming
|
|||||||
from server.data_common.data_adaptor import DataAdaptor
|
from server.data_common.data_adaptor import DataAdaptor
|
||||||
from server.data_common.fbs.matrix import encode_matrix_fbs
|
from server.data_common.fbs.matrix import encode_matrix_fbs
|
||||||
from server.common.utils import series_to_schema
|
from server.common.utils import series_to_schema
|
||||||
|
from server.common.colors import convert_anndata_category_colors_to_cxg_category_colors
|
||||||
from server.common.constants import Axis, MAX_LAYOUTS
|
from server.common.constants import Axis, MAX_LAYOUTS
|
||||||
from server.common.errors import PrepareError, DatasetAccessError, FilterError
|
from server.common.errors import PrepareError, DatasetAccessError, FilterError
|
||||||
from server.compute.scanpy import scanpy_umap
|
from server.compute.scanpy import scanpy_umap
|
||||||
@@ -333,6 +334,9 @@ class AnndataAdaptor(DataAdaptor):
|
|||||||
lfc_cutoff = self.config.diffexp__lfc_cutoff
|
lfc_cutoff = self.config.diffexp__lfc_cutoff
|
||||||
return diffexp_generic.diffexp_ttest(self, maskA, maskB, top_n, lfc_cutoff)
|
return diffexp_generic.diffexp_ttest(self, maskA, maskB, top_n, lfc_cutoff)
|
||||||
|
|
||||||
|
def get_colors(self):
|
||||||
|
return convert_anndata_category_colors_to_cxg_category_colors(self.data)
|
||||||
|
|
||||||
def get_X_array(self, obs_mask=None, var_mask=None):
|
def get_X_array(self, obs_mask=None, var_mask=None):
|
||||||
if obs_mask is None:
|
if obs_mask is None:
|
||||||
obs_mask = slice(None)
|
obs_mask = slice(None)
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ class DataAdaptor(metaclass=ABCMeta):
|
|||||||
def query_obs_array(self, term_var):
|
def query_obs_array(self, term_var):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_colors(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_obs_index(self):
|
def get_obs_index(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ class CxgAdaptor(DataAdaptor):
|
|||||||
lfc_cutoff = self.config.diffexp__lfc_cutoff
|
lfc_cutoff = self.config.diffexp__lfc_cutoff
|
||||||
return diffexp_cxg.diffexp_ttest(self, maskA, maskB, top_n, lfc_cutoff)
|
return diffexp_cxg.diffexp_ttest(self, maskA, maskB, top_n, lfc_cutoff)
|
||||||
|
|
||||||
|
def get_colors(self):
|
||||||
|
meta = self.open_array("cxg_group_metadata").meta
|
||||||
|
return json.loads(meta["cxg_category_colors"]) if "cxg_category_colors" in meta else dict()
|
||||||
|
|
||||||
def get_X_array(self, obs_mask=None, var_mask=None):
|
def get_X_array(self, obs_mask=None, var_mask=None):
|
||||||
obs_items = pack_selector_from_mask(obs_mask)
|
obs_items = pack_selector_from_mask(obs_mask)
|
||||||
var_items = pack_selector_from_mask(var_mask)
|
var_items = pack_selector_from_mask(var_mask)
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ class WSGIServer(Server):
|
|||||||
if len(style_hashes) > 0:
|
if len(style_hashes) > 0:
|
||||||
csp["style-src"] = style_hashes
|
csp["style-src"] = style_hashes
|
||||||
|
|
||||||
Talisman(app, force_https=app_config.server__force_https, frame_options='DENY', content_security_policy=csp)
|
Talisman(app, force_https=app_config.server__force_https, frame_options="DENY", content_security_policy=csp)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_csp_hashes(app):
|
def load_csp_hashes(app):
|
||||||
|
|||||||
+24
-3
@@ -1,6 +1,6 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
from os import path
|
from os import path, popen
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
@@ -11,11 +11,14 @@ from server.data_common.fbs.matrix import encode_matrix_fbs
|
|||||||
from server.data_common.matrix_loader import MatrixDataLoader, MatrixDataType
|
from server.data_common.matrix_loader import MatrixDataLoader, MatrixDataType
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT_ROOT = popen("git rev-parse --show-toplevel").read().strip()
|
||||||
|
|
||||||
|
|
||||||
def data_with_tmp_annotations(ext: MatrixDataType, annotations_fixture=False):
|
def data_with_tmp_annotations(ext: MatrixDataType, annotations_fixture=False):
|
||||||
tmp_dir = tempfile.mkdtemp()
|
tmp_dir = tempfile.mkdtemp()
|
||||||
annotations_file = path.join(tmp_dir, "test_annotations.csv")
|
annotations_file = path.join(tmp_dir, "test_annotations.csv")
|
||||||
if annotations_fixture:
|
if annotations_fixture:
|
||||||
shutil.copyfile(f"test/test_datasets/pbmc3k-annotations.csv", annotations_file)
|
shutil.copyfile(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k-annotations.csv", annotations_file)
|
||||||
args = {
|
args = {
|
||||||
"embeddings__names": ["umap"],
|
"embeddings__names": ["umap"],
|
||||||
"presentation__max_categories": 100,
|
"presentation__max_categories": 100,
|
||||||
@@ -24,7 +27,7 @@ def data_with_tmp_annotations(ext: MatrixDataType, annotations_fixture=False):
|
|||||||
"diffexp__lfc_cutoff": 0.01,
|
"diffexp__lfc_cutoff": 0.01,
|
||||||
}
|
}
|
||||||
fname = {
|
fname = {
|
||||||
MatrixDataType.H5AD: "../example-dataset/pbmc3k.h5ad",
|
MatrixDataType.H5AD: f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad",
|
||||||
MatrixDataType.CXG: "test/test_datasets/pbmc3k.cxg",
|
MatrixDataType.CXG: "test/test_datasets/pbmc3k.cxg",
|
||||||
}[ext]
|
}[ext]
|
||||||
data_locator = DataLocator(fname)
|
data_locator = DataLocator(fname)
|
||||||
@@ -53,3 +56,21 @@ def skip_if(condition, reason: str):
|
|||||||
return wraps
|
return wraps
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def app_config(data_locator, backed=False):
|
||||||
|
args = {
|
||||||
|
"embeddings__names": ["umap", "tsne", "pca"],
|
||||||
|
"presentation__max_categories": 100,
|
||||||
|
"single_dataset__obs_names": None,
|
||||||
|
"single_dataset__var_names": None,
|
||||||
|
"diffexp__lfc_cutoff": 0.01,
|
||||||
|
"adaptor__anndata_adaptor__backed": backed,
|
||||||
|
"single_dataset__datapath": data_locator,
|
||||||
|
"limits__diffexp_cellcount_max": None,
|
||||||
|
"limits__column_request_max": None,
|
||||||
|
}
|
||||||
|
config = AppConfig()
|
||||||
|
config.update(**args)
|
||||||
|
config.complete_config()
|
||||||
|
return config
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ from parameterized import parameterized_class
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
|
||||||
from server.common.errors import FilterError
|
|
||||||
from server.common.data_locator import DataLocator
|
from server.common.data_locator import DataLocator
|
||||||
from server.common.app_config import AppConfig
|
from server.common.errors import FilterError
|
||||||
|
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
||||||
|
from server.test import PROJECT_ROOT, app_config
|
||||||
|
from server.test.test_datasets.fixtures import pbmc3k_colors
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Test the anndata adaptor using the pbmc3k data set.
|
Test the anndata adaptor using the pbmc3k data set.
|
||||||
@@ -23,30 +24,17 @@ Test the anndata adaptor using the pbmc3k data set.
|
|||||||
@parameterized_class(
|
@parameterized_class(
|
||||||
("data_locator", "backed"),
|
("data_locator", "backed"),
|
||||||
[
|
[
|
||||||
("../example-dataset/pbmc3k.h5ad", False),
|
(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad", False),
|
||||||
("test/test_datasets/pbmc3k-CSC-gz.h5ad", False),
|
(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k-CSC-gz.h5ad", False),
|
||||||
("test/test_datasets/pbmc3k-CSR-gz.h5ad", False),
|
(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k-CSR-gz.h5ad", False),
|
||||||
("../example-dataset/pbmc3k.h5ad", True),
|
(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad", True),
|
||||||
("test/test_datasets/pbmc3k-CSC-gz.h5ad", True),
|
(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k-CSC-gz.h5ad", True),
|
||||||
("test/test_datasets/pbmc3k-CSR-gz.h5ad", True),
|
(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k-CSR-gz.h5ad", True),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class AdaptorTest(unittest.TestCase):
|
class AdaptorTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
args = {
|
config = app_config(self.data_locator, self.backed)
|
||||||
"embeddings__names": ["umap", "tsne", "pca"],
|
|
||||||
"presentation__max_categories": 100,
|
|
||||||
"single_dataset__obs_names": None,
|
|
||||||
"single_dataset__var_names": None,
|
|
||||||
"diffexp__lfc_cutoff": 0.01,
|
|
||||||
"adaptor__anndata_adaptor__backed": self.backed,
|
|
||||||
"single_dataset__datapath": self.data_locator,
|
|
||||||
"limits__diffexp_cellcount_max": None,
|
|
||||||
"limits__column_request_max": None,
|
|
||||||
}
|
|
||||||
config = AppConfig()
|
|
||||||
config.update(**args)
|
|
||||||
config.complete_config()
|
|
||||||
self.data = AnndataAdaptor(DataLocator(self.data_locator), config)
|
self.data = AnndataAdaptor(DataLocator(self.data_locator), config)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
@@ -92,6 +80,9 @@ class AdaptorTest(unittest.TestCase):
|
|||||||
self.assertEqual(np.sum(self.data.data.var[self.data.get_schema()["annotations"]["var"]["index"]].isna()), 0)
|
self.assertEqual(np.sum(self.data.data.var[self.data.get_schema()["annotations"]["var"]["index"]].isna()), 0)
|
||||||
self.assertEqual(np.sum(self.data.data.obs[self.data.get_schema()["annotations"]["obs"]["index"]].isna()), 0)
|
self.assertEqual(np.sum(self.data.data.obs[self.data.get_schema()["annotations"]["obs"]["index"]].isna()), 0)
|
||||||
|
|
||||||
|
def test_get_colors(self):
|
||||||
|
self.assertEqual(self.data.get_colors(), pbmc3k_colors)
|
||||||
|
|
||||||
def test_get_schema(self):
|
def test_get_schema(self):
|
||||||
with open(path.join(path.dirname(__file__), "schema.json")) as fh:
|
with open(path.join(path.dirname(__file__), "schema.json")) as fh:
|
||||||
schema = json.load(fh)
|
schema = json.load(fh)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import json
|
|||||||
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
||||||
from server.common.data_locator import DataLocator
|
from server.common.data_locator import DataLocator
|
||||||
from server.common.app_config import AppConfig
|
from server.common.app_config import AppConfig
|
||||||
|
from server.test import PROJECT_ROOT
|
||||||
|
|
||||||
|
|
||||||
class DataLoadAdaptorTest(unittest.TestCase):
|
class DataLoadAdaptorTest(unittest.TestCase):
|
||||||
@@ -12,7 +13,7 @@ class DataLoadAdaptorTest(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.data_file = DataLocator("../example-dataset/pbmc3k.h5ad")
|
self.data_file = DataLocator(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad")
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.update(single_dataset__datapath=self.data_file.path)
|
config.update(single_dataset__datapath=self.data_file.path)
|
||||||
config.complete_config()
|
config.complete_config()
|
||||||
|
|||||||
+14
-3
@@ -8,8 +8,10 @@ import pandas as pd
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
import server.test.decode_fbs as decode_fbs
|
import server.test.decode_fbs as decode_fbs
|
||||||
from server.test import data_with_tmp_annotations, make_fbs
|
|
||||||
from server.data_common.matrix_loader import MatrixDataType
|
from server.data_common.matrix_loader import MatrixDataType
|
||||||
|
from server.test import data_with_tmp_annotations, make_fbs, PROJECT_ROOT
|
||||||
|
from server.test.test_datasets.fixtures import pbmc3k_colors
|
||||||
|
|
||||||
|
|
||||||
BAD_FILTER = {"filter": {"obs": {"annotation_value": [{"name": "xyz"}]}}}
|
BAD_FILTER = {"filter": {"obs": {"annotation_value": [{"name": "xyz"}]}}}
|
||||||
|
|
||||||
@@ -255,6 +257,15 @@ class EndPoints(object):
|
|||||||
self.assertEqual(df["n_rows"], 2638)
|
self.assertEqual(df["n_rows"], 2638)
|
||||||
self.assertEqual(df["n_cols"], 1)
|
self.assertEqual(df["n_cols"], 1)
|
||||||
|
|
||||||
|
def test_colors(self):
|
||||||
|
endpoint = "colors"
|
||||||
|
url = f"{self.URL_BASE}{endpoint}"
|
||||||
|
result = self.session.get(url)
|
||||||
|
self.assertEqual(result.status_code, HTTPStatus.OK)
|
||||||
|
self.assertEqual(result.headers["Content-Type"], "application/json")
|
||||||
|
result_data = result.json()
|
||||||
|
self.assertEqual(result_data, pbmc3k_colors)
|
||||||
|
|
||||||
def test_static(self):
|
def test_static(self):
|
||||||
endpoint = "static"
|
endpoint = "static"
|
||||||
file = "assets/favicon.ico"
|
file = "assets/favicon.ico"
|
||||||
@@ -349,7 +360,7 @@ class EndPointsAnndata(unittest.TestCase, EndPoints):
|
|||||||
"cellxgene",
|
"cellxgene",
|
||||||
"--no-upgrade-check",
|
"--no-upgrade-check",
|
||||||
"launch",
|
"launch",
|
||||||
"../example-dataset/pbmc3k.h5ad",
|
f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad",
|
||||||
"--disable-annotations",
|
"--disable-annotations",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
"--port",
|
"--port",
|
||||||
@@ -383,7 +394,7 @@ class EndPointsCxg(unittest.TestCase, EndPoints):
|
|||||||
"cellxgene",
|
"cellxgene",
|
||||||
"--no-upgrade-check",
|
"--no-upgrade-check",
|
||||||
"launch",
|
"launch",
|
||||||
"test/test_datasets/pbmc3k.cxg",
|
f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k.cxg",
|
||||||
"--disable-annotations",
|
"--disable-annotations",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
"--port",
|
"--port",
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import anndata
|
||||||
|
from server.common.colors import convert_color_to_hex_format, convert_anndata_category_colors_to_cxg_category_colors
|
||||||
|
from server.common.errors import ColorFormatException
|
||||||
|
from server.test import PROJECT_ROOT
|
||||||
|
from server.test.test_datasets.fixtures import pbmc3k_colors
|
||||||
|
|
||||||
|
|
||||||
|
class ColorsTest(unittest.TestCase):
|
||||||
|
""" Test color helper functions """
|
||||||
|
|
||||||
|
def test_convert_color_to_hex_format(self):
|
||||||
|
self.assertEqual(convert_color_to_hex_format("wheat"), "#f5deb3")
|
||||||
|
self.assertEqual(convert_color_to_hex_format("WHEAT"), "#f5deb3")
|
||||||
|
self.assertEqual(convert_color_to_hex_format((245, 222, 179)), "#f5deb3")
|
||||||
|
self.assertEqual(convert_color_to_hex_format([245, 222, 179]), "#f5deb3")
|
||||||
|
self.assertEqual(convert_color_to_hex_format("#f5deb3"), "#f5deb3")
|
||||||
|
self.assertEqual(
|
||||||
|
convert_color_to_hex_format([0.9607843137254902, 0.8705882352941177, 0.7019607843137254]), "#f5deb3"
|
||||||
|
)
|
||||||
|
for bad_input in ["foo", "BAR", "#AABB", "#AABBCCDD", "#AABBGG", (1, 2), [1, 2], (1, 2, 3, 4), [1, 2, 3, 4]]:
|
||||||
|
with self.assertRaises(ColorFormatException):
|
||||||
|
convert_color_to_hex_format(bad_input)
|
||||||
|
|
||||||
|
def test_anndata_colors_to_cxg_colors(self):
|
||||||
|
# test standard behavior
|
||||||
|
adata = self._get_h5ad()
|
||||||
|
self.assertEqual(convert_anndata_category_colors_to_cxg_category_colors(adata), pbmc3k_colors)
|
||||||
|
# test that invalid color formats raise an exception
|
||||||
|
adata.uns["louvain_colors"][0] = "#NOTCOOL"
|
||||||
|
with self.assertRaises(ColorFormatException):
|
||||||
|
convert_anndata_category_colors_to_cxg_category_colors(adata)
|
||||||
|
# test that colors without a matching obs category are skipped
|
||||||
|
adata = self._get_h5ad()
|
||||||
|
del adata.obs["louvain"]
|
||||||
|
self.assertEqual(convert_anndata_category_colors_to_cxg_category_colors(adata), {})
|
||||||
|
|
||||||
|
def _get_h5ad(self):
|
||||||
|
return anndata.read_h5ad(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad")
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from server.common.data_locator import DataLocator
|
||||||
|
from server.data_cxg.cxg_adaptor import CxgAdaptor
|
||||||
|
from server.test import PROJECT_ROOT, app_config
|
||||||
|
from server.test.test_datasets.fixtures import pbmc3k_colors
|
||||||
|
|
||||||
|
|
||||||
|
class TestCxgAdaptor(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
data_locator = f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k.cxg"
|
||||||
|
config = app_config(data_locator)
|
||||||
|
self.data = CxgAdaptor(DataLocator(data_locator), config)
|
||||||
|
|
||||||
|
def test_get_colors(self):
|
||||||
|
self.assertEqual(self.data.get_colors(), pbmc3k_colors)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
import string
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import anndata
|
||||||
|
|
||||||
|
from server.common.data_locator import DataLocator
|
||||||
|
from server.converters.cxgtool import write_cxg
|
||||||
|
from server.data_cxg.cxg_adaptor import CxgAdaptor
|
||||||
|
from server.test import PROJECT_ROOT, app_config
|
||||||
|
from server.test.test_datasets.fixtures import pbmc3k_colors
|
||||||
|
|
||||||
|
|
||||||
|
class TestCxgAdaptor(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.fixtures = []
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
try:
|
||||||
|
for data_locator in self.fixtures:
|
||||||
|
print("REMOVING ", data_locator)
|
||||||
|
shutil.rmtree(data_locator)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_cxg_category_colors(self):
|
||||||
|
data = self.convert_pbmc3k(extract_colors=True)
|
||||||
|
self.assertEqual(data.get_colors(), pbmc3k_colors)
|
||||||
|
data = self.convert_pbmc3k(extract_colors=False)
|
||||||
|
self.assertEqual(data.get_colors(), {})
|
||||||
|
|
||||||
|
def convert_pbmc3k(self, **kwargs):
|
||||||
|
random_string = "".join(random.choice(string.ascii_letters) for _ in range(8))
|
||||||
|
data_locator = f"/tmp/test_{random_string}.cxg"
|
||||||
|
self.fixtures.append(data_locator)
|
||||||
|
source_h5ad = anndata.read_h5ad(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad")
|
||||||
|
write_cxg(adata=source_h5ad, container=data_locator, title="pbmc3k", **kwargs)
|
||||||
|
config = app_config(data_locator)
|
||||||
|
return CxgAdaptor(DataLocator(data_locator), config)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
pbmc3k_colors = {
|
||||||
|
"louvain": {
|
||||||
|
"B cells": "#2ca02c",
|
||||||
|
"CD14+ Monocytes": "#ff7f0e",
|
||||||
|
"CD4 T cells": "#1f77b4",
|
||||||
|
"CD8 T cells": "#d62728",
|
||||||
|
"Dendritic cells": "#e377c2",
|
||||||
|
"FCGR3A+ Monocytes": "#8c564b",
|
||||||
|
"Megakaryocytes": "#bcbd22",
|
||||||
|
"NK cells": "#9467bd",
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
Regular → Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
Regular → Executable
@@ -5,6 +5,8 @@ import server.compute.diffexp_cxg as diffexp_cxg
|
|||||||
import server.compute.diffexp_generic as diffexp_generic
|
import server.compute.diffexp_generic as diffexp_generic
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from server.test import PROJECT_ROOT
|
||||||
|
|
||||||
|
|
||||||
class DiffExpTest(unittest.TestCase):
|
class DiffExpTest(unittest.TestCase):
|
||||||
"""Tests the diffexp returns the expected results for one test case, using different
|
"""Tests the diffexp returns the expected results for one test case, using different
|
||||||
@@ -50,7 +52,7 @@ class DiffExpTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_anndata_default(self):
|
def test_anndata_default(self):
|
||||||
"""Test an anndata adaptor with its default diffexp algorithm (diffexp_generic)"""
|
"""Test an anndata adaptor with its default diffexp algorithm (diffexp_generic)"""
|
||||||
adaptor = self.load_dataset("../example-dataset/pbmc3k.h5ad")
|
adaptor = self.load_dataset(f"{PROJECT_ROOT}/example-dataset/pbmc3k.h5ad")
|
||||||
maskA = self.get_mask(adaptor, 1, 10)
|
maskA = self.get_mask(adaptor, 1, 10)
|
||||||
maskB = self.get_mask(adaptor, 2, 10)
|
maskB = self.get_mask(adaptor, 2, 10)
|
||||||
results = adaptor.compute_diffexp_ttest(maskA, maskB, 10)
|
results = adaptor.compute_diffexp_ttest(maskA, maskB, 10)
|
||||||
@@ -58,7 +60,7 @@ class DiffExpTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_cxg_default(self):
|
def test_cxg_default(self):
|
||||||
"""Test a cxg adaptor with its default diffexp algorithm (diffexp_cxg)"""
|
"""Test a cxg adaptor with its default diffexp algorithm (diffexp_cxg)"""
|
||||||
adaptor = self.load_dataset("test/test_datasets/pbmc3k.cxg")
|
adaptor = self.load_dataset(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k.cxg")
|
||||||
maskA = self.get_mask(adaptor, 1, 10)
|
maskA = self.get_mask(adaptor, 1, 10)
|
||||||
maskB = self.get_mask(adaptor, 2, 10)
|
maskB = self.get_mask(adaptor, 2, 10)
|
||||||
|
|
||||||
@@ -72,7 +74,7 @@ class DiffExpTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_cxg_generic(self):
|
def test_cxg_generic(self):
|
||||||
"""Test a cxg adaptor with the generic adaptor"""
|
"""Test a cxg adaptor with the generic adaptor"""
|
||||||
adaptor = self.load_dataset("test/test_datasets/pbmc3k.cxg")
|
adaptor = self.load_dataset(f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k.cxg")
|
||||||
maskA = self.get_mask(adaptor, 1, 10)
|
maskA = self.get_mask(adaptor, 1, 10)
|
||||||
maskB = self.get_mask(adaptor, 2, 10)
|
maskB = self.get_mask(adaptor, 2, 10)
|
||||||
# run it directly
|
# run it directly
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ import shutil
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from server.test import PROJECT_ROOT
|
||||||
|
|
||||||
|
|
||||||
class MatrixCacheTest(unittest.TestCase):
|
class MatrixCacheTest(unittest.TestCase):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def make_temporay_datasets(self, dirname, num):
|
def make_temporay_datasets(self, dirname, num):
|
||||||
source = "test/test_datasets/pbmc3k.cxg"
|
source = f"{PROJECT_ROOT}/server/test/test_datasets/pbmc3k.cxg"
|
||||||
for i in range(num):
|
for i in range(num):
|
||||||
target = os.path.join(dirname, str(i) + ".cxg")
|
target = os.path.join(dirname, str(i) + ".cxg")
|
||||||
shutil.copytree(source, target)
|
shutil.copytree(source, target)
|
||||||
|
|||||||
@@ -8,39 +8,22 @@ import server.test.decode_fbs as decode_fbs
|
|||||||
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
from server.data_anndata.anndata_adaptor import AnndataAdaptor
|
||||||
from server.common.errors import FilterError
|
from server.common.errors import FilterError
|
||||||
from server.common.data_locator import DataLocator
|
from server.common.data_locator import DataLocator
|
||||||
from server.common.app_config import AppConfig
|
from server.test import PROJECT_ROOT, app_config
|
||||||
|
|
||||||
|
|
||||||
class NaNTest(unittest.TestCase):
|
class NaNTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.args = {
|
self.data_locator = DataLocator(f"{PROJECT_ROOT}/server/test/test_datasets/nan.h5ad")
|
||||||
"embeddings__names": ["umap"],
|
self.config = app_config(self.data_locator.path)
|
||||||
"presentation__max_categories": 100,
|
|
||||||
"single_dataset__obs_names": None,
|
|
||||||
"single_dataset__var_names": None,
|
|
||||||
"diffexp__lfc_cutoff": 0.01,
|
|
||||||
"limits__diffexp_cellcount_max": None,
|
|
||||||
"limits__column_request_max": None,
|
|
||||||
}
|
|
||||||
config = AppConfig()
|
|
||||||
config.update(**self.args)
|
|
||||||
locator = DataLocator("test/test_datasets/nan.h5ad")
|
|
||||||
config.update(single_dataset__datapath=locator.path)
|
|
||||||
config.complete_config()
|
|
||||||
|
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("ignore", category=UserWarning)
|
warnings.simplefilter("ignore", category=UserWarning)
|
||||||
self.data = AnndataAdaptor(locator, config)
|
self.data = AnndataAdaptor(self.data_locator, self.config)
|
||||||
self.data._create_schema()
|
self.data._create_schema()
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
with self.assertWarns(UserWarning):
|
with self.assertWarns(UserWarning):
|
||||||
config = AppConfig()
|
self.data = AnndataAdaptor(self.data_locator, self.config)
|
||||||
config.update(**self.args)
|
|
||||||
locator = DataLocator("test/test_datasets/nan.h5ad")
|
|
||||||
config.update(single_dataset__datapath=locator.path)
|
|
||||||
config.complete_config()
|
|
||||||
self.data = AnndataAdaptor(locator, config)
|
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
self.assertEqual(self.data.cell_count, 100)
|
self.assertEqual(self.data.cell_count, 100)
|
||||||
|
|||||||
Reference in New Issue
Block a user