mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-05 04:28:12 +08:00
color by expression, handle new update cell middleware logic
This commit is contained in:
@@ -12,40 +12,130 @@ import actions from "../../actions";
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
class DiffExp extends React.Component {
|
class DiffExp extends React.Component {
|
||||||
|
handleGeneColorScaleClick(gene) {
|
||||||
|
return () => {
|
||||||
|
this.props.dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene))
|
||||||
|
}
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.differential.diffExp) return null
|
if (!this.props.differential.diffExp) return null
|
||||||
|
|
||||||
|
|
||||||
const topGenesForCellSet1 = this.props.differential.diffExp.data.celllist1;
|
const topGenesForCellSet1 = this.props.differential.diffExp.data.celllist1;
|
||||||
const topGenesForCellSet2 = this.props.differential.diffExp.data.celllist2;
|
const topGenesForCellSet2 = this.props.differential.diffExp.data.celllist2;
|
||||||
const rectSide = 10;
|
const rectSide = 10;
|
||||||
const rightMargin = 0;
|
const rightMargin = 0;
|
||||||
|
|
||||||
|
const extent = d3.extent(
|
||||||
|
_.union(
|
||||||
|
topGenesForCellSet1.mean_expression_cellset1,
|
||||||
|
topGenesForCellSet1.mean_expression_cellset2,
|
||||||
|
topGenesForCellSet2.mean_expression_cellset1,
|
||||||
|
topGenesForCellSet2.mean_expression_cellset2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('diffexp', this.props)
|
||||||
|
console.log('extent', extent)
|
||||||
|
|
||||||
|
const greyColorScale = d3.scaleSequential()
|
||||||
|
.domain(extent)
|
||||||
|
.interpolator(d3.interpolateGreys);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p> Top genes expressed by cellset 1 </p>
|
<div style={{
|
||||||
<p> Gene {"............"} Exp in set 1 {"............"} Exp in set 2 </p>
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: 400,
|
||||||
|
fontWeight: 700,
|
||||||
|
}}>
|
||||||
|
<p style={{width: 200}}>Gene</p>
|
||||||
|
<p>Set 1 exp</p>
|
||||||
|
<p>Set 2 exp</p>
|
||||||
|
</div>
|
||||||
{
|
{
|
||||||
topGenesForCellSet1.topgenes.map((gene, i) => {
|
topGenesForCellSet1.topgenes.map((gene, i) => {
|
||||||
return (
|
return (
|
||||||
<p key={gene}>
|
<div key={gene} style={{
|
||||||
{gene} {"...................... "}
|
width: 400,
|
||||||
{Math.floor(topGenesForCellSet1.mean_expression_cellset1[i])} {" ................... "}
|
display: "flex",
|
||||||
{Math.floor(topGenesForCellSet1.mean_expression_cellset2[i])}
|
justifyContent: "space-between",
|
||||||
</p>
|
alignItems: "baseline",
|
||||||
|
}}>
|
||||||
|
<div style={{width: 200}}>
|
||||||
|
<span
|
||||||
|
onClick={this.handleGeneColorScaleClick(gene).bind(this)}
|
||||||
|
style={{
|
||||||
|
fontSize: 24,
|
||||||
|
cursor: "pointer",
|
||||||
|
position: "relative",
|
||||||
|
top: 5,
|
||||||
|
marginRight: 10
|
||||||
|
}}>🎨</span>
|
||||||
|
<span>{gene}</span>
|
||||||
|
</div>
|
||||||
|
<p style={{
|
||||||
|
padding: 20,
|
||||||
|
color: extent[1] === topGenesForCellSet1.mean_expression_cellset1[i] ? "white" : "black",
|
||||||
|
width: 60,
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: greyColorScale(Math.floor(topGenesForCellSet1.mean_expression_cellset1[i])),
|
||||||
|
}}>
|
||||||
|
{Math.floor(topGenesForCellSet1.mean_expression_cellset1[i])}
|
||||||
|
</p>
|
||||||
|
<p style={{
|
||||||
|
padding: 20,
|
||||||
|
color: extent[1] === topGenesForCellSet1.mean_expression_cellset2[i] ? "white" : "black",
|
||||||
|
width: 60,
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: greyColorScale(Math.floor(topGenesForCellSet1.mean_expression_cellset2[i])),
|
||||||
|
}}>
|
||||||
|
{Math.floor(topGenesForCellSet1.mean_expression_cellset2[i])}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
<p> Top genes expressed by cellset 2 </p>
|
|
||||||
<p> Gene {"............"} Exp in set 1 {"............"} Exp in set 2 </p>
|
|
||||||
{
|
{
|
||||||
topGenesForCellSet2.topgenes.map((gene, i) => {
|
topGenesForCellSet2.topgenes.map((gene, i) => {
|
||||||
return (
|
return (
|
||||||
<p key={gene}>
|
<div key={gene} style={{
|
||||||
{gene} {"...................... "}
|
width: 400,
|
||||||
{Math.floor(topGenesForCellSet2.mean_expression_cellset1[i])} {" ................... "}
|
display: "flex",
|
||||||
{Math.floor(topGenesForCellSet2.mean_expression_cellset2[i])}
|
justifyContent: "space-between",
|
||||||
</p>
|
alignItems: "baseline",
|
||||||
|
}}>
|
||||||
|
<div style={{width: 200}}>
|
||||||
|
<span
|
||||||
|
onClick={this.handleGeneColorScaleClick(gene).bind(this)}
|
||||||
|
style={{
|
||||||
|
fontSize: 24,
|
||||||
|
cursor: "pointer",
|
||||||
|
position: "relative",
|
||||||
|
top: 5,
|
||||||
|
marginRight: 10
|
||||||
|
}}>🎨</span>
|
||||||
|
<span>{gene}</span>
|
||||||
|
</div>
|
||||||
|
<p style={{
|
||||||
|
padding: 20,
|
||||||
|
color: extent[1] === topGenesForCellSet2.mean_expression_cellset1[i] ? "white" : "black",
|
||||||
|
width: 60,
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: greyColorScale(Math.floor(topGenesForCellSet2.mean_expression_cellset1[i])),
|
||||||
|
}}>
|
||||||
|
{Math.floor(topGenesForCellSet2.mean_expression_cellset1[i])}
|
||||||
|
</p>
|
||||||
|
<p style={{
|
||||||
|
padding: 20,
|
||||||
|
color: extent[1] === topGenesForCellSet2.mean_expression_cellset2[i] ? "white" : "black",
|
||||||
|
width: 60,
|
||||||
|
margin: 0,
|
||||||
|
backgroundColor: greyColorScale(Math.floor(topGenesForCellSet2.mean_expression_cellset2[i])),
|
||||||
|
}}>
|
||||||
|
{Math.floor(topGenesForCellSet2.mean_expression_cellset2[i])}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -54,6 +144,9 @@ class DiffExp extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <p> Top genes expressed by cellset 2 </p>
|
||||||
|
// <p> Gene {"............"} Exp in set 1 {"............"} Exp in set 2 </p>
|
||||||
|
|
||||||
|
|
||||||
@connect((state) => {
|
@connect((state) => {
|
||||||
return {
|
return {
|
||||||
@@ -120,46 +213,74 @@ class Expression extends React.Component {
|
|||||||
) {
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('expression render sees', this.props.differential)
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginTop: 50}}>
|
<div>
|
||||||
<SectionHeader text="Differential Expression"/>
|
<div style={{
|
||||||
<div style={{marginBottom: 20}}>
|
marginBottom: 20,
|
||||||
<button onClick={this.set1.bind(this)}>
|
width: 500,
|
||||||
|
}}>
|
||||||
|
<button
|
||||||
|
style={{
|
||||||
|
width: 200,
|
||||||
|
fontSize: 14,
|
||||||
|
margin: 5,
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "#5A5F63",
|
||||||
|
padding: "10px 20px",
|
||||||
|
backgroundColor: "#C5D1D8",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: 3,
|
||||||
|
cursor: "pointer",
|
||||||
|
|
||||||
|
}}
|
||||||
|
onClick={this.set1.bind(this)}>
|
||||||
{
|
{
|
||||||
this.props.differential.celllist1 ?
|
this.props.differential.celllist1 ?
|
||||||
this.props.differential.celllist1.length + " cells selected" :
|
this.props.differential.celllist1.length + " cells selected" :
|
||||||
"Store current cell selection as 'set 1'"
|
"Store current cell selection as 'set 1'"
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={this.set2.bind(this)}>
|
<button
|
||||||
|
style={{
|
||||||
|
width: 200,
|
||||||
|
fontSize: 14,
|
||||||
|
margin: 5,
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "#5A5F63",
|
||||||
|
padding: "10px 20px",
|
||||||
|
backgroundColor: "#C5D1D8",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: 3,
|
||||||
|
cursor: "pointer",
|
||||||
|
|
||||||
|
}}
|
||||||
|
onClick={this.set2.bind(this)}>
|
||||||
{
|
{
|
||||||
this.props.differential.celllist2 ?
|
this.props.differential.celllist2 ?
|
||||||
this.props.differential.celllist2.length + " cells selected" :
|
this.props.differential.celllist2.length + " cells selected" :
|
||||||
"Store current cell selection as 'set 2'"
|
"Store current cell selection as 'set 2'"
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={this.computeDiffExp.bind(this)}>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
margin: 5,
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "#41633C",
|
||||||
|
padding: "10px 20px",
|
||||||
|
backgroundColor: "#B9D1B5",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: 3,
|
||||||
|
cursor: "pointer",
|
||||||
|
|
||||||
|
}}
|
||||||
|
onClick={this.computeDiffExp.bind(this)}>
|
||||||
Compute differential expression
|
Compute differential expression
|
||||||
</button>
|
</button>
|
||||||
Gene count:
|
|
||||||
<input placeholder={"Default is 5"}/>
|
|
||||||
</div>
|
</div>
|
||||||
<DiffExp/>
|
<DiffExp/>
|
||||||
<SectionHeader text="Color by expression"/>
|
|
||||||
{
|
|
||||||
_.map(this.props.expression.genes, (gene) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={gene}
|
|
||||||
onClick={this.handleClick(gene).bind(this)}
|
|
||||||
style={{marginRight: 10}}>
|
|
||||||
{gene}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -168,6 +289,25 @@ class Expression extends React.Component {
|
|||||||
export default Expression;
|
export default Expression;
|
||||||
|
|
||||||
|
|
||||||
|
// <SectionHeader text="Differential Expression"/>
|
||||||
|
// <SectionHeader text="Color by expression"/>
|
||||||
|
// {
|
||||||
|
// _.map(this.props.expression.genes, (gene) => {
|
||||||
|
// return (
|
||||||
|
// <button
|
||||||
|
// key={gene}
|
||||||
|
// onClick={this.handleClick(gene).bind(this)}
|
||||||
|
// style={{marginRight: 10}}>
|
||||||
|
// {gene}
|
||||||
|
// </button>
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// Gene count:
|
||||||
|
// <input placeholder={"Default is 5"}/>
|
||||||
|
|
||||||
// <svg>
|
// <svg>
|
||||||
// <g transform="translate(200, 40)">
|
// <g transform="translate(200, 40)">
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -79,10 +79,9 @@ const updateCellSelectionMiddleware = (store) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === "color by expression") {
|
if (action.type === "color by expression") {
|
||||||
/* we assume if they clicked the control we must have loaded something, that could change though. */
|
|
||||||
|
|
||||||
/* this bit could be sped up and could be on the server, but it's drawn once on click, not at 60fps */
|
|
||||||
const indexOfGene = s.expression.data.genes.indexOf(action.gene);
|
const indexOfGene = 0 /* we only get one, this comes from server as needed now */
|
||||||
|
|
||||||
const expressionMap = {}
|
const expressionMap = {}
|
||||||
/*
|
/*
|
||||||
@@ -93,34 +92,33 @@ const updateCellSelectionMiddleware = (store) => {
|
|||||||
cell789: [0, 8]
|
cell789: [0, 8]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
_.each(s.expression.data.cells, (cell) => {
|
_.each(action.data.data.cells, (cell) => { /* this action is coming directly from the server */
|
||||||
expressionMap[cell.cellname] = cell.e
|
expressionMap[cell.cellname] = cell.e
|
||||||
})
|
})
|
||||||
|
|
||||||
const minExpressionCell = _.minBy(s.expression.data.cells, (cell) => {
|
const minExpressionCell = _.minBy(action.data.data.cells, (cell) => {
|
||||||
return cell.e[indexOfGene]
|
return cell.e[indexOfGene]
|
||||||
})
|
})
|
||||||
|
|
||||||
const maxExpressionCell = _.maxBy(s.expression.data.cells, (cell) => {
|
const maxExpressionCell = _.maxBy(action.data.data.cells, (cell) => {
|
||||||
return cell.e[indexOfGene]
|
return cell.e[indexOfGene]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
console.log('middle', action, expressionMap, minExpressionCell)
|
||||||
|
|
||||||
|
|
||||||
colorScale = d3.scaleLinear()
|
colorScale = d3.scaleLinear()
|
||||||
.domain([minExpressionCell.e[indexOfGene], maxExpressionCell.e[indexOfGene]])
|
.domain([minExpressionCell.e[indexOfGene], maxExpressionCell.e[indexOfGene]])
|
||||||
.range([1,0]) /* invert viridis... probably pass this scale through to others */
|
.range([1,0]) /* invert viridis... probably pass this scale through to others */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_.each(currentSelectionWithUpdatedColors, (cell, i) => {
|
_.each(currentSelectionWithUpdatedColors, (cell, i) => {
|
||||||
|
|
||||||
currentSelectionWithUpdatedColors[i]["__color__"] = d3.interpolateViridis(
|
currentSelectionWithUpdatedColors[i]["__color__"] = d3.interpolateViridis(
|
||||||
colorScale(
|
colorScale(
|
||||||
expressionMap[cell.CellName][indexOfGene]
|
expressionMap[cell.CellName][indexOfGene]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user