mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-16 05:07:55 +08:00
* mocks for redux refactor - for discussion * more API design on redux refactor * add new reuqired dependencies for build * change babel target to use modern browser * remove dead code * remove dead code - joy plots * checkpoint on redux refactoring * checkpoint on redux refactoring * fix mistaken rebase conflict resolution * dead code removal; add name to dataframe backmap * rename dataframe to universe * update eslint config to more closely match prettier * lint * more eslint updates to match prettier * additional config to make eslint match prettier * add expression data to Universe/World * remove obsolete reducers * lint fixes * more eslint cleanup * lint * lint * fix but in countAllOnes when dimensions gt 1 * lint; do not display name metadata field * lint; colors refactor * lint; colors refactor * update comments * first cut at regraph and reset * enable object-curly-braces consistent mode * lint, handle regraph with no selection * fix expression scatterplot bugs * fix regression legend display * add expression data cache * remove console logging * reset cell color on regraph/reset * remove obsolete server URLs * rename UniverseV01 to Universe_REST_API_v01 * add additional comments on the varDataCache * merge universe reducer into controls reducer; simplify initialization-related actions * use spread operator * fix erroneous comment * convert universe and world state to plain objects, and functionalize supporting code (remove ES6 classes) * use spread operator * lint * improve variable names * rename obsCrossfilter to crossfilter and obsDimensionMap to dimensionMap * rename controls2 to controls
310 lines
8.2 KiB
JavaScript
310 lines
8.2 KiB
JavaScript
// jshint esversion: 6
|
|
import React from "react";
|
|
import _ from "lodash";
|
|
import memoize from "memoize-one";
|
|
import { connect } from "react-redux";
|
|
import * as globals from "../../globals";
|
|
import styles from "./expression.css";
|
|
import SectionHeader from "../framework/sectionHeader";
|
|
import actions from "../../actions";
|
|
import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */
|
|
import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast
|
|
import FaPaintBrush from "react-icons/lib/fa/paint-brush";
|
|
import * as d3 from "d3";
|
|
import { interpolateGreys } from "d3-scale-chromatic";
|
|
|
|
class HeatmapSquare extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
value: ""
|
|
};
|
|
}
|
|
|
|
render() {
|
|
const contrastColor = getContrast(
|
|
this.props.backgroundColor
|
|
.substring(4, this.props.backgroundColor.length - 1)
|
|
.replace(/ /g, "")
|
|
.split(",")
|
|
);
|
|
return (
|
|
<p
|
|
style={{
|
|
padding: "12px 6px",
|
|
textAlign: "center",
|
|
color: contrastColor,
|
|
width: 40,
|
|
flexShrink: 0,
|
|
fontSize: 12,
|
|
margin: 0,
|
|
backgroundColor: this.props.backgroundColor
|
|
}}
|
|
>
|
|
{this.props.text}
|
|
</p>
|
|
);
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
***********************************
|
|
***********************************
|
|
Row
|
|
***********************************
|
|
***********************************
|
|
**********************************/
|
|
@connect(state => {
|
|
return {
|
|
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
|
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
|
|
colorAccessor: state.controls.colorAccessor
|
|
};
|
|
})
|
|
class HeatmapRow extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
value: ""
|
|
};
|
|
}
|
|
|
|
handleGeneColorScaleClick(gene) {
|
|
return () => {
|
|
this.props.dispatch(
|
|
actions.requestSingleGeneExpressionCountsForColoringPOST(
|
|
this.props.gene
|
|
)
|
|
);
|
|
};
|
|
}
|
|
|
|
handleSetGeneAsScatterplotX(gene) {
|
|
return () => {
|
|
this.props.dispatch({
|
|
type: "set scatterplot x",
|
|
data: this.props.gene
|
|
});
|
|
};
|
|
}
|
|
|
|
handleSetGeneAsScatterplotY(gene) {
|
|
return () => {
|
|
this.props.dispatch({
|
|
type: "set scatterplot y",
|
|
data: this.props.gene
|
|
});
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div
|
|
style={{
|
|
width: 220,
|
|
display: "flex",
|
|
justifyContent: "flex-start",
|
|
alignItems: "baseline"
|
|
}}
|
|
>
|
|
<div style={{ width: 150, flexShrink: 0 }}>
|
|
<span
|
|
style={{
|
|
fontSize: 14
|
|
}}
|
|
>
|
|
{this.props.gene}
|
|
</span>
|
|
</div>
|
|
<HeatmapSquare
|
|
backgroundColor={this.props.greyColorScale(this.props.set1exp)}
|
|
text={this.props.set1exp}
|
|
/>
|
|
<HeatmapSquare
|
|
backgroundColor={this.props.greyColorScale(this.props.set2exp)}
|
|
text={this.props.set2exp}
|
|
/>
|
|
<span
|
|
title={this.props.aveDiff}
|
|
style={{
|
|
fontSize: 12,
|
|
marginLeft: 10,
|
|
marginRight: 10
|
|
}}
|
|
>
|
|
{this.props.aveDiff.toFixed(2)}
|
|
</span>
|
|
<span
|
|
onClick={this.handleSetGeneAsScatterplotX(this.props.gene).bind(this)}
|
|
style={{
|
|
fontSize: 16,
|
|
color:
|
|
this.props.scatterplotXXaccessor === this.props.gene
|
|
? "white"
|
|
: globals.brightBlue,
|
|
cursor: "pointer",
|
|
position: "relative",
|
|
top: 1,
|
|
fontWeight: 700,
|
|
marginRight: 4,
|
|
borderRadius: 3,
|
|
padding: "2px 3px",
|
|
backgroundColor:
|
|
this.props.scatterplotXXaccessor === this.props.gene
|
|
? globals.brightBlue
|
|
: "inherit"
|
|
}}
|
|
>
|
|
X
|
|
</span>
|
|
<span
|
|
onClick={this.handleSetGeneAsScatterplotY(this.props.gene).bind(this)}
|
|
style={{
|
|
fontSize: 16,
|
|
color:
|
|
this.props.scatterplotYYaccessor === this.props.gene
|
|
? "white"
|
|
: globals.brightBlue,
|
|
cursor: "pointer",
|
|
position: "relative",
|
|
top: 1,
|
|
fontWeight: 700,
|
|
marginRight: 4,
|
|
borderRadius: 3,
|
|
padding: "2px 3px",
|
|
backgroundColor:
|
|
this.props.scatterplotYYaccessor === this.props.gene
|
|
? globals.brightBlue
|
|
: "inherit"
|
|
}}
|
|
>
|
|
Y
|
|
</span>
|
|
<span
|
|
onClick={this.handleGeneColorScaleClick(this.props.gene).bind(this)}
|
|
style={{
|
|
fontSize: 16,
|
|
cursor: "pointer",
|
|
position: "relative",
|
|
marginRight: 6,
|
|
borderRadius: 3,
|
|
padding: "0px 2px 2px 2px",
|
|
color:
|
|
this.props.colorAccessor === this.props.gene
|
|
? "white"
|
|
: "inherit",
|
|
backgroundColor:
|
|
this.props.colorAccessor === this.props.gene
|
|
? globals.brightBlue
|
|
: "inherit"
|
|
}}
|
|
>
|
|
<FaPaintBrush style={{ display: "inline-block" }} />
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
***********************************
|
|
***********************************
|
|
DiffExp Heatmap
|
|
***********************************
|
|
***********************************
|
|
**********************************/
|
|
|
|
@connect(state => {
|
|
return {
|
|
differential: state.differential,
|
|
world: state.controls.world
|
|
};
|
|
})
|
|
class Heatmap extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
value: ""
|
|
};
|
|
}
|
|
|
|
getAllGeneNames = memoize(world =>
|
|
_.map(this.props.world.varAnnotations, "name")
|
|
);
|
|
|
|
render() {
|
|
if (!this.props.differential.diffExp)
|
|
return <p>Select cells & compute differential to see heatmap</p>;
|
|
|
|
const topGenesForCellSet1 = this.props.differential.diffExp.data.celllist1;
|
|
const topGenesForCellSet2 = this.props.differential.diffExp.data.celllist2;
|
|
// const allGeneNames = this.getAllGeneNames(this.props.world);
|
|
|
|
const extent = d3.extent(
|
|
_.union(
|
|
topGenesForCellSet1.mean_expression_cellset1,
|
|
topGenesForCellSet1.mean_expression_cellset2,
|
|
topGenesForCellSet2.mean_expression_cellset1,
|
|
topGenesForCellSet2.mean_expression_cellset2
|
|
)
|
|
);
|
|
|
|
const greyColorScale = d3
|
|
.scaleSequential()
|
|
.domain(extent)
|
|
.interpolator(interpolateGreys);
|
|
|
|
return (
|
|
<div>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
justifyContent: "flex-start",
|
|
width: 400,
|
|
fontWeight: 700
|
|
}}
|
|
>
|
|
<p style={{ marginRight: 110 }}>Gene</p>
|
|
<p style={{ marginRight: 25 }}>1</p>
|
|
<p style={{ marginRight: 20 }}>2</p>
|
|
<p>ave diff</p>
|
|
</div>
|
|
{topGenesForCellSet1.topgenes.map((gene, i) => {
|
|
return (
|
|
<HeatmapRow
|
|
key={gene}
|
|
gene={gene}
|
|
greyColorScale={greyColorScale}
|
|
aveDiff={topGenesForCellSet1.ave_diff[i]}
|
|
set1exp={Math.floor(
|
|
topGenesForCellSet1.mean_expression_cellset1[i]
|
|
)}
|
|
set2exp={Math.floor(
|
|
topGenesForCellSet1.mean_expression_cellset2[i]
|
|
)}
|
|
/>
|
|
);
|
|
})}
|
|
{topGenesForCellSet2.topgenes.map((gene, i) => {
|
|
return (
|
|
<HeatmapRow
|
|
key={gene}
|
|
gene={gene}
|
|
greyColorScale={greyColorScale}
|
|
aveDiff={topGenesForCellSet2.ave_diff[i]}
|
|
set1exp={Math.floor(
|
|
topGenesForCellSet2.mean_expression_cellset1[i]
|
|
)}
|
|
set2exp={Math.floor(
|
|
topGenesForCellSet2.mean_expression_cellset2[i]
|
|
)}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default Heatmap;
|