From f0e9b1ab91390764f81e75476cdfb675611e929d Mon Sep 17 00:00:00 2001 From: Colin Megill Date: Sun, 2 May 2021 03:20:28 -0700 Subject: [PATCH] dotplot proto full --- client/src/components/dotplot/column.js | 216 +++++++++++++++++ client/src/components/dotplot/dot.js | 54 +++++ client/src/components/dotplot/index.js | 296 ++++-------------------- 3 files changed, 320 insertions(+), 246 deletions(-) create mode 100644 client/src/components/dotplot/column.js create mode 100644 client/src/components/dotplot/dot.js diff --git a/client/src/components/dotplot/column.js b/client/src/components/dotplot/column.js new file mode 100644 index 00000000..51daa3a0 --- /dev/null +++ b/client/src/components/dotplot/column.js @@ -0,0 +1,216 @@ +import React from "react"; +import { connect, shallowEqual } from "react-redux"; + +import memoize from "memoize-one"; + +import Async from "react-async"; +import ErrorLoading from "./err"; +import StillLoading from "./load"; +import Dot from "./dot"; + +import { createCategorySummaryFromDfCol } from "../../util/stateManager/controlsHelpers"; + +import { + createColorTable, + createColorQuery, +} from "../../util/stateManager/colorHelpers"; + +@connect((state) => ({ + annoMatrix: state.annoMatrix, + colors: state.colors, + genesets: state.genesets.genesets, + pointDilation: state.pointDilation, + differential: state.differential, +})) +class Column extends React.Component { + static watchAsync(props, prevProps) { + return !shallowEqual(props.watchProps, prevProps.watchProps); + } + + createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol); + + fetchAsyncProps = async (props) => { + const { annoMatrix, colors, _geneSymbol, _geneIndex } = props.watchProps; + + const [categoryData, categorySummary, colorData] = await this.fetchData( + annoMatrix, + "tissue", + colors, + _geneSymbol, + _geneIndex + ); + + return { + categoryData, + categorySummary, + colorData, + }; + }; + + async fetchData(annoMatrix, metadataField, colors, _geneSymbol) { + /* + fetch our data and the color-by data if appropriate, and then build a summary + of our category and a color table for the color-by annotation. + */ + const { schema } = annoMatrix; + const { colorMode } = colors; + const { genesets, differential } = this.props; + let colorDataPromise = Promise.resolve(null); + if (_geneSymbol) { + const query = createColorQuery( + colorMode, + _geneSymbol, + schema, + genesets, + differential.diffExp + ); + if (query) colorDataPromise = annoMatrix.fetch(...query); + } + const [categoryData, colorData] = await Promise.all([ + annoMatrix.fetch("obs", metadataField), + colorDataPromise, + ]); + + // our data + const column = categoryData.icol(0); + const colSchema = schema.annotations.obsByName[metadataField]; + + const categorySummary = this.createCategorySummaryFromDfCol( + column, + colSchema + ); + + return [categoryData, categorySummary, colorData]; + } + + updateColorTable(colors, colorDf) { + const { annoMatrix } = this.props; + const { schema } = annoMatrix; + + /* update color table state */ + if (!colors || !colorDf) { + return createColorTable( + null, // default mode + null, + null, + schema, + null + ); + } + + const { colorAccessor, userColors, colorMode } = colors; + return createColorTable( + colorMode, + colorAccessor /* TODO(colinmegill) #632 dotplot wiring */, + colorDf, + schema, + userColors + ); + } + + createColorByQuery(colors) { + const { annoMatrix, genesets, differential } = this.props; + const { schema } = annoMatrix; + const { colorMode, colorAccessor } = colors; + + return createColorQuery( + colorMode, + colorAccessor /* TODO(colinmegill) #632 dotplot wiring */, + schema, + genesets, + differential.diffExp + ); + } + + render() { + const { + annoMatrix, + pointDilation, + colors, + viewport, + _geneSymbol, + _geneIndex, + rowColumnSize, + } = this.props; + + return ( + + + + + + + {(error) => ( + + )} + + + {(asyncProps) => { + const { + categoryData, + width, + height, + categorySummary, + colorData, + } = asyncProps; + + if (!_geneSymbol || !colorData) return null; + + /* TODO(colinmegill) #632 wire to dotplot */ + + const metadataField = "tissue"; + + const groupBy = categoryData.col(metadataField); + const col = colorData.icol(0); + const range = col.summarize(); + + const histogramMap = col.histogram( + 100, + [range.min, range.max], + groupBy + ); + + return categorySummary.categoryValues.map( + (val, _categoryValueIndex) => { + return ( + + ); + } + ); + }} + + + + ); + } +} + +export default Column; diff --git a/client/src/components/dotplot/dot.js b/client/src/components/dotplot/dot.js new file mode 100644 index 00000000..795cd469 --- /dev/null +++ b/client/src/components/dotplot/dot.js @@ -0,0 +1,54 @@ +import React from "react"; + +const Dot = (props) => { + const { + categoryValue, + _categoryValueIndex, + histogramMap, + _geneSymbol, + _geneIndex, + rowColumnSize, + } = props; + + const bins = histogramMap.has(categoryValue) + ? histogramMap.get(categoryValue) + : new Array(100).fill(0); + + const zeros = bins.shift(); /* MUTATES, REMOVES FIRST ELEMENT */ + const rest = bins.reduce( + (acc, current) => acc + current + ); /* SUM REMAINING ELEMENTS */ + + /* TODO(colinmegill) #632 colorscale */ + + return ( + + {_geneIndex === 0 && ( + + {categoryValue} + + )} + 20 ? 20 : (rest / zeros) * 10} + cx="11" + cy="-3.5" + style={{ + fill: "steelblue", + fillOpacity: 0.3, + stroke: "none", + }} + /> + + ); +}; + +export default Dot; diff --git a/client/src/components/dotplot/index.js b/client/src/components/dotplot/index.js index 487b422d..ffa58bba 100644 --- a/client/src/components/dotplot/index.js +++ b/client/src/components/dotplot/index.js @@ -1,40 +1,20 @@ import React from "react"; -import { connect, shallowEqual } from "react-redux"; -// import _regl from "regl"; -import Async from "react-async"; -import memoize from "memoize-one"; -// import * as d3 from "d3"; +import { connect } from "react-redux"; -import ErrorLoading from "./err"; -import StillLoading from "./load"; - -import { createCategorySummaryFromDfCol } from "../../util/stateManager/controlsHelpers"; - -import { - createColorTable, - createColorQuery, -} from "../../util/stateManager/colorHelpers"; +import Column from "./column"; @connect((state) => ({ - annoMatrix: state.annoMatrix, layoutChoice: state.layoutChoice, - colors: state.colors, genesets: state.genesets.genesets, - differential: state.differential, - pointDilation: state.pointDilation, + colors: state.colors, })) class Dotplot extends React.Component { - static watchAsync(props, prevProps) { - return !shallowEqual(props.watchProps, prevProps.watchProps); - } - - createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol); - constructor(props) { super(props); const viewport = this.getViewportDimensions(); - this.dotplotTopPadding = 150; - this.dotplotLeftPadding = 200; + this.dotplotTopPadding = 120; + this.dotplotLeftPadding = 170; + this.rowColumnSize = 15; this.state = { viewport, @@ -49,8 +29,6 @@ class Dotplot extends React.Component { window.removeEventListener("resize", this.handleResize); } - // handleResize = () => {}; - getViewportDimensions = () => { const { viewportRef } = this.props; return { @@ -59,183 +37,16 @@ class Dotplot extends React.Component { }; }; - createRow = ( - metadataField, - categoryData, - colorAccessor, - colorData, - categoryValue, - width, - height, - index, - histogramMap - ) => { - const bins = histogramMap.has(categoryValue) - ? histogramMap.get(categoryValue) - : new Array(100).fill(0); - - const zeros = bins.shift(); /* MUTATES, REMOVES FIRST ELEMENT */ - const rest = bins.reduce( - (acc, current) => acc + current - ); /* SUM REMAINING ELEMENTS */ - - return ( - - {categoryValue} - - - ); - }; - - dotplot = ( - metadataField, - categoryData, - categorySummary, - colorAccessor = "tissue", - colorData, - width, - height - ) => { - if (!colorAccessor || !colorData) return null; - - const groupBy = categoryData.col(metadataField); - const col = colorData.icol(0); - const range = col.summarize(); - - const histogramMap = col.histogram(100, [range.min, range.max], groupBy); - - return categorySummary.categoryValues.map((val, index) => { - return this.createRow( - metadataField, - categoryData, - colorAccessor, - colorData, - val, - width, - height, - index, - histogramMap - ); - }); - }; - - dotplotColumns = () => { - const { genesets } = this.props; - /* TODO(colinmegill) #632 wire to dotplot reducer */ - if (genesets && genesets.get("bladder urothelial")) { - const _geneset = genesets.get("bladder urothelial"); - _geneset.genes.forEach((a, b) => { - console.log("bladder u", b); - }); - } - }; - - fetchAsyncProps = async (props) => { - const { viewport, annoMatrix, colors } = props.watchProps; - - const [categoryData, categorySummary, colorData] = await this.fetchData( - annoMatrix, - "tissue", - colors - ); - - const { width, height } = viewport; - return { - width, - height, - categoryData, - categorySummary, - colorData, - }; - }; - - async fetchData(annoMatrix, metadataField, colors) { - /* - fetch our data and the color-by data if appropriate, and then build a summary - of our category and a color table for the color-by annotation. - */ - const { schema } = annoMatrix; - const { colorAccessor, colorMode } = colors; - const { genesets, differential } = this.props; - let colorDataPromise = Promise.resolve(null); - if (colorAccessor) { - const query = createColorQuery( - colorMode, - colorAccessor, - schema, - genesets, - differential.diffExp - ); - if (query) colorDataPromise = annoMatrix.fetch(...query); - } - const [categoryData, colorData] = await Promise.all([ - annoMatrix.fetch("obs", metadataField), - colorDataPromise, - ]); - - // our data - const column = categoryData.icol(0); - const colSchema = schema.annotations.obsByName[metadataField]; - - const categorySummary = this.createCategorySummaryFromDfCol( - column, - colSchema - ); - return [categoryData, categorySummary, colorData]; - } - - updateColorTable(colors, colorDf) { - const { annoMatrix } = this.props; - const { schema } = annoMatrix; - - /* update color table state */ - if (!colors || !colorDf) { - return createColorTable( - null, // default mode - null, - null, - schema, - null - ); - } - - const { colorAccessor, userColors, colorMode } = colors; - return createColorTable( - colorMode, - colorAccessor, - colorDf, - schema, - userColors - ); - } - - createColorByQuery(colors) { - const { annoMatrix, genesets, differential } = this.props; - const { schema } = annoMatrix; - const { colorMode, colorAccessor } = colors; - - return createColorQuery( - colorMode, - colorAccessor, - schema, - genesets, - differential.diffExp - ); - } - render() { const { viewport } = this.state; - const { annoMatrix, colors, pointDilation } = this.props; + const { genesets } = this.props; + + const _TESTgeneset = genesets.get("bladder urothelial"); + + /* TODO(colinmegill) #632 componetize */ + if (!_TESTgeneset) return null; + + const _genes = Array.from(_TESTgeneset.genes.keys()); return (
- {this.dotplotColumns()} - - - - - - {(error) => ( - - )} - - - {(asyncProps) => { - const { - colorAccessor, - categoryData, - width, - height, - categorySummary, - colorData, - } = asyncProps; - return this.dotplot( - "tissue", - categoryData, - categorySummary, - colorAccessor, - colorData, - width, - height + {/* Acaa1b, Mal, Foxq1 ... across the top of the dotplot */} + + {_genes.map((_geneSymbol, _geneIndexInGeneset) => { + return ( + + {_geneSymbol} + ); - }} - - + })} + + {/* loop over genes in the geneset, */} + + {_genes.map((_geneSymbol, _geneIndexInGeneset) => { + return ( + + ); + })} + +
);