Skip to content

Commit

Permalink
support yAgg (getredash#4)
Browse files Browse the repository at this point in the history
* support yAgg

* support stuff

* key

* ts

* ts
  • Loading branch information
stevenhao authored Feb 3, 2022
1 parent e5de607 commit 868115e
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export default function DefaultColorsSettings({ options, data, onOptionsChange }
},
];

// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean[]' is not assignable to type 'object... Remove this comment to see the full error message
return <Table showHeader={false} dataSource={series} columns={columns} pagination={false} />;
}

Expand Down
21 changes: 20 additions & 1 deletion viz-lib/src/visualizations/chart/Editor/GeneralSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isArray, map, mapValues, includes, some, each, difference, toNumber } from "lodash";
import { isArray, map, mapValues, includes, some, each, difference, toNumber, values } from "lodash";
import React, { useMemo } from "react";
import { Section, Select, Checkbox, InputNumber } from "@/components/visualizations/editor";
import { UpdateOptionsStrategy } from "@/components/visualizations/editor/createTabbedEditor";
import { EditorPropTypes } from "@/visualizations/prop-types";

import ChartTypeSelect from "./ChartTypeSelect";
import ColumnMappingSelect from "./ColumnMappingSelect";
import { AggregationFunctionName, DefaultAggregationFunctionName } from "../getChartData";

function getAvailableColumnMappingTypes(options: any) {
const result = ["x", "y"];
Expand Down Expand Up @@ -164,6 +165,24 @@ export default function GeneralSettings({ options, data, onOptionsChange }: any)
/>
))}

{!includes(["custom", "heatmap", "bubble", "scatter"], 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>
<Select
label="Aggregation of points with same x"
defaultValue={DefaultAggregationFunctionName}
onChange={(value: any) => onOptionsChange({ yAgg: value })}>
{values(AggregationFunctionName).map(val => (
// @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 key={val} value={val}>
{val}
{/* @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>
))}
</Select>
</Section>
)}

{includes(["bubble"], 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 */}
Expand Down
2 changes: 0 additions & 2 deletions viz-lib/src/visualizations/chart/Editor/SeriesSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ export default function SeriesSettings({ options, data, onOptionsChange }: any)
({ oldIndex, newIndex }) => {
const seriesOptions = [...series];
seriesOptions.splice(newIndex, 0, ...seriesOptions.splice(oldIndex, 1));
// @ts-expect-error ts-migrate(2339) FIXME: Property 'key' does not exist on type 'Boolean'.
onOptionsChange({ seriesOptions: fromPairs(map(seriesOptions, ({ key }, zIndex) => [key, { zIndex }])) });
},
[onOptionsChange, series]
Expand Down Expand Up @@ -134,7 +133,6 @@ export default function SeriesSettings({ options, data, onOptionsChange }: any)
}}>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
<Table
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean[]' is not assignable to type 'object... Remove this comment to see the full error message
dataSource={series}
columns={columns}
components={{
Expand Down
95 changes: 85 additions & 10 deletions viz-lib/src/visualizations/chart/getChartData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,64 @@
import { isNil, isObject, each, forOwn, sortBy, values } from "lodash";
import { sort } from "d3";
import { isNil, isObject, each, forOwn, sortBy, values, groupBy, min, max } from "lodash";

function addPointToSeries(point: any, seriesCollection: any, seriesName: any) {
interface Series {
name: string;
type: "column";
data: { $raw: any; x: string | number; y: string | number }[];
}
type SeriesCollection = Partial<Record<string, Series>>;
type AggregationFunction = (yvals: (number | string)[]) => number | string;
export enum AggregationFunctionName {
FIRST = "FIRST",
SUM = "SUM",
MEAN = "MEAN",
MEDIAN = "MEDIAN",
MAX = "MAX",
MIN = "MIN",
COUNT = "COUNT",
P25 = "P25",
P75 = "P75",
P05 = "P05",
P95 = "P95",
}
export const DefaultAggregationFunctionName = AggregationFunctionName.FIRST;

const safeSum = (yvals: (number | string)[]): number | string => {
let result: number | string = 0;
for (const val of yvals) {
if (typeof result === "number") {
if (typeof val === "number") {
result += val;
} else if (result === 0) {
result = val;
}
}
}
return result;
};
const safeDiv = (val: number | string, denom: number) => {
if (typeof val === "number") return val / denom;
return val;
};
const percentile = (vals: (number | string)[], percentile: number): number | string => {
vals = sortBy([...vals]);
return vals[Math.floor(vals.length * percentile)];
};
const AGGREGATION_FUNCTIONS: Record<AggregationFunctionName, AggregationFunction> = {
FIRST: yvals => yvals[0],
MEAN: yvals => safeDiv(safeSum(yvals), yvals.length),
COUNT: yvals => yvals.length,
MEDIAN: yvals => percentile(yvals, 0.5),
SUM: yvals => safeSum(yvals),
MIN: yvals => min(yvals)!,
MAX: yvals => max(yvals)!,
P25: yvals => percentile(yvals, 0.25),
P75: yvals => percentile(yvals, 0.75),
P05: yvals => percentile(yvals, 0.05),
P95: yvals => percentile(yvals, 0.95),
};

function addPointToSeries(point: any, seriesCollection: SeriesCollection, seriesName: string) {
if (seriesCollection[seriesName] === undefined) {
seriesCollection[seriesName] = {
name: seriesName,
Expand All @@ -9,14 +67,18 @@ function addPointToSeries(point: any, seriesCollection: any, seriesName: any) {
};
}

seriesCollection[seriesName].data.push(point);
seriesCollection[seriesName]!.data.push(point);
}

export default function getChartData(data: any, options: any) {
const series = {};
const _window: any = window;
_window.getChartData = getChartData;

const mappings = options.columnMapping;
_window.data = data;
_window.options = options;
const series: SeriesCollection = {};

const mappings = options.columnMapping;
each(data, row => {
let point = { $raw: row };
let seriesName = null;
Expand Down Expand Up @@ -98,10 +160,23 @@ export default function getChartData(data: any, options: any) {
addPointToSeries(point, series, seriesName);
}
});
return sortBy(values(series), ({ name }) => {
if (isObject(options.seriesOptions[name])) {
return options.seriesOptions[name].zIndex || 0;

const aggregationFunction: AggregationFunction =
AGGREGATION_FUNCTIONS[options.yAgg as AggregationFunctionName] ??
AGGREGATION_FUNCTIONS[DefaultAggregationFunctionName]!;
return sortBy(
values(series).map(series => {
const data = values(groupBy(series!.data, point => point.x)).map(points => ({
...points[0],
y: aggregationFunction(points.map(p => p.y)),
}));
return { ...series, data };
}),
({ name }) => {
if (isObject(options.seriesOptions[name])) {
return options.seriesOptions[name].zIndex || 0;
}
return 0;
}
return 0;
});
);
}

0 comments on commit 868115e

Please sign in to comment.