Color by gene set mean expression (#2157)

* colorby histo

* color graph by mean expression

* move var index after returns

* add genesets as an argument

* varindex

* undo redo for mean expression

* destructure

* ternary

* Revert "destructure"

This reverts commit 2d9432c1c7.

* color by mean for diffexp
This commit is contained in:
Colin Megill
2021-04-22 13:58:30 -04:00
committed by GitHub
parent 860547ced1
commit fd2a7a53ab
10 changed files with 170 additions and 27 deletions
@@ -44,6 +44,8 @@ const LABEL_WIDTH_ANNO = LABEL_WIDTH - ANNO_BUTTON_WIDTH;
schema, schema,
crossfilter: state.obsCrossfilter, crossfilter: state.obsCrossfilter,
isUserAnno, isUserAnno,
genesets: state.genesets.genesets,
differential: state.differential,
}; };
}) })
class Category extends React.PureComponent { class Category extends React.PureComponent {
@@ -142,9 +144,16 @@ class Category extends React.PureComponent {
*/ */
const { schema } = annoMatrix; const { schema } = annoMatrix;
const { colorAccessor, colorMode } = colors; const { colorAccessor, colorMode } = colors;
const { genesets, differential } = this.props;
let colorDataPromise = Promise.resolve(null); let colorDataPromise = Promise.resolve(null);
if (colorAccessor) { if (colorAccessor) {
const query = createColorQuery(colorMode, colorAccessor, schema); const query = createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
if (query) colorDataPromise = annoMatrix.fetch(...query); if (query) colorDataPromise = annoMatrix.fetch(...query);
} }
const [categoryData, colorData] = await Promise.all([ const [categoryData, colorData] = await Promise.all([
@@ -106,16 +106,26 @@ const continuous = (selectorId, colorScale, colorAccessor) => {
@connect((state) => ({ @connect((state) => ({
annoMatrix: state.annoMatrix, annoMatrix: state.annoMatrix,
colors: state.colors, colors: state.colors,
genesets: state.genesets.genesets,
differential: state.differential,
})) }))
class ContinuousLegend extends React.Component { class ContinuousLegend extends React.Component {
async componentDidUpdate(prevProps) { async componentDidUpdate(prevProps) {
const { annoMatrix, colors } = this.props; const { annoMatrix, colors, genesets, differential } = this.props;
if (!colors || !annoMatrix) return; if (!colors || !annoMatrix) return;
if (colors !== prevProps?.colors || annoMatrix !== prevProps?.annoMatrix) { if (colors !== prevProps?.colors || annoMatrix !== prevProps?.annoMatrix) {
const { schema } = annoMatrix; const { schema } = annoMatrix;
const { colorMode, colorAccessor, userColors } = colors; const { colorMode, colorAccessor, userColors } = colors;
const colorQuery = createColorQuery(colorMode, colorAccessor, schema);
const colorQuery = createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
const colorDf = colorQuery ? await annoMatrix.fetch(...colorQuery) : null; const colorDf = colorQuery ? await annoMatrix.fetch(...colorQuery) : null;
const colorTable = createColorTable( const colorTable = createColorTable(
colorMode, colorMode,
@@ -184,7 +184,12 @@ class GeneSet extends React.Component {
)} )}
</span> </span>
<div> <div>
<GenesetMenus genesetsEditable geneset={setName} /> <GenesetMenus
isOpen={isOpen}
toggleSummaryHisto={toggleSummaryHisto}
genesetsEditable
geneset={setName}
/>
</div> </div>
</div> </div>
@@ -1,12 +1,14 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Tooltip2 } from "@blueprintjs/popover2";
import { import {
Button, Button,
AnchorButton,
Menu, Menu,
MenuItem, MenuItem,
Popover, Popover,
Position, Position,
Tooltip,
Icon, Icon,
PopoverInteractionKind, PopoverInteractionKind,
} from "@blueprintjs/core"; } from "@blueprintjs/core";
@@ -18,6 +20,7 @@ import AddGeneToGenesetDialogue from "./addGeneToGenesetDialogue";
@connect((state) => { @connect((state) => {
return { return {
genesetsUI: state.genesetsUI, genesetsUI: state.genesetsUI,
colorAccessor: state.colors.colorAccessor,
}; };
}) })
class GenesetMenus extends React.PureComponent { class GenesetMenus extends React.PureComponent {
@@ -43,19 +46,41 @@ class GenesetMenus extends React.PureComponent {
}); });
}; };
handleColorByEntireGeneset = () => {
const { dispatch, geneset } = this.props;
dispatch({
type: "color by geneset mean expression",
geneset,
});
};
handleDeleteCategory = () => { handleDeleteCategory = () => {
const { dispatch, geneset } = this.props; const { dispatch, geneset } = this.props;
dispatch(actions.genesetDelete(geneset)); dispatch(actions.genesetDelete(geneset));
}; };
render() { render() {
const { geneset, genesetsEditable, createText } = this.props; const {
geneset,
genesetsEditable,
createText,
colorAccessor,
isOpen,
toggleSummaryHisto,
} = this.props;
const isColorBy = geneset === colorAccessor;
const genesetClosed = !isOpen;
const showingAllGenes = !toggleSummaryHisto;
const notShowingSummary = genesetClosed || showingAllGenes;
return ( return (
<> <>
{genesetsEditable ? ( {genesetsEditable && (
<> <>
<Tooltip <Tooltip2
content={createText} content={createText}
position={Position.BOTTOM} position={Position.BOTTOM}
hoverOpenDelay={globals.tooltipHoverOpenDelay} hoverOpenDelay={globals.tooltipHoverOpenDelay}
@@ -69,7 +94,7 @@ class GenesetMenus extends React.PureComponent {
small small
minimal minimal
/> />
</Tooltip> </Tooltip2>
<AddGeneToGenesetDialogue geneset={geneset} /> <AddGeneToGenesetDialogue geneset={geneset} />
<Popover <Popover
interactionKind={PopoverInteractionKind.HOVER} interactionKind={PopoverInteractionKind.HOVER}
@@ -104,15 +129,24 @@ class GenesetMenus extends React.PureComponent {
minimal minimal
/> />
</Popover> </Popover>
<Button <Tooltip2
disabled content={`Color by gene set ${geneset} mean (gene set must be open, and toggled to mean expression histogram)`}
style={{ marginLeft: 0 }} position={Position.BOTTOM}
data-testclass="colorby-entire-geneset" hoverOpenDelay={globals.tooltipHoverOpenDelay}
data-testid={`${geneset}:colorby-entire-geneset`} >
icon={<Icon icon="tint" iconSize={16} />} <AnchorButton
/> disabled={notShowingSummary && !isColorBy}
active={isColorBy}
intent={isColorBy ? "primary" : "none"}
style={{ marginLeft: 0 }}
onClick={this.handleColorByEntireGeneset}
data-testclass="colorby-entire-geneset"
data-testid={`${geneset}:colorby-entire-geneset`}
icon={<Icon icon="tint" iconSize={16} />}
/>
</Tooltip2>
</> </>
) : null} )}
</> </>
); );
} }
+12 -2
View File
@@ -76,6 +76,8 @@ function createModelTF() {
graphInteractionMode: state.controls.graphInteractionMode, graphInteractionMode: state.controls.graphInteractionMode,
colors: state.colors, colors: state.colors,
pointDilation: state.pointDilation, pointDilation: state.pointDilation,
genesets: state.genesets.genesets,
differential: state.differential,
})) }))
class Graph extends React.Component { class Graph extends React.Component {
static createReglState(canvas) { static createReglState(canvas) {
@@ -526,6 +528,7 @@ class Graph extends React.Component {
colorsProp, colorsProp,
pointDilation pointDilation
); );
const { currentDimNames } = layoutChoice; const { currentDimNames } = layoutChoice;
const X = layoutDf.col(currentDimNames[0]).asArray(); const X = layoutDf.col(currentDimNames[0]).asArray();
const Y = layoutDf.col(currentDimNames[1]).asArray(); const Y = layoutDf.col(currentDimNames[1]).asArray();
@@ -782,10 +785,17 @@ class Graph extends React.Component {
} }
createColorByQuery(colors) { createColorByQuery(colors) {
const { annoMatrix } = this.props; const { annoMatrix, genesets, differential } = this.props;
const { schema } = annoMatrix; const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors; const { colorMode, colorAccessor } = colors;
return createColorQuery(colorMode, colorAccessor, schema);
return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
} }
renderPoints( renderPoints(
@@ -14,6 +14,8 @@ export default
dilatedValue: state.pointDilation.categoryField, dilatedValue: state.pointDilation.categoryField,
categoricalSelection: state.categoricalSelection, categoricalSelection: state.categoricalSelection,
showLabels: state.centroidLabels?.showLabels, showLabels: state.centroidLabels?.showLabels,
genesets: state.genesets.genesets,
differential: state.differential,
})) }))
class CentroidLabels extends PureComponent { class CentroidLabels extends PureComponent {
static watchAsync(props, prevProps) { static watchAsync(props, prevProps) {
@@ -74,10 +76,16 @@ class CentroidLabels extends PureComponent {
}; };
colorByQuery() { colorByQuery() {
const { annoMatrix, colors } = this.props; const { annoMatrix, colors, genesets, differential } = this.props;
const { schema } = annoMatrix; const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors; const { colorMode, colorAccessor } = colors;
return createColorQuery(colorMode, colorAccessor, schema); return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential.diffExp
);
} }
async fetchData() { async fetchData() {
@@ -53,6 +53,7 @@ const getYScale = memoize(getScale);
differential: state.differential, differential: state.differential,
crossfilter, crossfilter,
genesets: state.genesets.genesets,
}; };
}) })
class Scatterplot extends React.PureComponent { class Scatterplot extends React.PureComponent {
@@ -313,10 +314,16 @@ class Scatterplot extends React.PureComponent {
} }
createColorByQuery(colors) { createColorByQuery(colors) {
const { annoMatrix } = this.props; const { annoMatrix, genesets, differential } = this.props;
const { schema } = annoMatrix; const { schema } = annoMatrix;
const { colorMode, colorAccessor } = colors; const { colorMode, colorAccessor } = colors;
return createColorQuery(colorMode, colorAccessor, schema); return createColorQuery(
colorMode,
colorAccessor,
schema,
genesets,
differential
);
} }
updateColorTable(colors, colorDf) { updateColorTable(colors, colorDf) {
+17 -2
View File
@@ -4,8 +4,8 @@ Color By UI state
const ColorsReducer = ( const ColorsReducer = (
state = { state = {
colorMode: null, colorMode: null /* by continuous, by expression */,
colorAccessor: null, colorAccessor: null /* tissue, Apod */,
}, },
action, action,
nextSharedState, nextSharedState,
@@ -96,6 +96,21 @@ const ColorsReducer = (
}; };
} }
case "color by geneset mean expression": {
/* toggle between this mode and reset */
const resetCurrent =
action.type === state.colorMode &&
action.geneset === state.colorAccessor;
const colorMode = !resetCurrent ? action.type : null;
const colorAccessor = !resetCurrent ? action.geneset : null;
return {
...state,
colorMode,
colorAccessor,
};
}
default: { default: {
return state; return state;
} }
+1
View File
@@ -80,6 +80,7 @@ const saveOnActions = new Set([
"color by categorical metadata", "color by categorical metadata",
"color by continuous metadata", "color by continuous metadata",
"color by expression", "color by expression",
"color by geneset mean expression",
"show centroid labels for category", "show centroid labels for category",
+46 -2
View File
@@ -12,8 +12,15 @@ import { range } from "../range";
given a color mode & accessor, generate an annoMatrix query that will given a color mode & accessor, generate an annoMatrix query that will
fulfill it fulfill it
*/ */
export function createColorQuery(colorMode, colorByAccessor, schema) { export function createColorQuery(
if (!colorMode || !colorByAccessor || !schema) return null; colorMode,
colorByAccessor,
schema,
genesets,
diffExp
) {
if (!colorMode || !colorByAccessor || !schema || !genesets) return null;
switch (colorMode) { switch (colorMode) {
case "color by categorical metadata": case "color by categorical metadata":
case "color by continuous metadata": { case "color by continuous metadata": {
@@ -33,6 +40,38 @@ export function createColorQuery(colorMode, colorByAccessor, schema) {
}, },
]; ];
} }
case "color by geneset mean expression": {
const varIndex = schema?.annotations?.var?.index;
if (!varIndex) return null;
if (!genesets) return null;
let _geneset;
let _setGenes;
if (colorByAccessor === "Temp DiffExp Set") {
_geneset = diffExp;
_setGenes = _geneset.map((diffexpResultItem) => {
const geneName = diffexpResultItem[0];
return geneName;
});
} else {
_geneset = genesets.get(colorByAccessor);
_setGenes = [..._geneset.genes.keys()];
}
return [
"X",
{
summarize: {
method: "mean",
field: "var",
column: varIndex,
values: _setGenes,
},
},
];
}
default: { default: {
return null; return null;
} }
@@ -86,6 +125,11 @@ function _createColorTable(
const { min, max } = col.summarize(); const { min, max } = col.summarize();
return createColorsByContinuousMetadata(col.asArray(), min, max); return createColorsByContinuousMetadata(col.asArray(), min, max);
} }
case "color by geneset mean expression": {
const col = colorByData.icol(0);
const { min, max } = col.summarize();
return createColorsByContinuousMetadata(col.asArray(), min, max);
}
default: { default: {
return defaultColors(schema.dataframe.nObs); return defaultColors(schema.dataframe.nObs);
} }