Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Histogram #4426

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions viz-lib/src/visualizations/chart/Editor/AxisSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { isString, isObject, isFinite, isNumber, merge } from "lodash";
import { isString, isObject, merge } from "lodash";
import React from "react";
import { useDebouncedCallback } from "use-debounce";
import * as Grid from "antd/lib/grid";
import { Section, Select, Input, InputNumber, ContextHelp } from "@/components/visualizations/editor";

function toNumber(value: any) {
value = isNumber(value) ? value : parseFloat(value);
return isFinite(value) ? value : null;
}
import { toNumber } from "../plotly/utils";

type OwnProps = {
id: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { visualizationsSettings } from "@/visualizations/visualizationsSettings"
const allChartTypes = [
{ type: "line", name: "Line", icon: "line-chart" },
{ type: "column", name: "Bar", icon: "bar-chart" },
{ type: "histogram", name: "Histogram", icon: "bar-chart" },
{ type: "area", name: "Area", icon: "area-chart" },
{ type: "pie", name: "Pie", icon: "pie-chart" },
{ type: "scatter", name: "Scatter", icon: "circle-o" },
Expand Down
4 changes: 2 additions & 2 deletions viz-lib/src/visualizations/chart/Editor/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export default function GeneralSettings({ options, data, onOptionsChange }: any)
label="Stacking"
data-test="Chart.Stacking"
defaultValue={options.series.stacking}
disabled={!includes(["line", "area", "column"], options.globalSeriesType)}
disabled={!includes(["line", "area", "column", "histogram"], options.globalSeriesType)}
onChange={(stacking: any) => onOptionsChange({ series: { stacking } })}>
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
<Select.Option value={null} data-test="Chart.Stacking.Disabled">
Expand All @@ -309,7 +309,7 @@ export default function GeneralSettings({ options, data, onOptionsChange }: any)
</Section>
)}

