From 825f5e19b2a0dacb2b046c93542993de51f53914 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Wed, 4 Mar 2020 15:06:27 +0300 Subject: [PATCH 1/2] X-log --- src/components/charts/DistPlusPlot.re | 17 +++-- src/components/charts/DistPlusPlotReducer.re | 27 ++++--- .../DistributionPlot/DistributionPlot.re | 12 ++- .../charts/DistributionPlot/distPlotD3.js | 74 ++++++++++++++----- .../charts/DistributionPlot/distPlotReact.js | 4 +- 5 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/components/charts/DistPlusPlot.re b/src/components/charts/DistPlusPlot.re index 7712a7c7..00b2cee9 100644 --- a/src/components/charts/DistPlusPlot.re +++ b/src/components/charts/DistPlusPlot.re @@ -156,7 +156,8 @@ module DistPlusChart = { let (yMaxDiscreteDomainFactor, yMaxContinuousDomainFactor) = adjustBoth(toDiscreteProbabilityMass); Distributions.Continuous.T.maxX; let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; {
+
; -}; \ No newline at end of file +}; diff --git a/src/components/charts/DistPlusPlotReducer.re b/src/components/charts/DistPlusPlotReducer.re index c3088a28..ad937a89 100644 --- a/src/components/charts/DistPlusPlotReducer.re +++ b/src/components/charts/DistPlusPlotReducer.re @@ -1,5 +1,6 @@ type chartConfig = { - log: bool, + xLog: bool, + yLog: bool, isCumulative: bool, height: int, }; @@ -15,7 +16,8 @@ type action = | CHANGE_SHOW_PARAMS | REMOVE_DIST(int) | ADD_DIST - | CHANGE_LOG(int) + | CHANGE_X_LOG(int) + | CHANGE_Y_LOG(int) | CHANGE_IS_CUMULATIVE(int, bool) | HEIGHT_INCREMENT(int) | HEIGHT_DECREMENT(int); @@ -42,7 +44,6 @@ let heightToPix = | _ => 140; let distributionReducer = (index, state: list(chartConfig), action) => { - Js.log3(index, action, state); switch (action, E.L.get(state, index)) { | (HEIGHT_INCREMENT(_), Some(dist)) => E.L.update( @@ -58,18 +59,24 @@ let distributionReducer = (index, state: list(chartConfig), action) => { ) | (CHANGE_IS_CUMULATIVE(_, isCumulative), Some(dist)) => E.L.update({...dist, isCumulative}, index, state) - | (CHANGE_LOG(_), Some(dist)) => - E.L.update({...dist, log: !dist.log}, index, state) + | (CHANGE_X_LOG(_), Some(dist)) => + E.L.update({...dist, xLog: !dist.xLog}, index, state) + | (CHANGE_Y_LOG(_), Some(dist)) => + E.L.update({...dist, yLog: !dist.yLog}, index, state) | (REMOVE_DIST(_), Some(_)) => E.L.remove(index, 1, state) | (ADD_DIST, Some(_)) => - E.L.append(state, [{log: false, isCumulative: false, height: 2}]) + E.L.append( + state, + [{yLog: false, xLog: false, isCumulative: false, height: 2}], + ) | _ => state }; }; let reducer = (state: state, action: action) => switch (action) { - | CHANGE_LOG(i) + | CHANGE_X_LOG(i) + | CHANGE_Y_LOG(i) | CHANGE_IS_CUMULATIVE(i, _) | HEIGHT_DECREMENT(i) | REMOVE_DIST(i) @@ -89,7 +96,7 @@ let init = { showStats: false, showParams: false, distributions: [ - {log: false, isCumulative: false, height: 2}, - {log: false, isCumulative: true, height: 1}, + {yLog: false, xLog: false, isCumulative: false, height: 2}, + {yLog: false, xLog: false, isCumulative: true, height: 1}, ], -}; \ No newline at end of file +}; diff --git a/src/components/charts/DistributionPlot/DistributionPlot.re b/src/components/charts/DistributionPlot/DistributionPlot.re index 4c031a42..aa30e8a5 100644 --- a/src/components/charts/DistributionPlot/DistributionPlot.re +++ b/src/components/charts/DistributionPlot/DistributionPlot.re @@ -29,7 +29,8 @@ module RawPlot = { ~onHover=(f: float) => (), ~continuous=?, ~discrete=?, - ~scale=?, + ~xScale=?, + ~yScale=?, ~showDistributionLines=?, ~showDistributionYAxis=?, ~showVerticalLine=?, @@ -51,7 +52,8 @@ module RawPlot = { ~onHover, ~continuous?, ~discrete?, - ~scale?, + ~xScale?, + ~yScale?, ~showDistributionLines?, ~showDistributionYAxis?, ~showVerticalLine?, @@ -116,7 +118,8 @@ let make = ~yMaxContinuousDomainFactor=?, ~onHover: float => unit=_ => (), ~continuous=?, - ~scale=?, + ~xScale=?, + ~yScale=?, ~showDistributionLines=false, ~showDistributionYAxis=false, ~showVerticalLine=false, @@ -128,7 +131,8 @@ let make = ?minX ?yMaxDiscreteDomainFactor ?yMaxContinuousDomainFactor - ?scale + ?xScale + ?yScale ?timeScale discrete={discrete |> E.O.fmap(XYShape.toJs)} height diff --git a/src/components/charts/DistributionPlot/distPlotD3.js b/src/components/charts/DistributionPlot/distPlotD3.js index eddd0748..46959a31 100644 --- a/src/components/charts/DistributionPlot/distPlotD3.js +++ b/src/components/charts/DistributionPlot/distPlotD3.js @@ -32,11 +32,20 @@ export class DistPlotD3 { xScaleLogBase: 10, // Y + minY: null, + maxY: null, + yScaleType: 'linear', + yScaleTimeOptions: null, + yScaleLogBase: 10, + + xMinContinuousDomainFactor: 1, + xMaxContinuousDomainFactor: 1, yMaxContinuousDomainFactor: 1, yMaxDiscreteDomainFactor: 1, - showDistributionYAxis: false, + showDistributionYAxis: false, showDistributionLines: true, + areaColors: ['#E1E5EC', '#E1E5EC'], verticalLine: 110, showVerticalLine: true, @@ -102,11 +111,18 @@ export class DistPlotD3 { if (!['log', 'linear'].includes(this.attrs.xScaleType)) { throw new Error('X-scale type should be either "log" or "linear".'); } + if (!['log', 'linear'].includes(this.attrs.yScaleType)) { + throw new Error('Y-scale type should be either "log" or "linear".'); + } // Log Scale. if (this.attrs.xScaleType === 'log') { - this.logFilter('continuous'); - this.logFilter('discrete'); + this.logFilter('continuous', (x, y) => x > 0); + this.logFilter('discrete', (x, y) => x > 0); + } + if (this.attrs.yScaleType === 'log') { + this.logFilter('continuous', (x, y) => y > 0); + this.logFilter('discrete', (x, y) => y > 0); } if ( this.attrs.xScaleType === 'log' @@ -116,6 +132,14 @@ export class DistPlotD3 { console.warn('minX should be positive.'); this.attrs.minX = undefined; } + if ( + this.attrs.yScaleType === 'log' + && this.attrs.minY !== null + && this.attrs.minY < 0 + ) { + console.warn('minY should be positive.'); + this.attrs.minY = undefined; + } // Fields. const fields = [ @@ -124,7 +148,7 @@ export class DistPlotD3 { 'svgWidth', 'svgHeight', 'yMaxContinuousDomainFactor', 'yMaxDiscreteDomainFactor', - 'xScaleLogBase', + 'xScaleLogBase', 'yScaleLogBase', ]; for (const field of fields) { if (!_.isNumber(this.attrs[field])) { @@ -212,10 +236,14 @@ export class DistPlotD3 { if (!_.isFinite(yMax)) throw new Error('yMax is undefined'); // X-domains. + const xMinDomainFactor = _.get(this.attrs, 'xMinContinuousDomainFactor', 1); + const xMaxDomainFactor = _.get(this.attrs, 'xMaxContinuousDomainFactor', 1); + const yMinDomainFactor = _.get(this.attrs, 'yMinContinuousDomainFactor', 1); const yMaxDomainFactor = _.get(this.attrs, 'yMaxContinuousDomainFactor', 1); - const xMinDomain = xMin; - const xMaxDomain = xMax; - const yMinDomain = yMin; + + const xMinDomain = xMin * xMinDomainFactor; + const xMaxDomain = xMax * xMaxDomainFactor; + const yMinDomain = yMin * yMinDomainFactor; const yMaxDomain = yMax * yMaxDomainFactor; // X-scale. @@ -229,9 +257,14 @@ export class DistPlotD3 { .range([0, this.calc.chartWidth]); // Y-scale. - const yScale = d3.scaleLinear() - .domain([yMinDomain, yMaxDomain]) - .range([this.calc.chartHeight, 0]); + const yScale = this.attrs.yScaleType === 'linear' + ? d3.scaleLinear() + .domain([yMinDomain, yMaxDomain]) + .range([this.calc.chartHeight, 0]) + : d3.scaleLog() + .base(this.attrs.yScaleLogBase) + .domain([yMinDomain, yMaxDomain]) + .range([this.calc.chartHeight, 0]); return { xMin, xMax, @@ -394,7 +427,7 @@ export class DistPlotD3 { addLollipopsChart(common) { const data = this.getDataPoints('discrete'); - const _yMin = d3.min(this.attrs.data.discrete.ys); + const yMin = d3.min(this.attrs.data.discrete.ys); const yMax = d3.max(this.attrs.data.discrete.ys); // X axis. @@ -404,15 +437,22 @@ export class DistPlotD3 { .call(d3.axisBottom(common.xScale)); // Y-domain. + const yMinDomainFactor = _.get(this.attrs, 'yMinDiscreteDomainFactor', 1); const yMaxDomainFactor = _.get(this.attrs, 'yMaxDiscreteDomainFactor', 1); - const yMinDomain = 0; + const yMinDomain = 0 * yMinDomainFactor; const yMaxDomain = yMax * yMaxDomainFactor; // Y-scale. - const yScale = d3.scaleLinear() - .domain([yMinDomain, yMaxDomain]) - .range([this.calc.chartHeight, 0]); + const yScale = this.attrs.yScaleType === 'linear' + ? d3.scaleLinear() + .domain([yMinDomain, yMaxDomain]) + .range([this.calc.chartHeight, 0]) + : d3.scaleLog() + .base(this.attrs.yScaleLogBase) + .domain([yMinDomain, yMaxDomain]) + .range([this.calc.chartHeight, 0]); + // const yTicks = Math.floor(this.calc.chartHeight / 20); const yAxis = d3.axisLeft(yScale).ticks(yTicks); @@ -554,7 +594,7 @@ export class DistPlotD3 { * @param {string} key * @returns {{x: number[], y: number[]}} */ - logFilter(key) { + logFilter(key, pred) { const xs = []; const ys = []; const emptyShape = { xs: [], ys: [] }; @@ -563,7 +603,7 @@ export class DistPlotD3 { for (let i = 0, len = data.xs.length; i < len; i++) { const x = data.xs[i]; const y = data.ys[i]; - if (x > 0) { + if (pred(x, y)) { xs.push(x); ys.push(y); } diff --git a/src/components/charts/DistributionPlot/distPlotReact.js b/src/components/charts/DistributionPlot/distPlotReact.js index 7cf0e34b..5e2ff81b 100644 --- a/src/components/charts/DistributionPlot/distPlotReact.js +++ b/src/components/charts/DistributionPlot/distPlotReact.js @@ -21,7 +21,6 @@ function getRandomInt(min, max) { function DistPlotReact(props) { const containerRef = React.createRef(); const key = "cdf-chart-react-" + getRandomInt(0, 1000); - const scale = props.scale || 'linear'; const style = !!props.width ? { width: props.width + "px" } : {}; const [sized, { width }] = useSize(() => { @@ -49,7 +48,8 @@ function DistPlotReact(props) { .set('verticalLine', props.verticalLine || 110) .set('showVerticalLine', props.showVerticalLine) .set('container', containerRef.current) - .set('xScaleType', scale) + .set('xScaleType', props.xScale || 'linear') + .set('yScaleType', props.yScale || 'linear') .set('xScaleTimeOptions', props.timeScale) .set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor || 1) .set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor || 1) From 28fbbc2f2082d89614ad2f29d065b124a4443922 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Wed, 4 Mar 2020 16:44:09 +0300 Subject: [PATCH 2/2] Weird --- src/components/charts/DistributionPlot/distPlotD3.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/charts/DistributionPlot/distPlotD3.js b/src/components/charts/DistributionPlot/distPlotD3.js index 46959a31..3bb13fb2 100644 --- a/src/components/charts/DistributionPlot/distPlotD3.js +++ b/src/components/charts/DistributionPlot/distPlotD3.js @@ -74,7 +74,7 @@ export class DistPlotD3 { /** * @param {string} name * @param value - * @returns {CdfChartD3} + * @returns {DistPlotD3} */ set(name, value) { _.set(this.attrs, [name], value); @@ -83,7 +83,7 @@ export class DistPlotD3 { /** * @param data - * @returns {CdfChartD3} + * @returns {DistPlotD3} */ data(data) { const continuousXs = _.get(data, 'continuous.xs', []); @@ -439,7 +439,7 @@ export class DistPlotD3 { // Y-domain. const yMinDomainFactor = _.get(this.attrs, 'yMinDiscreteDomainFactor', 1); const yMaxDomainFactor = _.get(this.attrs, 'yMaxDiscreteDomainFactor', 1); - const yMinDomain = 0 * yMinDomainFactor; + const yMinDomain = yMin * yMinDomainFactor; const yMaxDomain = yMax * yMaxDomainFactor; // Y-scale. @@ -497,7 +497,7 @@ export class DistPlotD3 { .attr('x1', d => common.xScale(d.x)) .attr('x2', d => common.xScale(d.x)) .attr('y1', d => yScale(d.y)) - .attr('y2', yScale(0)); + .attr('y2', yScale(yMin)); // Define the div for the tooltip const tooltip = this._container.append('div') @@ -592,6 +592,7 @@ export class DistPlotD3 { /** * @param {string} key + * @param {function} pred * @returns {{x: number[], y: number[]}} */ logFilter(key, pred) {