Files
cellxgene/client/src/components/expression/diffExpHeatmap.js
Bruce Martin d4e850d18f #130 redux refactor (#208)
* 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
2018-09-17 20:43:39 -07:00

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;