{includes(["line", "area", "column"], options.globalSeriesType) && (
{includes(["line", "area", "column", "histogram"], options.globalSeriesType) && (
// @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message
<Section>
<Checkbox
Expand Down
32 changes: 31 additions & 1 deletion viz-lib/src/visualizations/chart/Editor/XAxisSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { includes } from "lodash";
import React from "react";
import { Section, Switch } from "@/components/visualizations/editor";
import { Section, InputNumber, Switch, Input } from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations/prop-types";
import { toNumber } from "../plotly/utils";

import AxisSettings from "./AxisSettings";

Expand All @@ -15,6 +17,34 @@ export default function XAxisSettings({ options, onOptionsChange }: any) {
onChange={(xAxis: any) => onOptionsChange({ xAxis })}
/>

{includes(["histogram"], options.globalSeriesType) && (
<React.Fragment>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
label="Bin Size"
className="w-100"
placeholder="Auto"
data-test="Chart.XAxis.BinSize"
defaultValue={options.binSize}
onChange={(e: any) => onOptionsChange({ binSize: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<InputNumber
label="Bin Start"
className="w-100"
placeholder="Auto"
data-test="Chart.XAxis.BinStart"
defaultValue={options.binStart}
onChange={(binStart: any) => onOptionsChange({ binStart: toNumber(binStart) })}
/>
</Section>
</React.Fragment>
)}

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
Expand Down
40 changes: 23 additions & 17 deletions viz-lib/src/visualizations/chart/getChartData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNil, isObject, each, forOwn, sortBy, values } from "lodash";
import { isNil, isObject, isEmpty, each, forOwn, sortBy, values } from "lodash";

function addPointToSeries(point: any, seriesCollection: any, seriesName: any) {
if (seriesCollection[seriesName] === undefined) {
Expand Down Expand Up @@ -75,25 +75,31 @@ export default function getChartData(data: any, options: any) {
});

if (isNil(seriesName)) {
each(yValues, (yValue, ySeriesName) => {
if (options.globalSeriesType === "histogram" && isEmpty(yValues)) {
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ x: number; y: never; $raw: any; }' is not ... Remove this comment to see the full error message
point = { x: xValue, y: yValue, $raw: point.$raw };
if (eValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'yError' does not exist on type '{ $raw: ... Remove this comment to see the full error message
point.yError = eValue;
}
point = { x: xValue, y: null, $raw: point.$raw };
addPointToSeries(point, series, "Count");
} else {
each(yValues, (yValue, ySeriesName) => {
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ x: number; y: never; $raw: any; }' is not ... Remove this comment to see the full error message
point = { x: xValue, y: yValue, $raw: point.$raw };
if (eValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'yError' does not exist on type '{ $raw: ... Remove this comment to see the full error message
point.yError = eValue;
}

if (sizeValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'size' does not exist on type '{ $raw: an... Remove this comment to see the full error message
point.size = sizeValue;
}
if (sizeValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'zVal' does not exist on type '{ $raw: an... Remove this comment to see the full error message
point.size = sizeValue;
}

if (zValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'zVal' does not exist on type '{ $raw: an... Remove this comment to see the full error message
point.zVal = zValue;
}
addPointToSeries(point, series, ySeriesName);
});
if (zValue !== null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'zVal' does not exist on type '{ $raw: an... Remove this comment to see the full error message
point.zVal = zValue;
}
addPointToSeries(point, series, ySeriesName);
});
}
} else {
addPointToSeries(point, series, seriesName);
}
Expand Down
41 changes: 41 additions & 0 deletions viz-lib/src/visualizations/chart/plotly/prepareDefaultData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@ function prepareBoxSeries(series: any, options: any, { seriesColor }: any) {
return series;
}

function prepareHistogramSeries(series: any, options: any) {
function estimateBinCount(xValues: any) {
const start = xValues[0];
const end = xValues.slice(-1)[0];
if (!isNaN(start)) { // number axis
return (end - start) / options.binSize;
} else if (!isNaN(new Date(start).getTime())) { // date axis
return (Date.parse(end) - Date.parse(start)) / options.binSize;
} else { // category axis
return xValues.length / options.binSize;
}
}

series.type = 'histogram';
series.hoverinfo = 'x+y+name';

if (!isNil(options.binSize) && (options.binSize !== "")) {
// avoid hanging by limiting the number of bins
const binCount = estimateBinCount(series.x);
if (binCount > 10000) {
// throwing error results in visualization page to crash after leaving Editor
// so instead draw an empty plot
series.x = [];
series.y = [];
}

series.autobinx = false;
series.xbins = series.xbins || {};
series.xbins.size = options.binSize;
}
if (!isNil(options.binStart)) {
series.autobinx = false;
series.xbins = series.xbins || {};
series.xbins.start = options.binStart;
}
return series;
}

function prepareSeries(series: any, options: any, additionalOptions: any) {
const { hoverInfoPattern, index } = additionalOptions;

Expand Down Expand Up @@ -148,6 +186,9 @@ function prepareSeries(series: any, options: any, additionalOptions: any) {
return prepareBubbleSeries(plotlySeries, options, additionalOptions);
case "box":
return prepareBoxSeries(plotlySeries, options, additionalOptions);
case 'histogram':
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 3.
return prepareHistogramSeries(plotlySeries, options, additionalOptions);
default:
return plotlySeries;
}
Expand Down
50 changes: 28 additions & 22 deletions viz-lib/src/visualizations/chart/plotly/updateData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,37 @@ function updateSeriesText(seriesList: any, options: any) {

function updatePercentValues(seriesList: any, options: any) {
if (options.series.percentValues) {
// Some series may not have corresponding x-values;
// do calculations for each x only for series that do have that x
const sumOfCorrespondingPoints = new Map();
each(seriesList, series => {
series.sourceData.forEach((item: any) => {
const sum = sumOfCorrespondingPoints.get(item.x) || 0;
sumOfCorrespondingPoints.set(item.x, sum + Math.abs(item.y || 0.0));
if (options.globalSeriesType === "histogram") {
each(seriesList, (series) => {
series.histnorm = "probability";
});
});

each(seriesList, series => {
const yValues: any = [];

series.sourceData.forEach((item: any) => {
if (isNil(item.y) && !options.missingValuesAsZero) {
item.yPercent = null;
} else {
const sum = sumOfCorrespondingPoints.get(item.x);
item.yPercent = (item.y / sum);
}
yValues.push(item.yPercent);
} else {
// Some series may not have corresponding x-values;
// do calculations for each x only for series that do have that x
const sumOfCorrespondingPoints = new Map();
each(seriesList, series => {
series.sourceData.forEach((item: any) => {
const sum = sumOfCorrespondingPoints.get(item.x) || 0;
sumOfCorrespondingPoints.set(item.x, sum + Math.abs(item.y || 0.0));
});
});

series.y = yValues;
});
each(seriesList, series => {
const yValues: any = [];

series.sourceData.forEach((item: any) => {
if (isNil(item.y) && !options.missingValuesAsZero) {
item.yPercent = null;
} else {
const sum = sumOfCorrespondingPoints.get(item.x);
item.yPercent = (item.y / sum);
}
yValues.push(item.yPercent);
});

series.y = yValues;
});
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion viz-lib/src/visualizations/chart/plotly/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isUndefined } from "lodash";
import { isUndefined, isNumber, isFinite } from "lodash";
import moment from "moment";
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'plot... Remove this comment to see the full error message
import plotlyCleanNumber from "plotly.js/src/lib/clean_number";
Expand All @@ -7,6 +7,11 @@ export function cleanNumber(value: any) {
return isUndefined(value) ? value : plotlyCleanNumber(value);
}

export function toNumber(value: any) {
value = isNumber(value) ? value : parseFloat(value);
return isFinite(value) ? value : null;
}

export function getSeriesAxis(series: any, options: any) {
const seriesOptions = options.seriesOptions[series.name] || { type: options.globalSeriesType };
if (seriesOptions.yAxis === 1 && (!options.series.stacking || seriesOptions.type === "line")) {
Expand Down