From 896fe854dc3865214325cfceea94824ff41a1b6c Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:35:18 -0300 Subject: [PATCH] feat: Adds the ECharts Histogram chart (#28652) --- .../src/operators/histogramOperator.ts | 40 ++++ .../src/operators/index.ts | 1 + .../src/shared-controls/index.ts | 1 + .../src/utils/columnChoices.ts | 52 +++-- .../src/utils/index.ts | 2 +- .../src/utils/selectOptions.ts | 8 +- .../test/operators/histogramOperator.test.ts | 47 +++++ .../test/utils/columnChoices.test.tsx | 40 +++- .../src/query/types/PostProcessing.ts | 39 ++++ .../test/query/types/PostProcessing.test.ts | 64 ++++++ .../src/index.js | 2 +- .../src/Histogram/Histogram.tsx | 61 ++++++ .../src/Histogram/buildQuery.ts | 32 +++ .../src/Histogram/controlPanel.tsx | 141 +++++++++++++ .../src/Histogram/images/example1.png | Bin 0 -> 58782 bytes .../src/Histogram/images/example2.png | Bin 0 -> 54583 bytes .../src/Histogram/images/thumbnail.png | Bin 0 -> 44371 bytes .../src/Histogram/index.ts | 66 ++++++ .../src/Histogram/transformProps.ts | 188 ++++++++++++++++++ .../src/Histogram/types.ts | 42 ++++ .../plugin-chart-echarts/src/controls.tsx | 2 +- .../plugins/plugin-chart-echarts/src/index.ts | 2 + .../components/ExploreViewContainer/index.jsx | 26 ++- .../ColumnSelectPopover.tsx | 20 +- .../ColumnSelectPopoverTrigger.tsx | 3 + .../DndColumnSelect.tsx | 3 + .../src/visualizations/presets/MainPreset.js | 2 + .../utils/pandas_postprocessing/__init__.py | 2 + .../utils/pandas_postprocessing/histogram.py | 87 ++++++++ .../pandas_postprocessing/test_histogram.py | 122 ++++++++++++ 30 files changed, 1061 insertions(+), 34 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/operators/histogramOperator.ts create mode 100644 superset-frontend/packages/superset-ui-chart-controls/test/operators/histogramOperator.test.ts create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/Histogram.tsx create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/buildQuery.ts create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/controlPanel.tsx create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example1.png create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example2.png create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/thumbnail.png create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/index.ts create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/transformProps.ts create mode 100644 superset-frontend/plugins/plugin-chart-echarts/src/Histogram/types.ts create mode 100644 superset/utils/pandas_postprocessing/histogram.py create mode 100644 tests/unit_tests/pandas_postprocessing/test_histogram.py diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/histogramOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/histogramOperator.ts new file mode 100644 index 000000000..38b8ecf10 --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/histogramOperator.ts @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitationsxw + * under the License. + */ +import { PostProcessingHistogram, getColumnLabel } from '@superset-ui/core'; +import { PostProcessingFactory } from './types'; + +/* eslint-disable @typescript-eslint/no-unused-vars */ +export const histogramOperator: PostProcessingFactory< + PostProcessingHistogram +> = (formData, queryObject) => { + const { bins, column, cumulative, groupby, normalize } = formData; + const parsedBins = Number.isNaN(Number(bins)) ? 5 : Number(bins); + const parsedColumn = getColumnLabel(column); + const parsedGroupBy = groupby!.map(getColumnLabel); + return { + operation: 'histogram', + options: { + column: parsedColumn, + groupby: parsedGroupBy, + bins: parsedBins, + cumulative, + normalize, + }, + }; +}; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts index c7151dafd..cac7088a7 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/index.ts @@ -21,6 +21,7 @@ export { rollingWindowOperator } from './rollingWindowOperator'; export { timeCompareOperator } from './timeCompareOperator'; export { timeComparePivotOperator } from './timeComparePivotOperator'; export { sortOperator } from './sortOperator'; +export { histogramOperator } from './histogramOperator'; export { pivotOperator } from './pivotOperator'; export { resampleOperator } from './resampleOperator'; export { renameOperator } from './renameOperator'; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.ts index ec769a871..8ff85439b 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.ts @@ -22,3 +22,4 @@ export { default as sharedControlComponents } from './components'; export * from './components'; export * from './customControls'; export * from './mixins'; +export * from './dndControls'; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts index f05615175..fe2c0fa92 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts @@ -16,26 +16,48 @@ * specific language governing permissions and limitations * under the License. */ -import { QueryResponse } from '@superset-ui/core'; -import { Dataset, isDataset, isQueryResponse } from '../types'; +import { GenericDataType, QueryColumn, QueryResponse } from '@superset-ui/core'; +import { ColumnMeta, Dataset, isDataset, isQueryResponse } from '../types'; + +export function columnsByType( + datasource?: Dataset | QueryResponse | null, + type?: GenericDataType, +): (ColumnMeta | QueryColumn)[] { + if (isDataset(datasource) || isQueryResponse(datasource)) { + const columns = datasource.columns as (ColumnMeta | QueryColumn)[]; + const filteredColumns = columns.filter( + col => type === undefined || col.type_generic === type, + ); + return filteredColumns.sort( + (col1: ColumnMeta | QueryColumn, col2: ColumnMeta | QueryColumn) => { + const opt1Name = + 'verbose_name' in col1 + ? col1.verbose_name || col1.column_name + : col1.column_name; + const opt2Name = + 'verbose_name' in col2 + ? col2.verbose_name || col2.column_name + : col2.column_name; + return opt1Name.toLowerCase() > opt2Name.toLowerCase() ? 1 : -1; + }, + ); + } + return []; +} /** * Convert Datasource columns to column choices */ export default function columnChoices( datasource?: Dataset | QueryResponse | null, + type?: GenericDataType, ): [string, string][] { - if (isDataset(datasource) || isQueryResponse(datasource)) { - return datasource.columns - .map((col): [string, string] => [ - col.column_name, - 'verbose_name' in col - ? col.verbose_name || col.column_name - : col.column_name, - ]) - .sort((opt1, opt2) => - opt1[1].toLowerCase() > opt2[1].toLowerCase() ? 1 : -1, - ); - } - return []; + return columnsByType(datasource, type).map( + (col: ColumnMeta | QueryColumn): [string, string] => [ + col.column_name, + 'verbose_name' in col + ? col.verbose_name || col.column_name + : col.column_name, + ], + ); } diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts index fb829ea05..77e883caf 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts @@ -22,7 +22,7 @@ export * from './D3Formatting'; export * from './expandControlConfig'; export * from './getColorFormatters'; export { default as mainMetric } from './mainMetric'; -export { default as columnChoices } from './columnChoices'; +export { default as columnChoices, columnsByType } from './columnChoices'; export * from './defineSavedMetrics'; export * from './getStandardizedControls'; export * from './getTemporalColumns'; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/selectOptions.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/selectOptions.ts index 666ab35f4..9872439f1 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/selectOptions.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/selectOptions.ts @@ -35,9 +35,13 @@ export function formatSelectOptions( * >> formatSelectOptionsForRange(1, 5) * >> [[1,'1'], [2,'2'], [3,'3'], [4,'4'], [5,'5']] */ -export function formatSelectOptionsForRange(start: number, end: number) { +export function formatSelectOptionsForRange( + start: number, + end: number, + step = 1, +): Formatted[] { const options: Formatted[] = []; - for (let i = start; i <= end; i += 1) { + for (let i = start; i <= end; i += step) { options.push([i, i.toString()]); } return options; diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/operators/histogramOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/operators/histogramOperator.test.ts new file mode 100644 index 000000000..46be335fd --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/test/operators/histogramOperator.test.ts @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { histogramOperator } from '@superset-ui/chart-controls'; +import { SqlaFormData } from '@superset-ui/core'; +import { omit } from 'lodash'; + +const formData: SqlaFormData = { + bins: 5, + column: 'quantity', + cumulative: true, + normalize: true, + groupby: ['country', 'region'], + viz_type: 'histogram', + datasource: 'foo', +}; + +test('matches formData', () => { + expect(histogramOperator(formData, {})).toEqual({ + operation: 'histogram', + options: omit(formData, ['viz_type', 'datasource']), + }); +}); + +test('defaults to 5 bins', () => { + expect( + histogramOperator(omit(formData, ['bins']) as SqlaFormData, {}), + ).toEqual({ + operation: 'histogram', + options: omit(formData, ['viz_type', 'datasource']), + }); +}); diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx index 2fac0e60e..f27365e95 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx @@ -74,6 +74,44 @@ describe('columnChoices()', () => { ['Column 2', 'Column 2'], ['Column 3', 'Column 3'], ]); - expect.anything(); + }); + + it('should return choices of a specific type', () => { + expect(columnChoices(testQueryResponse, GenericDataType.Temporal)).toEqual([ + ['Column 2', 'Column 2'], + ]); + }); + it('should use name when verbose_name key exists but is not defined', () => { + expect( + columnChoices({ + id: 1, + metrics: [], + type: DatasourceType.Table, + main_dttm_col: 'test', + time_grain_sqla: [], + columns: [ + { + column_name: 'foo', + verbose_name: null, + type: 'VARCHAR', + type_generic: GenericDataType.String, + }, + { + column_name: 'bar', + verbose_name: null, + type: 'VARCHAR', + type_generic: GenericDataType.String, + }, + ], + verbose_map: {}, + column_formats: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' }, + currency_formats: {}, + datasource_name: 'my_datasource', + description: 'this is my datasource', + }), + ).toEqual([ + ['bar', 'bar'], + ['foo', 'foo'], + ]); }); }); diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts index 3b4094133..a70d0111f 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts @@ -233,6 +233,20 @@ interface _PostProcessingRank { } export type PostProcessingRank = _PostProcessingRank | DefaultPostProcessing; +interface _PostProcessingHistogram { + operation: 'histogram'; + options?: { + column: string; + groupby: string[]; + bins: number; + cumulative?: boolean; + normalize?: boolean; + }; +} +export type PostProcessingHistogram = + | _PostProcessingHistogram + | DefaultPostProcessing; + /** * Parameters for chart data postprocessing. * See superset/utils/pandas_processing.py. @@ -251,6 +265,7 @@ export type PostProcessingRule = | PostProcessingResample | PostProcessingRename | PostProcessingFlatten + | PostProcessingHistogram | PostProcessingRank; export function isPostProcessingAggregation( @@ -318,3 +333,27 @@ export function isPostProcessingResample( ): rule is PostProcessingResample { return rule?.operation === 'resample'; } + +export function isPostProcessingRename( + rule?: PostProcessingRule, +): rule is PostProcessingRename { + return rule?.operation === 'rename'; +} + +export function isPostProcessingFlatten( + rule?: PostProcessingRule, +): rule is PostProcessingFlatten { + return rule?.operation === 'flatten'; +} + +export function isPostProcessingRank( + rule?: PostProcessingRule, +): rule is PostProcessingRank { + return rule?.operation === 'rank'; +} + +export function isPostProcessingHistogram( + rule?: PostProcessingRule, +): rule is PostProcessingHistogram { + return rule?.operation === 'histogram'; +} diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts index 047699fa5..05c385fb4 100644 --- a/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts @@ -41,6 +41,14 @@ import { PostProcessingResample, PostProcessingRolling, PostProcessingSort, + PostProcessingHistogram, + isPostProcessingHistogram, + PostProcessingRename, + PostProcessingFlatten, + PostProcessingRank, + isPostProcessingRename, + isPostProcessingFlatten, + isPostProcessingRank, } from '@superset-ui/core'; import { ComparisonType, RollingType, TimeGranularity } from '../../../src'; @@ -151,6 +159,38 @@ const SORT_RULE: PostProcessingSort = { }, }; +const HISTOGRAM_RULE: PostProcessingHistogram = { + operation: 'histogram', + options: { + column: 'foo', + groupby: ['bar'], + bins: 5, + normalize: true, + cumulative: true, + }, +}; + +const RENAME_RULE: PostProcessingRename = { + operation: 'rename', + options: { + columns: { + foo: 'bar', + }, + }, +}; + +const FLATTEN_RULE: PostProcessingFlatten = { + operation: 'flatten', +}; + +const RANK_RULE: PostProcessingRank = { + operation: 'rank', + options: { + metric: 'foo', + group_by: 'bar', + }, +}; + test('PostProcessingAggregation type guard', () => { expect(isPostProcessingAggregation(AGGREGATE_RULE)).toEqual(true); expect(isPostProcessingAggregation(BOXPLOT_RULE)).toEqual(false); @@ -216,3 +256,27 @@ test('PostProcessingSort type guard', () => { expect(isPostProcessingSort(AGGREGATE_RULE)).toEqual(false); expect(isPostProcessingSort(undefined)).toEqual(false); }); + +test('PostProcessingHistogram type guard', () => { + expect(isPostProcessingHistogram(HISTOGRAM_RULE)).toEqual(true); + expect(isPostProcessingHistogram(AGGREGATE_RULE)).toEqual(false); + expect(isPostProcessingHistogram(undefined)).toEqual(false); +}); + +test('PostProcessingRename type guard', () => { + expect(isPostProcessingRename(RENAME_RULE)).toEqual(true); + expect(isPostProcessingRename(AGGREGATE_RULE)).toEqual(false); + expect(isPostProcessingRename(undefined)).toEqual(false); +}); + +test('PostProcessingFlatten type guard', () => { + expect(isPostProcessingFlatten(FLATTEN_RULE)).toEqual(true); + expect(isPostProcessingFlatten(AGGREGATE_RULE)).toEqual(false); + expect(isPostProcessingFlatten(undefined)).toEqual(false); +}); + +test('PostProcessingRank type guard', () => { + expect(isPostProcessingRank(RANK_RULE)).toEqual(true); + expect(isPostProcessingRank(AGGREGATE_RULE)).toEqual(false); + expect(isPostProcessingRank(undefined)).toEqual(false); +}); diff --git a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/index.js b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/index.js index e14ab372c..5704f5833 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/index.js +++ b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/index.js @@ -34,7 +34,7 @@ const metadata = new ChartMetadata({ { url: example2 }, { url: example3 }, ], - name: t('Histogram'), + name: t('Histogram (legacy)'), tags: [t('Comparison'), t('Legacy'), t('Pattern'), t('Range')], thumbnail, useLegacyApi: true, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/Histogram.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/Histogram.tsx new file mode 100644 index 000000000..08641b27c --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/Histogram.tsx @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { HistogramTransformedProps } from './types'; +import Echart from '../components/Echart'; +import { EventHandlers } from '../types'; + +export default function Histogram(props: HistogramTransformedProps) { + const { + height, + width, + echartOptions, + onFocusedSeries, + onLegendStateChanged, + refs, + } = props; + + const eventHandlers: EventHandlers = { + legendselectchanged: payload => { + onLegendStateChanged?.(payload.selected); + }, + legendselectall: payload => { + onLegendStateChanged?.(payload.selected); + }, + legendinverseselect: payload => { + onLegendStateChanged?.(payload.selected); + }, + mouseout: () => { + onFocusedSeries(undefined); + }, + mouseover: params => { + onFocusedSeries(params.seriesIndex); + }, + }; + + return ( + + ); +} diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/buildQuery.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/buildQuery.ts new file mode 100644 index 000000000..351660ba6 --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/buildQuery.ts @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { buildQueryContext } from '@superset-ui/core'; +import { histogramOperator } from '@superset-ui/chart-controls'; +import { HistogramFormData } from './types'; + +export default function buildQuery(formData: HistogramFormData) { + const { column, groupby = [] } = formData; + return buildQueryContext(formData, baseQueryObject => [ + { + ...baseQueryObject, + columns: [...groupby, column], + post_processing: [histogramOperator(formData, baseQueryObject)], + }, + ]); +} diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/controlPanel.tsx new file mode 100644 index 000000000..59a7de282 --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/controlPanel.tsx @@ -0,0 +1,141 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + GenericDataType, + t, + validateInteger, + validateNonEmpty, +} from '@superset-ui/core'; +import { + ControlPanelConfig, + formatSelectOptionsForRange, + dndGroupByControl, + columnsByType, +} from '@superset-ui/chart-controls'; +import { showLegendControl, showValueControl } from '../controls'; + +const config: ControlPanelConfig = { + controlPanelSections: [ + { + label: t('Query'), + expanded: true, + controlSetRows: [ + [ + { + name: 'column', + config: { + ...dndGroupByControl, + label: t('Column'), + multi: false, + description: t('Numeric column used to calculate the histogram.'), + validators: [validateNonEmpty], + freeForm: false, + disabledTabs: new Set(['saved', 'sqlExpression']), + mapStateToProps: ({ datasource }) => ({ + options: columnsByType(datasource, GenericDataType.Numeric), + }), + }, + }, + ], + ['groupby'], + ['adhoc_filters'], + ['row_limit'], + [ + { + name: 'bins', + config: { + type: 'SelectControl', + freeForm: true, + label: t('Bins'), + default: 5, + choices: formatSelectOptionsForRange(5, 20, 5), + description: t('The number of bins for the histogram'), + validators: [validateInteger], + }, + }, + ], + [ + { + name: 'normalize', + config: { + type: 'CheckboxControl', + label: t('Normalize'), + description: t(` + The normalize option transforms the histogram values into proportions or + probabilities by dividing each bin's count by the total count of data points. + This normalization process ensures that the resulting values sum up to 1, + enabling a relative comparison of the data's distribution and providing a + clearer understanding of the proportion of data points within each bin.`), + default: false, + }, + }, + ], + [ + { + name: 'cumulative', + config: { + type: 'CheckboxControl', + label: t('Cumulative'), + description: t(` + The cumulative option allows you to see how your data accumulates over different + values. When enabled, the histogram bars represent the running total of frequencies + up to each bin. This helps you understand how likely it is to encounter values + below a certain point. Keep in mind that enabling cumulative doesn't change your + original data, it just changes the way the histogram is displayed.`), + default: false, + }, + }, + ], + ], + }, + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [ + ['color_scheme'], + [showValueControl], + [showLegendControl], + [ + { + name: 'x_axis_title', + config: { + type: 'TextControl', + label: t('X Axis Title'), + renderTrigger: true, + default: '', + }, + }, + ], + [ + { + name: 'y_axis_title', + config: { + type: 'TextControl', + label: t('Y Axis Title'), + renderTrigger: true, + default: '', + }, + }, + ], + ], + }, + ], +}; + +export default config; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example1.png b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example1.png new file mode 100644 index 0000000000000000000000000000000000000000..45955dd05a61645298ee037e03adbd4d9970f154 GIT binary patch literal 58782 zcmeFZcUV)~5;sgW6b+yhr6UT6G^KZtCeo$XfOJ9tA)yMP2o|JR5s)su6KW`-h@kY| zn<6EYAiaifqvzav&OP`0@B8C>pZDf@lI-m4HEZ^)ncvJ>Yi2*w)mFPe!AL z14?%^p2F|Ahr)^OlBb1FCq~7RkX?VQpj*Njd9#|{K)EDc!_dR$fCYh?QU0?INT zo|Okp5npQ?IjWDk18{Ycj@^&qJLQE>k?Y7F3xf7d=9fHl??CKY@2`V*ytCEXKJ`Cr zAs~K3Kod(4)zU?KD=h4U@G5E-x7h$v(BCZ@Gbloyo?@O8#iV>8C7@81NRIoWrL<3* zE?9l_Aw5A(*i(p)-#e%06Es=RU!J2`PyF`j3Tx80eP`9M<@X!9^vwnY8`r>UAIJ!v zMei?U(97)i?p-{3eAS3X64meqE`NjaJ8_tUX=7q^%EGm0 zI-mHwgiT9Po!@=;)km_<9rk#JHb(Myy>11k*au}fj`BqgC}mb${KU7w=k>`8as39( zedNUy{;rfnT2|4HvC#HL({4@jt;9$h^^*tcIpxTP-ig4XqBuCtIeuo%xba<4*FB28 zm+MKwBl3{;4;}GW_}8M^_BK95K8yVvSvHwjp`6>)?;Dg?HJB35!eyBlE)(ko4a;n|t^q9_6pUEaO((_a3r;mJ6muhStNtw5crs!0am6Q=$ zV;j0=%|FNt%!jhY{(LREAil zo@XX_q9`hHPCMAa$uOk_z*iE2IzLN}^)o^_dZup8MPjbPYF zv-w>8MyW4{92F$AxHY9^rb)g{hkBm;kVNecb%44P`-aHKGx)8>osy`%G~Gh z5O~V=J54U8*s1o?dA?10&kF|^56BNP_*8~1-tl+5w!CRBFmWwFZTBs+qBbP@MwNkp zagp-+$4UMgJ-^Vp5E+eRPK;`U1-|@I@o_bgO4EIgyBu0Js5^ztqTx0wAJRk8v#;vh zg8VG`cD+@R;(5A;jb8Pe3vW2zbiCo;CsVy1XRafK+gPV$PGiVgvbtApJlKCH)q2 zrH`d*-(P*s;nEt=5;pb9UXbjf;eY-v@|^-Sd)Tj#0e)TTb@DyI%&LL5@}Y{r^6TSI z%B3r~;pY)o;hS(qIP1zq#1%9-S{g9{Z`&^4CfYVy9$J>|YD6f1f|v zM5f%Je7m9(n_$~>ps`P>W%#&8%_KB2d;a|_Rx}=RmR=- z#q~Is8E^SlkoS=I8GYm>WGNC#z&y)T!a1v#8k8zjB1uM48s<$R3l=-yO@0=z|H z>y+G2^_#B5IJV)U*uIxl*VYL7xz#1r(KHBS-k-`eNv28INc2fQRQq=K74*J(e@^gq zo=bJwyXtbW#~jyrGI{N9i}F@*Tu7mh^ES)j(WI%&hGrGt#a)blPN95qCHhM3?a!Zr zzJ1l?@ltotpr^a?!tLIRYw~x`m06Z`pyUN&EP_14n`v1TKqI^|b z279}kx08R)NZ6RojJWU_WT4W>2ZkR{Lrkyaja(YZE;!}yxmqlNn~KP}P>kEV7<*m@;&;j>$cO-&6-(@zq3&-2~`+fq5Q z1$kfFyxZ3EA_dB8cr%h0V?>^Rn6H#S0bPZDR_9R~dN!6H}BVlo?s9WCU4>*qJ?iL%Nkap{6nP%39!-Kx(4mah0_ zoYZ@k#bPOltXJ31@#U2lD_!ENZX#3V^<(>qYwl4=-bq?jtL_z(-qlm%6(uW^EF;nk zIRD9d&zL}*s*kmgv5vA11ov$kFK0EgaWZ&R%w{M5-nV+4W<9qz&sC@4d8-;w{qXPV={lTA- z*sZN+KOi2Edl*I}>||kYtD9_6{mBm~n2gbLAw!J(bhpHzgmw#yZS zX9xyJ@RxczCkGyY6l5P{eZD$ON)XNIQUd-GHJba9XYxYh@bLYlq`>UO?Hf&3A$%a& ztR;N33Zzlq&LQHo2!}KidOLLPcmPi+1Sb2mR7Ujxh+UuCo2WZzX%X-O$7BQ~gp35F zz!4$vmLp{P*Rd)g4*~J7^F#y$Pn-!z{;ZRncVj=J zYxBSz0hhgc^{k_Ry?*m)@9X^Up4`0txGmrY1<(Ewyd@wc_%GT(Q<<}~Qo7E*_O7PN z&M-h`Kp%2q;vzD?uK&kB|L*ZGElvL2@|KXW#9y2K<*)y2YUpL}spJj=`h?5+WqSy{&b_y)A>>_w;teDM7K zK0AJ3zH3rfMnIrIpsuWF;7hnV1*)SRI>G-GtltAaCwri%CqAuh(Y$#;NVr@dcPfWE9TWDMs`uHOt`QFKk^4@G zt1(~bZ0NCx_gm6nz)NKRaXA@# zta0!xyWj<#zmqw;tcfu2?-b4mZc3RdH_Et8 zN+ik!qG1!2{HP*p$t`P|aQ_{wy;b36V+b*9w8X}U3Dgn){5c<=S$X@k{|djLh)K1F z&BB!E$!a{EJzbgB;r6m^%M+?Siv$tpS36kj$vqEUS@G%cze_9jdTQhBTVtzI9l3)~ zl;`2IdBjIb9}H>PwbU6G!lXW zzf+51v=6}5`~0|>*XlS@WC_)g`T3ZE(MdTxlql-ysBMg_X2!Mcnu!YN;)yH3c~q4$ zVxN_He^=5F$7H|H$^O_IxL{LI zA>I@h%_8j&Dr?Z)IA3#W!XY2#xN0~|SQbi=G&N!xROmAkxL2XCBU=$W6n>b{BY!%{ z8nkbdC<=YIcL2J;IL}g!s~E9Ld$jviY}lu-CyGhjLDQl8!}8EnF+~1oxT@iY<+R?I zKW{+vZ0c>6WTn5`QjUQFDBrd%y25!N%MaAW{lO!24ReYg&e6-D0Cl}B`k0`Dwe|xP zWVe}8aP+l~-15*!eSlBx{K#90K zucX8&tvhYM6a_RJ?Oz~M{6Y?KI1nozHfkDX-XpRfVlhWnd7nzT9h7 zx15AkE5n7u6;Na8qg7O)V^6XXCg|ku4TIA}_l9K_p8SGC5a;l6!N-TsN^T%roq@~PN8=E2MVf&{cn^Q6h^VHJY z1gBRC>ETd!EqZm(iPmrK3ECCLfeATPUz7~!$jc6gIh-)nxlKvcV)OFrR!*2ioUE&+ z{qw7|DnfSKIb%jO1ncKdCXn5di=(}1O3=b0tQ$s?)@2eHvuQGta8R73cRZ|W%3G!I z)odz{fYpm%78rcVq|Q6Ta<`w>yjn_>@Kr$dyvF!4YfT`i)^5K0I;QYPbx73|{>{nJ z*w4y}$4XhQKBQ#)W+D~PVyT|P?e%vjBAAhfAyf=KWf&bl-XZR);ar{j7^}b|f80wk z^kT16#ng}TQq18{v1?N(Y1Ybt=y@wpW@gEf(Gz2fhvCc7=MwnzM)sc6JGq4ep^|l-nRZ23-VrzI?d5fQ|!$~VywKUhGxj+lxw0XT_?-F z;DzvR=B1@@K~&xglewn%iZkWOg|$Yzx0CZp2`vXsyQOiyJ{tS2f2x?0I$ZG6Z6`AVE`)$+Li4(l$ zX2Ynu571bd{U33V@tqNfd!B83EXVGeJ5t)wDZP)%z3ykk-ed@f)g&qJPGC%P7@)ml ziIH~{+u|LQO*an7!4z5F!hgFF2|~++jn@oZaod^XZcwi{sVb)jRcx{nd$0XiPnI%b zG^GR4h40*Mw%-5HxQCP(&O*v$SBt;x&8L$%!8&G-$+aBP)fp!2SV)hViRg`YOB>7f z+yD6bR?IQltKZHxueOqYC|iBXtir`UuLH9E-n$%;SoNU0^8i;&DGxb0h8^t{jNDH& zi~Fo)_=aT=A%3*r_Qdc}N#o35L&8f)75Ia+Ptl0l&Pip5K&>yRg0zV1U0mSt&$j80 z!RRRn%Aui4Em6YiL)d@In_ z%>HL_-l;b!$Mf7-m<-I(6s~SU65Go7!{f(WW3$EXr23Qtqm3pI+h_`Yn;X}7k{&5g zHsv!TJEIZle=LYRh!kJNxX(1~wy@!a+?~>Km<~bAWPnwi=9}g+yDt}yfb+BAtgW3s z3Ld%?g>7QLB9KAJ5H-2u=JnHAlvLGJ(}g0(oz-!rBl8(Be;AdqncQkgOI_0aq*lrG z>hHXP{5nRoCgr~2Ne`zh5K^xF+)~(*Iq$xMHiKi2x5wT9#wL1~RV>Xy_9RANmQYDRnb~$xVnfz3IStP-n++#L{QyFucfIu=NL^2D@&GInQv% zjT$XmDFN$}wHwWp^#|u%ptZ=86-dDA^`wCHn&~Th4?^qZPY-&sFkWWkm9Q03qPLdc zJeD)#{F|CYM=bO?Bzo;HQh-HL6dP35@}&8f#9lI%rb=FRLGzU8VVr>=gdt%G3_hSI z1tsVxWZa^)e$#yQQWS09)0YC4Xy1v&nE4#nBQh}Wz?|f7gWV*X+#Kc$aP~grb4iXH zeD*}Nrzt0yg>u>`iKTyxPbN9oOF~YkvaQF1{2dKjNx0GEfOY3_E996v z=A>>`p-T9s-N(nL4e0HyD)(u(n_>k;8i^ll&Gk8LR4uLf88ds(i@r_9to>xfmU~4U zI9dPg5@N7Kqx4g=djE=%J*&VSL!4yNml47l)_~QYZxNqPf*m$SM346-y>u2d(cNzH zfE(IHp6(%4E+`2upm;p=sA&`Y}qN;yn;NFl4=86wSML z@vkllLbv~YKYsTsHznp!nI&Kz6tGv(Cv6n8E584d3>?bX`gSHoq$6mv14F-;b@9BR^r>a$&y3&My;4r5R440Qy{l0&W62=W(89UfooajN~y>J)l zS=d(!5V>c>7*#}ocoH`p*2b*Azr=aq{6LQMVM2*+oJo$1>!^@>bnv4F(F@?gWJb}C z=Ay}-UvG%{Z!bMEG(4zgyW=`p-M7Lfy;`D_iTlF9ywqsP@s>!hYamr+LJ|mbaj$ZR z3(b-C^@(Dz&4x>K!AlokK8kF)YmKi{qsESl-u ziuu9xFg3}Yzx8~JK-TnqU)TW&XhxkB6mIEV;k{;to7DrkVH8%&dhAe5>or9hD$zUm#W>ce|t-aH&yTbRb+iJP<#d=|3n7-vmLj98K zx2+y|+?u8f3%b%06|gc}na9$!il93T=no_Anl0pL9$y}g_H2MgRiFygvE{Y}!VPAg zQ4+mPDMjYPBoY4gTtdu3*1^v0um;pa#^FNYWUQc+(;)x62xl$Vn+`;7GvGf~w9e(B z^6+-4G4l9ogZp9hFj%)6zh^>%Ja2 zs~FgT_(2J_OY7j;c27_(U)rP!1Uc1~2a!@H_{@e<3Y1KFPjs2r%Y{v6EQ<7H;HSP? zSp^-~N<~eaqGJd!%EPf@f2!XBDIqq#_H#MES>U z1_LWA268!?rG(2)@kfpyD}*zM)mTcFJ0rPH`vVZMJ_B0fe#Hab7%*Q2an@qFK%p@x zn=zt5SlGYbn?5WO+$kbm72$O_@f~9C;J@&J-9Y*F|O?qBt-mJn}J~=BQwisEvU5keeUS~!#D|_WT1)Qif<*Ujr9T|AR?F2`x z0ty-KVs~RHSn|z|JLqVwu`^v?0g(F&_XYn3IT--t!a*P@>Flr@EH75v&NC9GvUnsN z{N#@BhqZ|0DM)LX;Q^ue*@Sj~UP{nOn(B8VNb=KbOr3kro$Q;wOrRjGtH%8BvCn5y zN36)p_~a2?v!tee!bFS{x1scml+XZS9(Z0v#wde>_U_q203{QCzfJzooqWv^FzL1T zdHirLtC3x7=cC=bmsn(==~Y|Z(mm2beNC_4S$m{b4n4LT_cMk_02ZLnsevBvj2Euo z%Zn1#uReT-G}3FyN2I3jVfm7}udZmWzqle?N9>){GyqdYEkfEwCBGdkrr@G3>pEqs zGou)yEObj!zl?61R!jo3?<~1Y;Rz}BK`=&Db&1}KLXy_S>CIy>qv*FgC#Ik%DTRUa z2Zyy8G$IEPo{sKHw=mqdBzi+?@c5fKuL>KuX)ikO`*DE6Y^t&&Ufd+nMfTn$BXvk9 zJq2cDJL8sV`Js{4!I@&woDv%t)Q*qvm1>eHxx5uMX(uVv!5aIO(B9!I==&}=hW;HJTel!+QmiY zBZM!zhxbw6q1>8jjpW=PW?9OepB8}^%J%^ze+QpXOM2LML@Tir<2_z}sZc?2k9Y+9 zigiRvlt|IB8J=ar35E;hsq{EM(E~Z7Clx`j@Rd&4-rXN0r$0U#e>T!xii{Ww@FF|a z@b?h1Yo{K7jf&7Db-%yS*w>VZ$+;lXtQ`0anEgEQnO#k>r#c5pAU_5I^lh+Nxz{RW zF@JAmlQi6bBDQ>>Fjz5C-aS-+U$^9AlG_e{y_)m{MOt}Ak?tH|@tfjbY9A}CoS!yK z<7x^qDo7h=^e@8a9YXwm(BT@@dzQCWQorKjC_ck)#M~h{k^PEremrgb`uek`kUC(B zuNt#-o1r57^d!9_alWAABHZ*DcoxMVFVXwmrhIY!%j`(zxx7Or}J$IWN8oNXA2G!#o(`_H^t@lf)Wnp_k8&VL(%=T1zW3^P( zC8Ld;Sfx0#_O#eDyH!kAZC!TUP1iG|K>f%RGKg#t-u4e{TdutuXMPZQN0qdU;<{*) zD@WuZMpX^5)<0Bqyz};LL-=g!eb!{M!8WdGG z5UruKpjMAp$&=yUx^aw7Al?8l@tVo+WZefR<+!THNjPd(}YJ_;fussY}H*6tw4aX6!&9tRUk;0qS*Am>-hm z7tod|f{;?9!()nFG^zOXFMT4<*{gG===hvID!~9qaq73H2og$N>wWBqFVsSSp3hdS zj*G>&a~-AL+P~s*U)#ll-8@`HinvfiS>*P;86~My!`M`LM$+^KuiY`mp>JGU+hJiP z$=vpU$m%3OfO&sZ=Rt{&W7y|OyR86)#*jAK{rrPC9uINqffX5JpD z!byyu3yIy==CQK^0ey8K7sseOe&3_S>z+}Zpvt9cQ&!HGE!6kMe_F-U?`Bd+MFzI9 z_!x}ZMfq+BI`=9T$dhNBUOnvxv7Dz3(5k^ie+}XimDtdttJ9kuNB1a=u#5q@h zfyXYFSEBd$oYeOe=+Cg=)meo=Wo@i5+Cni4dy~RaWVu9@Xa3Nw)9}e~W@R4_Um(c1 zlA75dtHta0a>qOk0BZsgy}DlEgi;d)#A--2(<;RQu+HukJ9!B**8L-`IjWVRkTi$9 zo{F%KhXQpQ%>d&LEyE#sRyZp6y^%8)e6@^<{bUik`FfU}g}Gh#wLaG0Hc5TxniZBx zjV-Wzgn>11TW@73-?)2rwv_?09kB^z8RJyleotSh(NbHn1jG|{gADN4j8xu{^jbcr zH|pNC#@rIUi+$&pYZfGW$Do#0G!`PpVe-$?xp+GoI7ylmb#EdM7&s|0EVd05OO3;) z8Pb>>LF|1V{sac46T?V0=Eq~n2^mUp&>J%Srq-}0hh?Dxfskc_a@tU0yc_R z3t%gY(uJ1cYkpF;8K`>+%sSFi=<5%PXh1dH$54~}1>RmE)1({S6;9muq?MXx6{Y>r zj})bA6Wr6vR4%~}P({YO=m*K15*|$w5E1 zC-@}Qa+Ze{d8sw_aLG8g?1aGh_mwwB()}FZHBorC`9xhkrOh2F((4p{VRLSP5{F*V zYBukz1j)yQ+FPz?Av@sSRx?V5{1t$_<9wE0@dYyAH{&oOmYeB)=O_p%EC&TVjgbR6 zS^p*G0@g&XTeOrQ&Y4JyB)DoSAraO&!l&aj1JNHN%IF!HdF*=Q_oo$k3v?D_%``~`i4)ya zh-0Aj47M_F3TaS6)I45D)D_ALLk6;8XCWt9y(%2B&ForM`5K>i&HuhiM9oY~&%C5r z7P0U0*-1;`oowACW>ujx{2XYUaEQcj+@?R;X%|o?YN?Em(5bMdkcbW!kcPD3cu1$H zQvHHTFBn~E>X_dG=#Pa5yRlYLR<8o*1(8oij=^C*!|im;B;0C)fjx-ExAjg&4=rPg zSZvlNp&;`U6{Q}8ag2r18$?vGcPb@07~Y#3#ywOBBNM>4lgg@kP`}AN@x0X(a@YpJ zZ!&BteOkq3&JJU(EoUqfxRov$xR47=^b(>Rnnr$@B~A!#I7QSjXYBQw=J{%{I*SL9Sj_=g*OC<-qEpnWO%wj!)#Y2fcazU9;|Q{ zeK#5*q;G0fel(XoC?4qTo~=iTR)u(zAp(^70b%+V7D*4MSmi?J5LvA$?0V93Qeins zI;7{DfK{Q`#pHk@@q>7Wonb`dmC;Fnqzgm6aK}Rz2$m;_rCKu6zDVVa zTv14x+M(j}Bn7!pAupjjd8<*1_GMvOtDyskkaHu(oy`7=bsCOzU= zK9cq#^F9!W*JcxZO?j>GS~fW;ArLZ=8a~^|5WV0Ch?&cAXo{Rx4$LYu3cabt%V06c zCUQ^xL(4C*#XQFP6}5rg2hEo-)7+1q-T*mGcQfwiIvOkciqJ{c@vR43X`46u#oOTtHi5b%5h)jOI0OWBCx7j83dQk zsL^RI$x7uIrwo#~qJu1|9u3;T(cPANF9okj^m;D`H(^*U9iJkEGRbKW-X;?t@!B4< zRdyosk>p@nUaV#{QbRWI2~TyuVrB!?pw6JUNMysf!4}1mYt2kbGZ=g0FPcM-U3 z`7Yx?axevP(>&^N|97;Esuz>fGuvq5u_<6jD(fYPF?`NlqsZu81{e8|VxjyIvS{QW z#(7F=e`v;jN>3&3#EG0!LRYBJ!z5@dWAONp@*tO5IRji?h$i8@d}qNQ2|EW;EWRTk z+CD9(WmGUB#T#tw#CpCT1<9Ww2YcpjT>iaxXGk`AJj{25TNTXrHC&bt{P{EqHp9Z3 z?40kKkC-in8c~{xtQv5HcoQM$)kiiCx0swj7SiF2=L(W+*Z8or+R_NLDhdeecc6HZ zF1|w4!*Q-c^Vb5&XH$A`0~hT8;m3$iN0DHo3Z)`F^225Km{1ZH5gvoy#}GVW=xKvc zkT$U6Y3XU&YW_34bUJ6`Rw~lHb|KG;vd9SbQT<1`ZGPHl(+^-Re?v8a=cC`ZXWVu8 zZ}KUE61`jFS1|a)mHL&zAtQgxxRK(UW{_4;eBgqXBYcP(PLSn2qFpwf5yr-HIS;qo zs5!ABh|QqL4b8CGX0;qn{n9?@SRt_12wgM>NgOg`Uz30u9*D>zC9y=%u^hjqx68OX zqL#D0Cd$A;w!xU=hL7IE^GWD(6++_;&xn#R;+%5U<&w-kLF4*x_rq;)~X%7;{$2% zkHJs7Y))VW%$|TN5xs>60nzgK{d>kg+_SAd>iGho^`EDpehw%9RRG&~c7_a9wxavVvYqkEorAqgRD~PEy)zw~=@KxN|>L z^s55sxqqQ|GQ6-Z0*$>*j%=Gi7JJ_leKvKrSf6A#ctwSjnK2}lMIY%ngbXy1$5>QP zb{}ioRBce?caNL&%sl~D;9?|;W(_-x*WTpw?^Gv`eA{C{wgyQr@2yfR1m439QJW`S zu`08B<|CB)lAD6cb4}q$8cR_IPeKVi91Yx?s^44Q!Dft08e_H@NQuq1O+4Bx1C6%$ zmtN!@mal8zQVNF`^->HVo5Vab3K&N=g#)C#flg%ztG;ZW4${~&P@q1Gt0QIR=t-BA zToqUT)028FCb80+dsTU^3>K-Iq<~ylBx^8xL@P~5HzV6i8CeA6fh7^8qm*e#{qpG5D|C9wbMrrQ?@drAms)unF zhDK~niuO`6Pkzt1SVNp6jmCSdKD=5SL;v)6vJkZvodNjcQ{=hq$|^JI#PJ&=+U7-J zUb489oqQK_xPvw)xSTVGV-fFvvgiCbY(cVji8M*;OT|*EBC>pbBFh4ozivDJIWh>I z*mZ1Yg)r8;h&yV{%pPsvJc|2N*=_(^szMe8y{%6J))KoY*WAZeHjq@qEa&Nu7gw5l zPJ?#eojC3?A#C)w+`cC#`BV=0$lu0o8OqL8WdynWJW?HmnHkRTEl8K;^;irnit=i5 zLg8^FmeZLV1+)w-^U!39@EZxt;xA&A);{VPP_&4u+wES<&0M0M_Ni&vI?j)c(U?)^c*NZNN35g*;)r2*@E z$^na0O z24s)Do_qLvSt^hEeU(eSC4S~crQO$o8 z;=sm)5>2Hkz7}vXDQ+g!3e7}4$t-i7u4{E;J$fv=*FTY2rcUavhT@7^#olayop`U9 zZUp$+Oi}wH=imX>SYd>%Evj-emSqpf=N^?Vd#P_a#C>SY61x&$6i0CoNBHLA(*@Dq zI%@P|_1DYv{=pXQj=dQ1v)%V8Aa%&o| z?tomy^^SX}neDZKmfb6ri7WwwWYF=C53#eU>w8R?3;U*e3Uf_mJ2zp+3~Zxn8uBQ? z5x%1R(2EC!fhe)z?6qC7@`?3I%lx9p=WbO28w{djIpFS{q5$iX5fj{i1h$3keWv}geT>Oi6jsE-In|^P4*TW0~ldAr~$8`x3dMS{qeU{sHPko?WW(OO7MtQTJ#QWAa{0le3*Iwqh(x*QI%D$cbX4(L+sDL))@y2S2K_{ zS?Av@!7k`f8(KWHdqbwLj);*ojX)8!CDnmLy3KIxHzd3{>DYP16wjlqXPrvJ)|Dt; zJe)~BHlgeh%ioOqkAzUsiyOoMB5{osn4;>0zuh!05;=|Oe#d}vnh#aw+eeyJx-J7r z2Uh~aNrzZMA)R8hhh{9FR<@ubkDJ4Qt@eN|1t;ZDIZP0CGa^9%&D^rrt21S#m!=4N z%tHbah4D}SMy+4ab@mafuK;R4{GLKXh3`CwkfAzsTn&?_yG$G46D*ToT=9gC-3hL| z;kB|S^e#0VVibIzi35<>W&Y1nf0G!XRXqAYcJ6JMi5&gk$D4r221ucOoYwxo7XM1x z{GY7;rzQWk{Qud<|FGmwxBPz|GJK+E?o7cnS_Qy9tTnW`RR&-zZ=Ud_^u$B;v$U(| zI*d&wD$i12{71pkuI`0q6$RUYGQaoM{^f;$1S8Q)Px&Q$H*-%%SKMEql&Ity!E(Ov zZk(6pj)1=&sDF-1aRSN=3JT3J)4O8nwLdMg>BVn!2=_>gv z)(%3$E-;CWR*XzNh-45fC04~jL2TtIqR#!FD&3|WfEWc;yYAO0clxL(in*pL$>Ug0 zRDX8r->~qyB_%5fEj zWT^DcvgMp#kmA4m+aJAzBv2~TGrLZqheR+Tn91F2%y_|A%KQbeTlG(!-LXZlx&tZ^ zUXFl1bkNqsitEo;ncm|!xPgj{Fcjn;p%EFou2M*QXxQpn-H+jkdP%CyD2e@{x0tFQ zxf6oP@aEE7zw;4}n5m;FDu!sO&6Qk9>fN3LM%Lfk=zn1?1WWnG)nns(%JEk**GVVG z6hz(y=3erBiR zenSb6d z{2v7%ce50F++Y`fHfe}SRR7E>-CfR3nJCTGpa$Qyd>#w^xu__y(&;{)8WQlsU%e>? zrlnO`ZQn5*3hAMw`Ik!kV(t}xZ;nlvG65hWk5l59(KEA~Z6aRn9MHkvvn{Zw`3mGH zh*n-lR7G?M)_#(LJDt8A+4!p^|JK_<%~ZRvurK;@<ga%-rQO zMcgB#7qGDN!LP|3ZFYovZ|gy-!oc%JFCfmQtbe~HY6W(ZkXIML+hbf>2`+n6r%#9S zAwAx!Lj#AqYqmhPenrgvD(es8$dH|-zOIGbF8feX~p3a(9x zrHL4z6hZ%~wNVeFxSJh-IDFo8b-$BZ|7!EkX>){|6kn!9#{J$B`b+(r0vV9uz`1;n7`}!j}oom|F!s^to~(U|I?EHZ|#Fe(|U7IXSHQHg>Ls75u`#N$13J) zJ=2-b-JS9vU%as6m{rxLM?0`k2~}xRz}t_IIUc>4HsZ4>uIe~k1D~QevhHh(pK0MK zcN*-BKumDHFjPTDkC({Jjh+-Jh8?~P0`vaMdw7xq1-SR;Zjn&u(1nb&X;qk?ZYt-j z$n6!htj-dja~M82=5`-9-{Nu%qRIf$#@fuQjFOs73_q?&?3iOvKmZVKw76coZXI{J zUEX;}Uts#cr|#5gVNZyn1-(~Qye4t=SNAiJ(~^35KcgJPw9leWflbS~w4&E*d=hm!hPIv$<4df-q9}kzK2K4;d)*Z|rxV{dcgUaIi!-GAw z0{HXya#3b4J?l(jVeb76kaKmlVAkYjP%z#N_{pCqSRznAshNnePAfDiZDtg8v4f45S2hG5nJj9vWH4l?0zh5~ zfI-D_-i~T$m`kbQiWUiI=)w0tp+Z-U3@E7r%9GuJ zvhzxB*u;1&2eIoXxfU#$Xd*CE2XJEd41)d>Qc^#Lg=MD%bz244)`@?$-tSc$t_+km z-~VwnQS|82(Svmv=uBFD(xY#sKmllAM%)>9JZ3;l=~Tv8A2OS&`becd{x22C1Vo5m zlLoq*M&oQnm{y}gGgupIVD$x7nMUB50S{L0XHdV(P7c_gLyp%SP`b_r{5XTIeZp_jk~4< z9oES1Z`fAaeYr0^$D}sVkg@yBDw>f-A=L?BF8zzk_Br6G1V8C^9DrZdzPJ8B@xfbX z6CGfE!3C%=Nv_}ccC*r^MRjE|^ECoH+GkX33e$+=+gcF6EaIUfeVEfyHz7QH%Lfpj z@-OkN{=bMX-2~-ODElUXS6i zADI3yE84sbKk!*+UuY}fa(FFnKUWkeTtx9UsNJTe{q4AUu7qf>CB$`B4lYETEja8- z<0d#`ZlhJqeQ1Jti}>Z}>A@yceDEVRUY zp%*ka&p>QAkZh96NaO6y>z%b4vLl1){8|>gm1Bq_gCLXczI*jCu+_DgC+I6Paz}S3 z0`}MA5z*$`MfIrp`c9k;tu_|%0pZ5zrt5W7F6g>^xY7LeT@>TEN`Nb5?t8NQ!nkf& zSp6Z+jv?wmH7mN6>D`1l||f7pA^u%^1LZB#`?RGKJAZvqws0qI>76hy2bU8G41(t8k5L6I&X zH7EkorG_3+kR~-ifDn2QgiatK;mr6v&wii%e%tN--XG^&*LnSy3zE6k8gs5O?{VK_ zjG02cP=goBY(GVZ7gWhLML-WU-OJHMbLUqL=;j((W+r>Tr)8J>_+_nu{9fhZM8^4s z+E5h#&4|x=+(V8beJ`ZvQ`L~}oBmWfq91X$y}Qb3kP8XyT!-DFp%(h>=Nc|Z%X~l;H;fYZc z^Q7U^ai@j&GQoU>SB#`8%(%DpshFe=p!%9Omi+oje&wY$!hLDLGUr2n?-eXZm1DkmuaY3 z5SQ;0&==#Ksm&{gEUbG0nG{;~c#Kb8QF8pjBU%j(-}HT4Dm<5g2e=68bg*~#Q@8Wo z2N|Kmo28r@`?<57;zxO;dFxamEL>OqM92k)DKAIAGOS3?)CVc&HveeGk9ZVNx-M2& zH@)ZhJ8@zP%bn&FR-|fDgT=d`wff1veFB1@xetWlDHTeLhA!#Kr^}B=FQ!1(vk%9e zy)gcH;O1ZW`_Tcg#O{9@)gq!X_)PWV!evS|KA>?<)dyU5XJ6vUmEw-+{-KJsh1Q-& zfr0dcBXr6O45BK*v0;M@r;UK`cDV9*lyI~zp!iViA-bf4;6v(P=zoQB=c!tZ8QR2t z6Myq+ZJ5$mgEHGO%ssP-Z`x(zlDrQ=znToIQG8i^bo|Lu8ePFZaxDF@dx&V(a&z4PpPaK zg&8?yXu${7zap`5N@#?F4KndjX8jSE^|61700ULN12-5X z^!o-WRLyS-{$7K>Uy|Q$=C`B%y_5Xji+_k0!N0-6Z@Be`jK%XCu>KTLr{364O-WXX zmryR80?b@SD0`k|D{~*{Hj1{%K6nznLWLT=Y$x4G#j_??Y&B93Q^HaP z5Z{1QSP+)}i}P78oBUF{e+%1y349ua8CJT+=jj&J~*!nl6%Kb$*)IH<*E@kr9jG(VWD$+o+*4dA!Azh5xOjNv9h&Y z%5_#{S~?g`f6HetW!zI>T>XDDuHmk0P}^HEv^x&n7<=B-%)tO&Bw{RQSn~$eJ=MCH zHYbsLZ~jNhRT`d~wScdb|K@Y>{A|G>!UQ$-|1!d)JbxuSq)^snT0ih0ZFTF37xe7* zoN^`j65V`*n97_lmwjA@>U%x}FD^V?Ls%NidA!mq3hS-)o-jy+dz;M~*0>N&-W3#c z=w1au>J{(^iT?;v=N~@)$iN$O(AABJr-SSo0D}CpA0#vNkQr_+=q#P^ zo2cfNKiNYe3Q-E#nSd=IJElGPX+RY|?WV31DMiY&s1}+Ssuy zIN3hJhS*+V8cwm29@@G3vDes>Tv)c$q$q*i8L6h(oZf*`XgwKgcyzATS{RUR z{NVWL5lF*B?+K;;HI3I3wKbo7qw9*mgXK_|$ zNU^xxDP*|RBepv32aj44R@|AJ(^mgzb*k!*z8vSxb>8c%k7XHbdXqXZK;A+u#^{ZF zQY7Mw9bqo&?cd5lPT~TIUNCc7=A>mKr_bef)N#V-beo}rgKe%(f8&nNXZXPW5;#~Cy@N*o9KzAbneyoP=BzK4)BOEC68Dre)T6_yCY z2O0;U#7c?h1>ch&6m%rMo$2lM*&4#=l;xlQ!s)bJysKR-dWplSGvAA;YHsuuKiv%_ z5(=14?ck!laz%ggJCkzvER51r-Tb?0FH{qk+QJjAF=)8L>dHPJTLO!hZSL^bxTUJZ z4F{CM+O+cU>c<)}B|sI^53hv)Blh>;ufFAvle=hVJ+jD+Yf2Z@Er7#F(ndjQ?(??R z>n?GOul=!VRZ5eQS|i*pNuh^d8?{^%KJZ8Eoz3tF^Zwe2g45xdU=@1WeUab%92jl@ z&o^F(3K7*@RFKT&sddBZR#8&;wr5-xKQM)rM)H5fbmiXEXC=SqBx8pw<$HA8N9n#U zVrw*BRJJ?ov$}A6FpQF=GYYo~qqsIe1S3C&xNv+oEN%a`BR|Bb+=xfFw-4o5ADk|g z&g^&0r> zn>l(q(zX_7?gGW|X4Yd9jmHu0r(_qa^1|W%q(+yc4&1 z>*PT_krlSHHm_F)lj5|!dZJ8rg9seUZIo2_C-=o6hlKH`xpo`JKu*O&u<1AgkG=G_ zO{W`l$H>?DldI$r35cSjdsgJWD3_Q}DQs33cckXC3jYxUI3$Mcd1n`4sW7qBen4EL zO3qjmB#X3xyWIrDCn@aT-0k-0=OBGJBstHhVzBDeI|{0*uBf7o-DQI^i-2Y<<@Zh& zoRYbE?ife;Frat;!T=FvfNb^qiep=NUC#Q98el#1!S)JFmaSa0l)mS?=x`J z3vj#C0Ig37Y>I=402F|ZM}KXKfh7k3j$!(f%MI27jH<}?TdV-cP1y@38Dx#*kNlfS z-cRk++-O8G0WE-UzcK*`#pq8en1$bRWXzn zNTi_x%c}&LvW@x0rANxH`T^F{Ls zh&j8Sy?w2h2VYIv24-LeUD10je3hSoy&+xfJ-wmn?e1PS(18;i$`1S7KR41U;5d{r zhaG;HwPA^dvRe*XrC)R(Nc3o#wV9CckyRXYi=Lq7iqQ87?d?1ls1%7X@zlw{aQaU! zS+usHgv++SOBXczzo<3wO}Zij+9;-X@F^BO%I^OrrsH?oR42*6cc`X7oS4VrwCLYB#132t$#~_Uu+{A0 z+!6HkRe15CID4dNEVi$(zh|3eL`bxnU`F|Na}fC~0AHu2+_KCSzln9s_Z9nj$U;b@ zVp|a#>&Z8)&izbFmV?vnIl>}D+WSTQ)2#)5!ZSv$j+9^5r+Gx5monxL$#_JykKxIl z{X91#_b2euopm;vA--pzUc2&Uj){SqOKALa%VDPn!e5@rTwn{P>e%P>KvMN7`1+o` zhbFJ=af)EsP9k36xQN@sL#Nl|B0ynnL!B z^N6m>;zMK}4eeFo@Ei`N%8{=RC_Kb8{`nF3@e%Oj{WO2vO9ac_dR3E27aWUIXF1gq zndNFmYF&?DDsx7``hYI*V`lJUnqxoiLWM_kUvOY`)69_Qc&Zci9;Z<+e!5&71NAq) z>%mlM0W!JN+T38+5AL$}HYb3p_Le&gIzA21Ut^Qlse<7CJM@QZ`vpNmyE?rTR-Yylq8 zi&2GR@`}<7l#x1Aeh;t}yVC==AejryW|ufl($)u92grO%;86v(0Onrhdm_^m;@eK* zqhs^f18gNd{&}A6x1oO<8my)NRV%uG&-FwlB$vgT!7`ik;4}u&vKKPTrx~alq8NVd z9Uf3EFRU%KbefqjP_up@;RiJVoYA+ZaT>kFL-$G`K<2`60pN^ee(sDu?JXbeCQczl z{U0bd@sQR6iW#XTA7AD;dEDyP@3|Cs&CMC*finP-{jc2KC*bu?hV+BmdB*rl=M+E- zeAE5<8^KikuYc`A?12aQ#^(hC`I=XeO+VA z^y+@eIe<>3e1E-bsYD;62IptQ1#yuxtJiQungS~Ne#5?#pA1YE5?m?HO6k}8LTYnx zV1>&}3pmb6P-h3r+Bk0=uSgV97*RQSaX*J)8-7a>|3RkZ|F@!nkEWfJsV zx^eIAY3d4AYO1$RpKg{0%3QGjPacPXdc&q6~TXlpWg&BOakPC*SX+Mz1lzHnR6c||~vpN{RmSTGe;@UQpQH#IVz z*hTzgT@Y6sIQECP;?<=pcZaF>AqAH>E`EB*Kz)$m*Bj$$JEvug2me%i)l#$-&Y!qg z6(n4CANGuCJaug9smz5J|AA2kUIk{|$*hP!7~VVs^FJfMSW=UV7#_T$E>>Ts<1*dy zI$rq%@#Fdh$TZ&hYY^UwATGU76}IEN5)fZDa5B``sZDLQB{f`PaefI769k3v!ew40 z4SdTM>j(?C^hH-Y^cC`uKkS9nomITV^!LaPp&RG6_0_mtA7O6CF7?#*e`j~}JIi9~ zb_V@TBeAG->w{-F0t&Lzkqx+%sm z-^AYZD}R#1)KtOr7#Q2fu&)^X;i|oXM^%;Jqk&} zUxIBUrasIRVPKFNU+w{qsfJgHctT>}RMa`{(@O1p9A5qHt0AET>#n+@Zt*3@ z&@1vWCEH|b(>Dr`S*pCbUfo4hr`47ZC04Xldk{&N*J@?>5OP)j6;;hTI7b9=YS(*S zS^P1{&B9uU&Y-MB)C|nSdVI0vPW_yie%w9u$ga#a9BF#&I^3oWM`e-5g|!&8GQa4) zc78WPdW3utnV(-Z-+#>MF&;(V`awTh9GiA0=SI;;5u!C$(c#hhI``CI_zS=ZoW-z2 zD#l@r?bk5)7fl<|iUxqg*dpykSCQ(o8!`@t`sfYEFPi}So59mPqEZ~HEW7R&h8B;I z4Zi6l-=Yv3g%0Or9Y+rfv~hU%Ux|MexHm^sD2TgEt1x^8qU>z?jTl)v1hx438qXh2 z&neVF%V{`-6aI3`ZX+6+Ja{6p610|VHC*!51s5(S&rfL9*BL{U#bv*euFmu&TaG}h ztO~Olq|_@&P1}x8eekXRM=!5f;w^nnBzn(aI;LJEa-X9Z-Zzyi^%LnTvxAg6`wW6u zdL5=HRGdFpF1nGKXG};Zi&_u8(I*bxgeirznE4OMBBgAOf(TqCWq<4fQnmxfp0`wV zoEp?a$E}H$U(e8n@fwnx@5}*_kR}2&Jx{uxa8X}s_>rIf!GKc0{rfA033t z7jgY@u|iR$n*;I+opEp2j#XDabX0lcd7b{nd-mpQmfo=(XMEQdJ1&QRxAG#cal5+` z_sQShib`}?9-M_2FI=KC@{*qG&G*T0NFlV$h|dQtSXjon=S>y0X(6^2{65Qq*(gOB zsfNkh;`xG{z-(xN*$AjX#kLXh^%nR;mY3tqC!2Zsiz#H)8TS0st!{+_7xj$spN@L- zqy3dVFwUAUWy+fi$-@*{DbGP~`|DXBi(aGd_9h$U78_BkRx5bHH&w>x zpT1E@COg2v^Z_mieBG8WIgEg(7I(evo~f{b>%RfnYD*Bn*sr1QXmUxf*QrBZ^v?-d z`@FiY27ieNqL5ty+f1jmoKhv|Qg)YGOCcmf$C`7|{#&Yqg4*o$g#k4=50lGM+YK*D zm0+gdwLn~R{m+OKT*}1SbguYn_FM=;1#Jrw<@>V;XpXiJAI-2Y-H?Rng(8 z(dIN~L!!9QAyL(~Ep`_D}wCXW+x3@y}-`q8tdV&KNmn|HtG8eDje4 z=zZ|I0g~-iaNuALjnhqeb|}>khHK!M3Wr5&adXKFnNRO7{nvFo`2Zbv6zrdekXPY1 zN6d6As&o&J^PDnDV5Cl0@ce@TTsd^{n$z;onrO>28YauHw0+SHEby4v3A60#Sb#|~ z{v;P8LCCAK-|0aNh}|j7*|Uq|*7d5s-`4*i*-})Zk5eBt`AP%u1MlDMp~`XJk`jCZ z8jm-0i`9t%CZ(G$>#XY+t5X%&87;J1l>ucF`Y!Eaab+nfGx*bkiE1r;Kp!u8*AXt*sery=}% zt|A*n?PG`r>YX4)90D}0z`#ExwYg_9%a?(+#!3k_1?yF>@aC4t$FfA#Tj9}} zEL>L3h^Zn)%Yl4O37ZggicnTB_(UdHyZky@18m5D%6>oFkXgLTcPGYH@!>BhBl*vj zkr=7tBR()5Qb`p9fQT*7nROG0YN~&OQ~w1;n8tZNFe>xYjZ28EKan{dG0L@%;iE2P zZ|C&-^;|l=qpnvC*pCO1LMcE!cjub;>Fh@t|Ijq~03H4VI#K;Wi6645?+6audVNHZ zhwkvHa)21KWA==vYHDyfJ)jEzm9_Rd-Q=lL1OjXxkS6cRtpaYv^G~(#Dj>&&p2~b` zp(qDHbG*d1A~DXRG;xVOIB*{gju$i=5ul)(NQyCkfo@p5RXC1ceN4ai(A{%DU5x=n znV%1G{V8~M1I6SeP(H=~kWB+4)OTLX1b38oQjMpoJOv?ZssTV;*{+iSsV)BksXgan zpgwSHNDVYa(cI&7Go3$N=2L*ize0<@r|6#n=2CF;aQxtAMEno&MUp0S|7=S#_`mQXep;_J85rEzi?I$=lYf zV$N@Tx5k9TAw!<5STfOOQ|HD>*C;MU<`_xTtf-bqMR|#BZ?-|B<2zH38q*0Pc1nWK z&ym7^ZEgWY%+w>HC(nfMPMEGvv6u?IPxT{ic2|2z#7p_TV4|7$-~)#gP0%5tdUXve z{%2P>L0xR!5u3Q{p6RPJsikih~kZAQ*_R;O}b8~1ooCDg= z+xMKZa_>q(JQvmfhNYJ9oagl1R;t0pPx#bFBCW31*&p7Wg6a zf|1kIhPYjyz3l9h8q3d5dcT-P&!_?J>HlDO{tZk~0U7{{srNlDuXUO^OipH!Z+Dku zSceyjk)Zjv*U_dFg`>Dl)XtYqIX51pB$WRWvi*G`PEZFPtPyamCFKjF99g$F3g9W$ zzSH01`B_YSW0LyXg+-vhG^H zs;HH&ob$e8%-5?|rqNeKzj-$C5?f!L6V_Yh;Ofx^w(3IJQMs z3ld?I7~0jJuBUifw1#ji!&uQ1HJ2}F$c(NgkbEqL)@d3VX=yQN_ZWWEApBk1n;lYc zmc&%QwURJJDY38e<(7T0A@Hj0sJHniqsIe&faQCZMy@WluWe>)$Vg$|tgPO`BvSsW zOxu5l;5r+5zeT%0?`fM71&&Mtb0q6j4w<9oflIvXh$3R;p? z18w|o+0UPacW^CjV)QZ04k+!g`!2BD1g*jC_C<}bR!`+3%wa#v-Ckx<$c$ZqKOc(; z^oPvzb{Bm`3RAv5qQkuS4KmR`qZG>+ooU^t9Zu&kHSa)(D}hhoGUI?|5*UEZ7Z&1% zTdO77tRoOhNXFH-MA25XDg6Gu*rkWWNMUrwc>qmU4wX2V!?xKAP~VoanuDnnCC!G) zSDLQ}?$&O-`XQm-e<(I!nK{z*Fl58#LdBefb?es4ftqb8&p zdVJ!=i!cYnW-lte(>BwcUqxE(avZ(yC+l${(fF0$YY$>1QG7T&cWRTL-Kv_~%Xe1B z3o_e3?rzN(W{?A0$66GRLBI*+t9|k?%P!Z#P;7xx{$-9K@cn3sp>S6SMt^e=ZdZs9 z{&E`5Evr01YEst{K3gS<&MEg=swP$pJzg`XOmHCmkV&6u$0k3MHHjzMdN<+?MocjQ+5J< z$ppjI0c+E%aEP&GJXEV$7cpeyy=ioj$yd3Za7|E$Boc$(il0Oep z+Zc{AD6&~{lW-^&W2?e_YqaQOhgl5v=1HgA2NH~1F2dt`aiyCR_ZGd0S2$(KvPv#$ z$WFxABev{|T5S{o8`jes^uriKZ*VC=JVA$EC%)f|5HkcR1R6$!Qc z!1CS@<)Ujqm(i$YJE!9(n_Z64f5tQvmop`YrY3{;<*tdp-D75L7IgVyMl?{z&chUW znEby(l)Fo{S++@6-zSpkANC}DIaQm|BIs_wTxl{pM*j?~szVBGqmB>V0g8i92cc9s zFI^voQt3RC1aT+$)BR^zxBFx~-Di2cHWO9HwCo-$Sz$;YJR0rk+>c9T9P5)gDd|>i zMD9y|9`~l1q}yuGV|czsRnXxCn_1{T!r_~J<0|QO{Ndy~wFKLb;BX$TJ|mYWiBZ*I zuCtxB294*6QK2#y-u!f%(Ubr=!Tn9i2JVFBGN59ln%lfvK15LrICtLyg zJx!*CE5)49y{`)`#po9G+%9V}h+nlAPkjC}WrPyz-U50^3N=Xd9q)D^wiX#PQma1$ z;>a6`fT9!BBS$#}^^Ck~gexC;$bxD^al_WsB&I8GT=Ze0Yh*E zBt+jN?+1usXQqN>3Z|nVkW)}rwDWMHvf~b@1~ue6*~0CyZ4Y#lZ~J~4_}i#+cya+f z2f(dDO1AQp4rQO!mLB1S9VK4}nw*sBD?`W9c=;GJ?6y+4(8;~vi+`qMQv|JlW?=sD z$RKfC7^GFJ03prL=`jj}Z+(-6WDXu3wA+l^z`TDs#GFLQc>F2OJKYAR3~bhJ6kP}; zbEodmyvdU}?eD7Xa|DnQ(0@OnAWz#Es6z)pe)(yCzyYmtr=2zgsT#SzfA-rX6p``A zYyW$d{9SkbZIb^(ivDeq-zNFJb*%j_@RL{GTD8TWW3q0i^Y0lQD$ z{C1rvwief$qn7v`{h@9L$RexUfktJ3Tj>9fP+G_ZKm1qMiZzniSU6i!+(Ng><{ZM} z$Iv2v4#7KH$f55h23Y2{sq#?WCZNBa<)#1@)(~~fF~GF}bVezD`vuba@?m^*S!jeF z+sdn6&3e=^X_>X`g zDBAl=UCVz1^qv^;s109EkpB8pYAfv`(5L7Dg$IfP|DJTCrwOzyP1a`bTDtmxy+36V z{|QHeV#L2Bw{z)0r%Qirp=b(l^^^Y$J^n4LD5e_-T({MWQVIaH{QOgH=e03 z9AN6s@auP=04ja(A7RPAB`7(i17Y&xo5%r55@#vGCs@D$8?pQ1_>!6b7(E zaW_i1=&HtmbBMO(_7qXp=7N?I!Sho!VUWGqK2VvX8YW!){dSBvSLh}H0x#J92S5NY zj@J`*0+amfEXA>n`bB7neDgsn)Om5etjCSX{&D!B>(B1#8DFE@CP68}@%R$W8b$0= z{~sWxn`PrF*2fi=N6ot{?g+_FtTRo^a*icB*2bJGsJsQ5m-AXDJ4MOIyi-emeJ!mT ztO&FJ5HkNAR0aV;)??TW21?(ki*RywElNI1QuIsUP>X9flr{>0>MAT-pPc_`F#BA` z8VB^IJcR`PX&TzUTX`p_XG|YIf6XPK?!MFJUev&x*waLOdZZxYR$5~dq>8L7HTa0c z2K!2H2NLHli2TniHd=!0h&gj7qb0>>fsYDhnyEglXW|yc9TNdhWX2DgQ@+|((aN~OX_?j`BvMdE3thXr9!smoF$3oNT>PXBuoF3jse zKsmpWLTN^LfD|oAQaC}aSL^1HQ831$m3tn&i9U^#Vowu0L0wR@at+9(uJ}3dinl&Y zj6=A^y@n|jZk4}#XNYchULX)V;PHf>(pQ1}yBl@B=-dt_Xw4<=0e{-6`DRMPmJksH zLP`5OzCvTiMrgo!O){jzXF3rz*VTN$R*)pdEBzMblGcqKcovjk? z$d7V#L~C^p)<~8E4Q#oo#=1O*dSL2>sL$Gs_zUK@nYD!+ zT=m1+bYdi@yS$(3 zu5UmJoFehe2`w4>;yz|gOeJ{W&I5VNzO1LRsJENlf>Pu~ROS+)r{}r_YCvTCb9yZX z^2@lJ-?uqCq$#=OjgP*~RQkT2zVPX`=|Co8QA6CJ`lWOR(H`Sr?S=)%I2f(jp^+?Y&wM?aL9NIL3cHETzi4FUO$NK@h3zI~FVsT-tvmb9s8 zCuQNAiAM+@FuMBJW9PcxZLPCIye8hb z?OYgFT4#YcBBU>;_>nPRzFx;kk9Qjrp$M$0Po+dYbebRh&)Ju3 zAT{xEqMOHpaQ7!usm1U1Sa=y`yO<7m7T(_5-MP8D-Ef*!K?L1#xT>J?tuSU_lb;sz z&6!Lj^lV)9;F-v$53(jW$1V-ua(6!#g|6PuX09ymSP-)jeaWw&*u9LH?3{MDYx?wH~rZor&nvV zrM07Amc$}CoFngz}hp~Z# z5!WlLctz&6&5z@k9FO}S3OIV`HM=se&GX9kJ%ZYAIQPz1ExdR}+wx?OTk_t^38&@v z^|%et(y+hb4Y=gTPbdD&0G7e!vtznC!eSB|qibM7r#QKX0vwuQ;qH2O%17Uy!)(LO zPvuJ7h@#^hjN7TLr4LgeOit>Y_4|x%eLf@Y07gV|DVNnk_VsvRe778sr&Nrk+nmgE zu!1VasJ5UHDTjT%w?{1HCq~g`U#D8mIYux;%s%pmZ>0&>zDN9wptW@Ya3caUce z=sPt`O?^`4>KV1oiQVgX;WXQ$+xtIa1tpE_?GM zfR%N@E2#$aRX~sHP~H{RlW|JjXSO{>5U}F}$U=Sn`13J{_ET^svx@71)*Afay*{Hf z|GI{nhjW$qeF zoRO>hLjEJ}eWB{$i7xiIzM zZu0(7>Grp`M*A(uCa0n2N~HVm?-Lkzj}t@Ug+-8U{kI=bJ|8-s3R0Z^{XaYm zi6EO_%53mQY2!Z)dC?4Tje-iE_Huu{vHuvAM+%H*|D^;ZkpHNw`^P)Y@aPLjT#r2H z*q1vX?z3qhFX0h((XQ{h!9_=2aKK9xI0r_3z7S{S57PePr5q|t9(7#;rve*dqumny z6C^VGMzErw_fK?nh8(P3G*LJJv<#>mBsKTe>4>hi2s(Oq`V>jpu?wK{M`yr6a`|q^nR3)lbRSvA<+7>&+H@Pn&Os#4I8WMR z7g}*oFu(?x*~%e3y*OAJt(#{%EN#8A^zPmI`bgJzZvWik_*mSCRgP}nyl;DWu~oA@ ze)z-E-XjFZiuQxsmIDl(ekgF1`1Q?!%3GE6$OH(ca+8#T6SA&ISSD68oI2A3>b2G=7eLx-fC1qt}OY{SMkkcS}Khe-S`}O>bvA z@@oL3YYO7_6%JJ9BYga7#yam*)}I%_*`B|b%|RzJ29hsbW?s;#E-Zhn+zpl z4XQx10KCPP2frPuhWs!w4;}#;Hur9GKnE_;%m%^7MnXZyg)L3>Fb)Cp#;~|BT*&VG zd?@wN``WQ4wlw%@v{kJrRksfI`u4KiPXK5b{;UZ(O$a7Dg^fg<9#tTQJi#@>`&NJj(fyH_qvux ziFRmh`};0d6~fr|6Vm1z%Q)kV{?gj%a3%6xT*b`ZTXU#se~lcg-KDEZTn+tZL5m>g z3gx)Yf1)Aul*J8|M$-0ft+0;5wrlq$IN<)gos1asa1jB|P*(A2yS1P;GN+*8%(h3S z#1Y%dywc4*10)am-u0YG#jA)UWAAOr=D6Hl`5pQqqvfU4Z_49A+E1TA^S2t6$>$5I z3Es{T?_&~Kb^Dj*-iji2ikwHuJ~2&XRtf8tKzb!XkRBHxAJHWkpB3eM!-?1odYw0e z0__QMzG>RR8nYEKh?aPR4Bh=I?!DeWzQF2l;SoD4Hbm*X(q~lcpJwkLeL708Eb=TZ z&Sx}I4sL+GH-awhqX!^F-)rhb6e+0|HnRIUZ8a@tMGD80+~+bTp)yAD1$ELPWLBf} zwY6QpYoM$9_6O1A!Yt|JmGt5>as9h0r63RI^s6*H!~Gj^N)%ICIo-@_W9 z#iX(|KP8{f1@iCLvesuSJ}xXLI;4+?YYCVd412C|Zg;qd3f}p+ zp@uGMpKq+ra?{ROskkGs-n3%up*))rrjuMCBQueEBH|} z7?u^RWA*tgYtkNrXql$*5jEWpCjJu+((>bG`n_!ihE{U|w)SO?FeM!RT+Gf5gv>m# z`#PT2V8s6}50=*H=F_ZZrA`BK0nzzA5ly#_0D5ZC7HZ2ECN*H8HrM~I=BSf|of0^FlXcwccD83P zY2-F!)8adNMy>V@`;gD;S4T+FeItGugaOhLlZVQbB3@t9x@GoyyLq^*!uEG@Kf>$D zR1IVF2ci>6q-!uastj*}h$e%W9y_-~x9=9Hy%Vp`zH^|Uk^uTUJgy%L(u671Xc{0$ zVLq0h&|P`(VYwo#2;$7PaUcDQ^5i}(QTiw`^Tvv~>4{fuD9H(xyoD8^l*notnCtkh z1_bY2>HztrN>Q(36PxrClMy3JhT=IY%J#!MaCr>-hIKPLPE3PSm;cQ>wV>uw)JRlP zf`o0)?4)d{0Nr|UE>}FpEsk@Fg`Hk_pmt?KsObQU39lSzDp9liZfrz{q>DKPN#Z4`i%O$mXR$1~X1Kue_NFsKlu9>kqKVzs(3%L7;^Kn>MUO}~ zlzz_SfvkGJ@t2v&7Z!2P<`|oopl`(+NNwrO0nd#+22`bnn>J%AVTg@QpUJQE%8|W8 z7iYoIeVG=p``w-g>B0Ii^;y)e*sasRTwJ>0{KZM5a=?C3t86mGhx_{erzr-RyC&uL zxc0_q_*YHc*zSAj&uOlB;J#x+AT^{dL84toEZ*zB=Y|}1xnRcVRORl-!!>OS4e96$ z)7tcWwP4tyr->_TGhgXM=H?AOY$s3XVInw;w!ea8`RtrGhex7>c9#MUk7f>K9R-2L zL%!BB%ygWjVkk_NM-HIC7r{%FM5q@r8ILKM-mrkPWxS>%xxe)teO+7o65BKL77h+V zd&@#ZWL#2gQoYH;k|@%7eSQ_JhK^4c+G}RVGKJowSyX&SIoZsG zz&axkB1fJonMjW|OylvBpBd7!9NJnb8d36T8iW|YhauH-n-;otfWZ^A%PsUK=uFlU zx!p8IhmR)@mJ=b8I#jTk@GaGWW1Ux&xqWBFyinR*!wr5m-UH3|j8X41L?>xU_y~b# z=%Kg@JjSQ-nh?j_GuNd95JJI>$!)%)uAo6tSf{>YQue3M9IYEK<>W`f1Q=Z@h9G-?l;7iQUIaspz&#{bA7a4l zJJu|wOwJbOm#*h5mL*stYe}2(()?qClUft9{)&=boNEd%xi9MPlZ(EkBZ1VHFswof z!jIUFj%ko1IW8bwcIn-zmAm0jkM?y)6J&*^7oUdYyA~f-uaayii))j@eEe|EkJcLI zQg}T{rkMs|9KAuOnR4?hm2u!4I6y*M>1&St>D znXW3OF&~|u)7@8g*#4aZE>USOCsv}P_Cox)S9$S7jZa*AY?t2FJHyM^Si_|ow%cFU zI!IO=E5v`Ad;7L@D0PfMO(!K zw^Gq*8qqq+YdaQ525OoYg1i3D15Z>!qH3+N)^v(+gGPgb!)Z~{qyuTo(v`trMJy=M zQ(=~pU0VjO8M-CMN_B9H)YBhE--azBI{kRr=V~JsYciRJux;nc-3~@-D%Wt%DTU0< zy&pOo^n~k4)#f1L8a@0`VM^FB95Ds(EsCH=k-qI*`lL<}*W29Q^rGvCkJtbSl6Zj~ z&BDw))Io1o%+dR7Qi@;}HX&=Yd$Gr%%MafanTGL}KncR~bmXV*!>#V4xE{`zvoGB?c_RU z9~T?CC3oXBP7GvLgq2sB%yIe&F z(LJI;K-5J0tPH|uVVBqZ8-vP(!v88q-| zzS!0{Q_db~s;c|};hJel38J+j`Dv^KPfUEBjd zJ((c+{gl=&ZFu6ry~f2;^LCC=Vi42(%(%R=!4?7b{LCp6zqU5<-ZtovYjo&Pmb{%{ zG5yBcHoIeWv@lP$T|koaYF8JFJrtc+x-zP^`xvG=-4ccy83ZlXBZ6N?rl~i(bkw{_ z(yb_|ntzE4!B)=AfChljp*CM%dD(_bs2dXp0~CNnApX8!MtJV2qPL&DVxz(CJ8X~) zXj}dzNLn4hF?B_g5=tjTsN6V_oN=W8f%l&x+M-_A3PsQ;QweSAz%#VVN?W zWNx3G=1Hn&gMKv%(a?xKEu?GktESc6S?lx0SMTaHbSAbdh4>jf5|%14)Kobzk06umi=Lz&E>y#eCre_K z+%&eAJTgz-NV<4t`)Gcg+1-i^|B>Nq6E%3UabAc#s%Q8M`8v_3u5Go~p)cq3?Dq>b z!XKc_U54D!Id^pC7Ut-zdfbQ$#iElh5+Pb5jDBBM*7E0Wp+;8a57qMkY9TnAJkfVz zZbJ)>Hmty6MB{}!`!IP4gc|wqYne^Z$$VIzJX11RZ)?=R5O2%6(W*n}Yw?ps#*AZq z3>lmr7aQ8E>%n^OJ#tGQw;XaU0CZ}FK2zGHGURZ-G-OOZhDL)t@~)`d2RW@fuzx); z^G2jkmobra1>O|fH*5>J=aD>YAXAhT*In7r+lCrB8tsRX+gr&KGt8{ZF}pF3lvb?C zteqLKm9I1jBKss_t>|aj@wc@^#_~-Oe#xlQi18MzWiAu1T%NJAt7y?xYEb;eY4Q9- zoR&!M+hHlLIOV;VxOss>S2k=0BxZ30w}l;7M`>^fHz9%^e$DEPAXRA2*&mdpA)B<8{A)+*n+ zM8IJ~#W?Sk20|ri>FD-QO&nr%KGk9~F6CRQ1dSWQFNw%*d}@Xvv@DZyoLK>j7)RtFEEybVJu8~C2FIsthq zjQ;EwC)0|1BXReA#bj~)U$G1N;ja~!BjD3xOdEj{Uy|%r)RnoxH~^c=y_qamOfLfTVfAIDWOFi4aBB{8Y5#AIB% z;QEEfqm|7iefuJxpP=?m-d@i`@+BV|h$VX&hi}m8>6CUYzFj8aSHVF><@}Gn9xL?d zR3!?=ap%k}xw${Rv6j4$Aq7>~LQZyHlQVqegEtGzIooqNx7t6WG}rF#ticIyS?!tx zl`-$Z%BdrKbj<4`YR0G7ij-0DqK-8r6TXi15HrHX_mhWm+qsF^45E0E2C2HA9VbeB#Hf80ODdmKwJ& zYQkmSi$s-U$_y(y*xfp0JU??{xRoAW?stfy-zJeIMQ$5q5$Td(g-wtCDJ;dqx z><#a*@|4}Y+o}g%y<$`>GDN=5d`_-#G}xcwwTWnc8f;h5Du^Xql*FuO>&Qvm23UcO z3n;4#L9#!pCTZRO$ zxkg~?ZvSYg!lMYoy6pgssNT13r4J!78Y&}?8^hRz!YiNCZFIkIzByYj z=~B=Sm#XMxj-uZu>36b|{;l+|uf}TK?M&SKz2oag-jOrm>v4sXLAYJlgm=(Av*1|0 zB=;7lQl)}rUtDX*6Rv@ZnmLL!eyrSYl+f9~=Fk~d6g|>as4xNc%A5lZV!WIIk!8?vA-KOyy(9)aAA!V>uf7mT=}sO z)+MrDZDqTdvVP)`2=TYK+h9hV7LY0cqsPQh(K7Ho%e{(?rM<%0iBChC`?VP)P$bMl zFNGY_GRg|}zcgx%o-HoY?9Q9$C`1-hi^s#tfrw)EfU;kGwm)s-m3rqwz}s zhV}}tLG6CoY-_1y45#NZ-jsJQ3DQj=MG;%tHEoacN)@xjx540^GD7|F~jbgofr-25QXMrokA=_(w6bjkq;-=&9!>~RNEcH3gTQle^4 zf&DPXw(<`Dt$t-TW8M!owu6e4yp|kG;i}jRu+Y=585(lbS0jcVx!Zm!Zq`zBnu#kU z>?Wu+cM-N^RFDUZCvQiXxv1Y$J19qOm^PWU67w%(@J;eAsFn`4nc#W!O7NZBd?nGJQ2b^OpT&1MKdvMQdMS1;FvlEBD zr`o5x`~J?5M7g6ns-aA-y2$pucox|2=oJH}y(JSXZ}T~O*#7D+_^5mKnsT2Tv$dFg zd7BsI@B5RFCfI4w^xYdJ3&xpmB$t*Zn)u#qtx)|-x_XT!6^0mJbFX$P-H%%FCY@^p zC+lvh4BOruX*KX86QC0TejEFbI-REoGDSGT`lM^Lc>^j&@?5QVthXy88=+`PO z-J>Yu{Su#z`k%&$iqm!3cb5Tz+wXc+U`lmL58AfO6vn3JLF=DM@G~wtiGj-$z{C&E ze-%Rc$e0*7(_iN!wgZ=6YDt*v(e=f>;)~v+kbDoCk!_=8u5c<91wsepQ#Gh4(%^?S zoxQ1yt?ndrpOY^gHhPj#Tc6bB?=$~QeNJgDzfXhxW;Db0;`UY!%C`ife86p@uQKMM(owyM_dW*Wh zQPQG&YA?i90@4Cp4dJ8SaPWRTK3jaNCktx!z?PUs zp!om(v^!IEy8#1#BwnCZ=gaew5h?Eh{ScE_-H6vP-BwNbYxbDQBYzM|%nJ;=?jb=x z5&vbQO&#-F8NOkj^E%0pC+5UV)p#K`YC>z$p^A&5tv+B)5-x;q)%Sb$Q5;af8_6L8 zi7UD^vJ881u{)hod1otps92r2I%ASS5QP*5c|PlI4V1DJ1J{}=Y47~#oRJ-Kf3ZY- zr2*ICR_A>#@RJ6TDM`wXS`jrJ3n_uBEpQxM)~gDmS+cv8AkuX)OYOZXJ+CR+L0)c= z#!$gjv;XB7Noft{_VF36C*^;L+9i;OKQoAV4Q}>Z5178J;LB{qui{H)^Y*RAk5S`k zwGQ+4_t0zP<=Fe36XqUYQ8fIn`LjR1i@q#4g1}SPZH(Zjz??JTi|@asm?=e`VAUaS z4V_QK%DoHrzj#(>(p6823N4_|E&t?eI#hRp(bFt5z1L(e4>Vxi>@EcR zB=RW19J<9<9(sdG29Vv266yP>u>B=vBFj;%ZIqGuot!yA4g|cUL&=1cpqjzT$x6O` zzLgx!teq=qzBs0Muh!w(khq$>2|cNWWihET^e8_?b58~K zvcusY58dK-av)61bTha_Nmn!OwaOQCW#o!?kwlwxXi5vdHoj7s|EeyaIWxv>c-3T`3aH`M`O5kfvJ@;K-Se&_<9 zZeE`+K6nGB4eY5GMCUdZ4%W~=Mh`C~#%b9agvHnz} z^%G@j>+?x&ijcT@dHb)lzQ4Px;ff&tcal-^^ns|ykv=K3M5~7lvtY3E6Iew)MApM9S%>q2A}w;i~kN zBQH3$&h5flwP@k$Juv-XwI^C7-v{JOiZ2JH(mNl=KZwBdkpOJE#a8(SXHzQJ;Rq%3 z4Vp~vq8rkQ3~lma@9R4yAQ34jku#ke_J((k5{_f%Mita4u>lfoMn3chkG?cx5WAcc zJ^1}4X9tPAwwfp8^5^u5{dRLDoUw6Rjci?8cW^&kS>Cw0Fka}7^Q{=>;sy1!NDf&H zwl7LC_3~(O7vpbdYzWEvDl(_fJQ;V?z!F&fh@gY{xRi`X$8p#ro0g%M+EC&l7v07D zASUzejG5<1hHmY*THvh}tZW0LtgF9vDvo%z4|}hI|3)T179I7yq&Hnn*dFGa%A95D zPJkyTFdQeBbb@dVs-g4%?@BByaGo>eSZ|0-`@%bpuSZ7y0!;$YHUhjzr#&rHdfA%$ z-mrG9!j<;|@8S1dvDd46L$>$2Hn8WJJ{=B0pr?g+)s@m4aYP?9+)<V(nX#2KDX>NnBz@nQ`w#ExQO}&e(pmoGlQgD%u04%>~-(#1|db*@L4WtK7Qx1 zQZSbMO}WbGisBdJykeue&)HRXq|=fTKT7)oraoRec)Q76a4%z98xUae&Vr$Z&)=Nd zI3f;No{hvG;5qhcmXuG+-$lBE0#oUI-iV^!ktZ(HaL9erFo#+P0*x{DYLrMwbJPR% zGM)ykHW&`GC4k-0&9!mS zjtGkGGXr(F#K%6pFVwWjFPI-ZTU6#hjTxkFl~V?Wt=(KF$_VPGCY9aiijtZ4;A#6fcl+ zW^uO-i!X;X|E|v?b-)Kc+}cMC6SP=V(!)Fj?#pN%Vyr0OSZW|m>T`^UZX!`S_z-{= zy1lPp?&yvN-@17?CeEWgKFYIegiq*%NZuiSyWFsv7Ug@@ytQ}2!nxHSfzeN1tWUfXaLxvO{gd`D5f_Q;upHECdYY7AC;i}}JlQ1Y2h zY6u8-@U}2w(T+~UF+Q|BBMoR4l|eOQGYOINtbI@O27npq_HxJpds#SKFW7DIPb#gx z#vts{ey?ZeAA4xr5E_F5Ri?5}$W-sn$~!K8I~WWJy?vp5y8hd%^pl}O)XvSj7Q4@6 z0{F9h5F5o>KbQ*wi=X-ckvmzpY}^?3F6~J0dy8tw?f4tZ+}ViJ?4_$&+-JmOgsvs` zeK0}H+^@R;GFVGsE*+J<$(wNN?>=}#zaTamZEWLNN@=mA1oPdmsj2CX@ajSN? z&iLkx#5Ji_nZa|dUs`nB_J-oBUtk2coFcZ#^;;2zKqT!0h>&vnWcV^=O?H!3Jn8s! zjue6TK3{S&3KY>C@DCCtvCY5texFH;_Lz>iIbk_hq%nXEAucYa5P$rL&!%WFCNh$& z_0U&tNpo=>6|k0A(98)`U_2z`rPbG~$$kqk?snE_IG$G4B(B|;{GQ{SZyvhS>3_oc z2X1VWEuA{b6Vm!HUohKQHMvLBRg(rm36VRl71DNpG(svruEymrWQ*rVwhyxmqhw#N za%m3CZ|4AIlMZsTy;3KaG5-8n?WOY9U8T?-Iju{JuYf_^2%wiG4Ujdy?2y5J{A0Ny4r5e0;$AmSic| z&*|d-12p3Fd{Jj`0pBl>`m!9c*-)bNuYmtm^?!z{qYuYS0ew(rAz6nwm#LBERg}T4 G*na}|#V39M literal 0 HcmV?d00001 diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example2.png b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/images/example2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab17b5b0eaefde84c623d82836b870eb30d18223 GIT binary patch literal 54583 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 uxi5s`;cPZ+4v{E^ zVQ%4)m}{JjJOXCPN5!8A(6i&14I4KwQ1k$@()FwA<%aD^NPE0Z4d1&X&KFfSCpvnq z-kB)yXYvR7JmxDM{u-U9{LXdlgts)yN*OFDDA)Nm<1!jW?#co#>&3wOxXzT*46Rkx zt0qIg$qkKi!y4D{?MBfX>mU}sRVuH>i2-*>N5h6!)tTZ16Bz_$Ce>bioBK@<)wK9>>Sok0&mRyM5sr@97#pJWX zT#X~s?bTV;@~7vG{l0g(iHzzJPl#NKw0OH7k2zIajwFqULb@^KJ%Kvb-80x<>}s;! z;-KmGeI5Fp3Qcgoa~2mb!Mp~As-|8%yR;%FB@lkCTKE63hUkTRPtp9Lf1J&DKoSzh z?!ZC)lINFb+lt^6h)DMt?e_NrTmS(6aBJ)XcO$&U_gC4pb|#*>Nj77($?bU)v9;fJ zezT{;Wtf{xCj6d}X2_~fHn!i$13lS$bLQ>*mWeM7m(@@$9_x~@r)hV^<@_UHf{PYQ zIl?}$FIK?5IwJ{6H$HhRr5qhO?=0?d3Y~*IJ&C!LDCLviucBtBXTiy8YTN@Zia?3O z8ab}HBEhwu<8vOxC2lhk!jR=FenwB*``WG|Ihi$mpk&g{VsOu_Ab%%`V|QoOy)EeN zqhX)~A+J*V{IU!^VEyi`$w@udy?0ZKSa@(6b=`oH77pHsV08-sdInQn^X<*`B6wVp zt%HrW*u$4wWzPTnC=sl?h&!P$;Na$L5{!GP5^=32o!lnpK1e~;-HX-4I z1G7(1LZnP{&fA7qUR?lq!;nUpQ2zT%1JZrj7_xV3n|`xZI4(nzx(W&ORy*bHKF%|1 zE;GJjM@?-29)q{FhG%RNux6y!Xd4ZFhDUw2{VHPcV#A}kB#3SN;z;s5v$Eo(r|CE8 zM$&huxtx_tD_I4bY9*TVc0epQ+Sv;aD*bh3KrF%hTzxC+OqxTuk#+^PN&RD$tHihj z#7vLy@GO#Sjw<+~FKj1E`wNWNYq|&$Uv3A}tcU9{trn7AD<*V9&~V4NS}j8 za_0*OXN}SE7?kTuIkt~N%j#IZ+xB1$D6W3$`&m>W)h ze7Zl^DgZfck0~4tg>*uk`kI?9T=NwsRK%3f$NWN;W)lXeWp7WT1!hcw=I%uN zfR8)ZQ_`<;A2kYwS`cL|0PywFW*1$r#ya92c4*uiPCw{JrjZeS3b?HGraQm&{pxfJ zo0Bg9^q%}_-ab_@e>Ju-U~c2_Q|&UwSW!;e0W;uaC);7mb#5XP9{os6KO`=MRp@Ns z8E!DkC$zn;F0QG8&nZQZr*_abTD~FGB^)j3J+gb_BA$W&xAg~7;ujlzGQ>7c@<_oy z4HUPvElJHd@{;E$B|&efi%reoa9shNtnDAK$mF8 zY=nnWEOu`@X!H@1ynNXDyH(Xx$LpT;GXtl1z-d`D?fDg+2V#ozJ4(F4>7qzPCFmc> zM!QRn70ogYW!H+MI$wIhAfptJ6N*CNcbckIg8vi?^EM?jYGwO7{yZ1euu`Sxw+&#y zq33J|3+LeA=qBoe#r0X{ScMbtO;lI=cx_u)0YFW4@!?4$+5?iZ?@1zo+ctzZsQ)Iv zCWE0Y*_yH)2WzFsi${8QF+RfSa8&^u6d~|3?KN_@4mzXsJZpr=z0`;|oGC~Ex8^I( zVZ$HP9tG|MR=()!u0g@dJodkM)ueR!B2BAYOWpB_Zz~4=sZeW7YV~G6u%u%(OyM}9 zc^@)~Dnx-~=1ASb<>`ZL-zqQ%3;lHX?$YX`@68TjUCQI(g8 !od6Q0_aS&%3%Aw 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 literal 0 HcmV?d00001 diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/index.ts new file mode 100644 index 000000000..6e732d35e --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/index.ts @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regardin + * g copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { Behavior, ChartMetadata, ChartPlugin, t } from '@superset-ui/core'; +import buildQuery from './buildQuery'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import thumbnail from './images/thumbnail.png'; +import example1 from './images/example1.png'; +import example2 from './images/example2.png'; +import { HistogramChartProps, HistogramFormData } from './types'; + +export default class EchartsHistogramChartPlugin extends ChartPlugin< + HistogramFormData, + HistogramChartProps +> { + /** + * The constructor is used to pass relevant metadata and callbacks that get + * registered in respective registries that are used throughout the library + * and application. A more thorough description of each property is given in + * the respective imported file. + * + * It is worth noting that `buildQuery` and is optional, and only needed for + * advanced visualizations that require either post processing operations + * (pivoting, rolling aggregations, sorting etc) or submitting multiple queries. + */ + constructor() { + super({ + buildQuery, + controlPanel, + loadChart: () => import('./Histogram'), + metadata: new ChartMetadata({ + behaviors: [Behavior.InteractiveChart], + credits: ['https://echarts.apache.org'], + category: t('Distribution'), + description: t( + `The histogram chart displays the distribution of a dataset by + representing the frequency or count of values within different ranges or bins. + It helps visualize patterns, clusters, and outliers in the data and provides + insights into its shape, central tendency, and spread.`, + ), + exampleGallery: [{ url: example1 }, { url: example2 }], + name: t('Histogram'), + tags: [t('Comparison'), t('ECharts'), t('Pattern'), t('Range')], + thumbnail, + }), + transformProps, + }); + } +} diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/transformProps.ts new file mode 100644 index 000000000..474cd95ba --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/transformProps.ts @@ -0,0 +1,188 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { BarSeriesOption, EChartsOption } from 'echarts'; +import { CallbackDataParams } from 'echarts/types/src/util/types'; +import { isEmpty } from 'lodash'; +import { + CategoricalColorNamespace, + NumberFormats, + getColumnLabel, + getNumberFormatter, + tooltipHtml, +} from '@superset-ui/core'; +import { HistogramChartProps, HistogramTransformedProps } from './types'; +import { LegendOrientation, LegendType, Refs } from '../types'; +import { defaultGrid, defaultYAxis } from '../defaults'; +import { getLegendProps } from '../utils/series'; +import { getDefaultTooltip } from '../utils/tooltip'; +import { getPercentFormatter } from '../utils/formatters'; + +export default function transformProps( + chartProps: HistogramChartProps, +): HistogramTransformedProps { + const refs: Refs = {}; + let focusedSeries: number | undefined; + const { + formData, + height, + hooks, + legendState = {}, + queriesData, + theme, + width, + } = chartProps; + const { onLegendStateChanged } = hooks; + const { + colorScheme, + column, + groupby = [], + normalize, + showLegend, + showValue, + sliceId, + xAxisTitle, + yAxisTitle, + } = formData; + const { data } = queriesData[0]; + const colorFn = CategoricalColorNamespace.getScale(colorScheme); + const formatter = getNumberFormatter( + normalize ? NumberFormats.FLOAT_2_POINT : NumberFormats.INTEGER, + ); + const percentFormatter = getPercentFormatter(NumberFormats.PERCENT_2_POINT); + const groupbySet = new Set(groupby); + const xAxisData: string[] = Object.keys(data[0]).filter( + key => !groupbySet.has(key), + ); + const barSeries: BarSeriesOption[] = data.map(datum => { + const seriesName = + groupby.length > 0 + ? groupby.map(key => datum[getColumnLabel(key)]).join(', ') + : getColumnLabel(column); + const seriesData = Object.keys(datum) + .filter(key => groupbySet.has(key) === false) + .map(key => datum[key] as number); + return { + name: seriesName, + type: 'bar', + data: seriesData, + itemStyle: { + color: colorFn(seriesName, sliceId), + }, + label: { + show: showValue, + position: 'top', + formatter: params => { + const { value } = params; + return formatter.format(value as number); + }, + }, + }; + }); + + const legendOptions = barSeries.map(series => series.name as string); + if (isEmpty(legendState)) { + legendOptions.forEach(legend => { + legendState[legend] = true; + }); + } + + const tooltipFormatter = (params: CallbackDataParams[]) => { + const title = params[0].name; + const rows = params.map(param => { + const { marker, seriesName, value } = param; + return [`${marker}${seriesName}`, formatter.format(value as number)]; + }); + if (groupby.length > 0) { + const total = params.reduce( + (acc, param) => acc + (param.value as number), + 0, + ); + if (!normalize) { + rows.forEach((row, i) => + row.push( + percentFormatter.format((params[i].value as number) / (total || 1)), + ), + ); + } + const totalRow = ['Total', formatter.format(total)]; + if (!normalize) { + totalRow.push(percentFormatter.format(1)); + } + rows.push(totalRow); + } + return tooltipHtml(rows, title, focusedSeries); + }; + + const onFocusedSeries = (index?: number | undefined) => { + focusedSeries = index; + }; + + const echartOptions: EChartsOption = { + grid: { + ...defaultGrid, + bottom: 30, + left: 30, + right: 30, + }, + xAxis: { + data: xAxisData, + name: xAxisTitle, + nameGap: 35, + type: 'category', + nameLocation: 'middle', + }, + yAxis: { + ...defaultYAxis, + name: yAxisTitle, + nameGap: normalize ? 55 : 40, + type: 'value', + nameLocation: 'middle', + axisLabel: { + formatter: (value: number) => formatter.format(value), + }, + }, + series: barSeries, + legend: { + ...getLegendProps( + LegendType.Scroll, + LegendOrientation.Top, + showLegend, + theme, + false, + legendState, + ), + data: legendOptions, + }, + tooltip: { + ...getDefaultTooltip(refs), + trigger: 'axis', + formatter: tooltipFormatter, + }, + }; + + return { + refs, + formData, + width, + height, + echartOptions, + onFocusedSeries, + onLegendStateChanged, + }; +} diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/types.ts new file mode 100644 index 000000000..ca6c16d79 --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Histogram/types.ts @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { QueryFormColumn, QueryFormData } from '@superset-ui/core'; +import { BaseChartProps, BaseTransformedProps } from '../types'; + +export type HistogramFormData = QueryFormData & { + bins: number; + column: QueryFormColumn; + colorScheme?: string; + cumulative: boolean; + normalize: boolean; + sliceId: number; + showLegend: boolean; + showValue: boolean; + xAxisTitle: string; + yAxisTitle: string; +}; + +export interface HistogramChartProps extends BaseChartProps { + formData: HistogramFormData; +} + +export type HistogramTransformedProps = + BaseTransformedProps & { + onFocusedSeries: (index: number | undefined) => void; + }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx index c91d27acc..3e38b480c 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx @@ -34,7 +34,7 @@ import { defaultXAxis } from './defaults'; const { legendMargin, legendOrientation, legendType, showLegend } = DEFAULT_LEGEND_FORM_DATA; -const showLegendControl: ControlSetItem = { +export const showLegendControl: ControlSetItem = { name: 'show_legend', config: { type: 'CheckboxControl', diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/index.ts index f3bee6209..36290ec45 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/index.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/index.ts @@ -28,6 +28,7 @@ export { default as EchartsMixedTimeseriesChartPlugin } from './MixedTimeseries' export { default as EchartsPieChartPlugin } from './Pie'; export { default as EchartsGraphChartPlugin } from './Graph'; export { default as EchartsGaugeChartPlugin } from './Gauge'; +export { default as EchartsHistogramChartPlugin } from './Histogram'; export { default as EchartsRadarChartPlugin } from './Radar'; export { default as EchartsFunnelChartPlugin } from './Funnel'; export { default as EchartsTreeChartPlugin } from './Tree'; @@ -56,6 +57,7 @@ export { default as HeatmapTransformProps } from './Heatmap/transformProps'; export { default as SunburstTransformProps } from './Sunburst/transformProps'; export { default as BubbleTransformProps } from './Bubble/transformProps'; export { default as WaterfallTransformProps } from './Waterfall/transformProps'; +export { default as HistogramTransformProps } from './Histogram/transformProps'; export { DEFAULT_FORM_DATA as TimeseriesDefaultFormData } from './Timeseries/constants'; diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx index fe5d27724..ff4209396 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx @@ -31,7 +31,7 @@ import { useComponentDidMount, usePrevious, } from '@superset-ui/core'; -import { debounce, omit, pick } from 'lodash'; +import { debounce, isEqual, isObjectLike, omit, pick } from 'lodash'; import { Resizable } from 're-resizable'; import { usePluginContext } from 'src/components/DynamicPlugins'; import { Global } from '@emotion/react'; @@ -460,15 +460,21 @@ function ExploreViewContainer(props) { const chartIsStale = useMemo(() => { if (lastQueriedControls) { - const changedControlKeys = Object.keys(props.controls).filter( - key => - typeof lastQueriedControls[key] !== 'undefined' && - !areObjectsEqual( - props.controls[key].value, - lastQueriedControls[key].value, - { ignoreFields: ['datasourceWarning'] }, - ), - ); + const { controls } = props; + const changedControlKeys = Object.keys(controls).filter(key => { + const lastControl = lastQueriedControls[key]; + if (typeof lastControl === 'undefined') { + return false; + } + const { value: value1 } = controls[key]; + const { value: value2 } = lastControl; + if (isObjectLike(value1) && isObjectLike(value2)) { + return !areObjectsEqual(value1, value2, { + ignoreFields: ['datasourceWarning'], + }); + } + return !isEqual(value1, value2); + }); return changedControlKeys.some( key => diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx index 02d5c47b0..4644f97af 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx @@ -74,6 +74,7 @@ interface ColumnSelectPopoverProps { label: string; isTemporal?: boolean; setDatasetModal?: Dispatch>; + disabledTabs?: Set; } const getInitialColumnValues = ( @@ -102,6 +103,7 @@ const ColumnSelectPopover = ({ onClose, setDatasetModal, setLabel, + disabledTabs = new Set<'saved' | 'simple' | 'sqlExpression'>(), }: ColumnSelectPopoverProps) => { const datasourceType = useSelector( state => state.explore.datasource.type, @@ -299,7 +301,11 @@ const ColumnSelectPopover = ({ width: ${width}px; `} > - + {calculatedColumns.length > 0 ? ( )} - + {isTemporal && simpleColumns.length === 0 ? ( - + void; children: React.ReactNode; isTemporal?: boolean; + disabledTabs?: Set; } const defaultPopoverLabel = t('My column'); @@ -48,6 +49,7 @@ const ColumnSelectPopoverTrigger = ({ isControlledComponent, children, isTemporal, + disabledTabs, ...props }: ColumnSelectPopoverTriggerProps) => { // @ts-ignore @@ -108,6 +110,7 @@ const ColumnSelectPopoverTrigger = ({ setLabel={setPopoverLabel} getCurrentTab={getCurrentTab} isTemporal={isTemporal} + disabledTabs={disabledTabs} /> ), diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx index ef6d39486..7ee006c83 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx @@ -37,6 +37,7 @@ import { DndControlProps } from './types'; export type DndColumnSelectProps = DndControlProps & { options: ColumnMeta[]; isTemporal?: boolean; + disabledTabs?: Set; }; function DndColumnSelect(props: DndColumnSelectProps) { @@ -50,6 +51,7 @@ function DndColumnSelect(props: DndColumnSelectProps) { name, label, isTemporal, + disabledTabs, } = props; const [newColumnPopoverVisible, setNewColumnPopoverVisible] = useState(false); @@ -121,6 +123,7 @@ function DndColumnSelect(props: DndColumnSelectProps) { }} editedColumn={column} isTemporal={isTemporal} + disabledTabs={disabledTabs} > DataFrame: + """ + Generate a histogram DataFrame from a given DataFrame. + + Parameters: + df (DataFrame): The input DataFrame. + column (str): The column of the DataFrame to calculate the histogram on. + groupby (list[str]): The columns to group by. If empty, no grouping is performed. + bins (int): The number of bins to use for the histogram. Default is 5. + cumulative (bool): Whether to calculate a cumulative histogram. Default is False. + normalize (bool): Whether to normalize the histogram. Default is False. + + Returns: + DataFrame: A DataFrame where each row corresponds to a group (or the entire DataFrame if no grouping is performed), + and each column corresponds to a histogram bin. The values are the counts in each bin. + """ + + if groupby is None: + groupby = [] + + # check if the column is numeric + if not np.issubdtype(df[column].dtype, np.number): + raise ValueError(f"The column '{column}' must be numeric.") + + # calculate the histogram bin edges + bin_edges = np.histogram_bin_edges(df[column], bins=bins) + + # convert the bin edges to strings + bin_edges_str = [ + f"{int(bin_edges[i])} - {int(bin_edges[i+1])}" + for i in range(len(bin_edges) - 1) + ] + + def hist_values(series: Series) -> np.ndarray: + result = np.histogram(series, bins=bin_edges)[0] + return result if not cumulative else np.cumsum(result) + + if len(groupby) == 0: + # without grouping + hist_dict = dict(zip(bin_edges_str, hist_values(df[column]))) + histogram_df = DataFrame(hist_dict, index=[0]) + else: + # with grouping + histogram_df = ( + df.groupby(groupby)[column] + .apply(lambda x: Series(hist_values(x))) + .unstack(fill_value=0) + ) + histogram_df.columns = bin_edges_str + + if normalize: + histogram_df = histogram_df / histogram_df.values.sum() + + # reorder the columns to have the groupby columns first + histogram_df = histogram_df.reset_index().loc[:, groupby + bin_edges_str] + + return histogram_df diff --git a/tests/unit_tests/pandas_postprocessing/test_histogram.py b/tests/unit_tests/pandas_postprocessing/test_histogram.py new file mode 100644 index 000000000..4e91a239a --- /dev/null +++ b/tests/unit_tests/pandas_postprocessing/test_histogram.py @@ -0,0 +1,122 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from pandas import DataFrame + +from superset.utils.pandas_postprocessing import histogram + +data = DataFrame( + { + "group": ["A", "A", "B", "B", "A", "A", "B", "B", "A", "A"], + "a": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "b": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + } +) + +bins = 5 + + +def test_histogram_no_groupby(): + data_with_no_groupings = DataFrame( + {"a": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "b": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]} + ) + result = histogram(data_with_no_groupings, "a", [], bins) + assert result.shape == (1, bins) + assert result.columns.tolist() == ["1 - 2", "2 - 4", "4 - 6", "6 - 8", "8 - 10"] + assert result.values.tolist() == [[2, 2, 2, 2, 2]] + + +def test_histogram_with_groupby(): + result = histogram(data, "a", ["group"], bins) + assert result.shape == (2, bins + 1) + assert result.columns.tolist() == [ + "group", + "1 - 2", + "2 - 4", + "4 - 6", + "6 - 8", + "8 - 10", + ] + assert result.values.tolist() == [["A", 2, 0, 2, 0, 2], ["B", 0, 2, 0, 2, 0]] + + +def test_histogram_with_groupby_and_normalize(): + result = histogram(data, "a", ["group"], bins, normalize=True) + assert result.shape == (2, bins + 1) + assert result.columns.tolist() == [ + "group", + "1 - 2", + "2 - 4", + "4 - 6", + "6 - 8", + "8 - 10", + ] + assert result.values.tolist() == [ + ["A", 0.2, 0.0, 0.2, 0.0, 0.2], + ["B", 0.0, 0.2, 0.0, 0.2, 0.0], + ] + + +def test_histogram_with_groupby_and_cumulative(): + result = histogram(data, "a", ["group"], bins, cumulative=True) + assert result.shape == (2, bins + 1) + assert result.columns.tolist() == [ + "group", + "1 - 2", + "2 - 4", + "4 - 6", + "6 - 8", + "8 - 10", + ] + assert result.values.tolist() == [["A", 2, 2, 4, 4, 6], ["B", 0, 2, 2, 4, 4]] + + +def test_histogram_with_groupby_and_cumulative_and_normalize(): + result = histogram(data, "a", ["group"], bins, cumulative=True, normalize=True) + assert result.shape == (2, bins + 1) + assert result.columns.tolist() == [ + "group", + "1 - 2", + "2 - 4", + "4 - 6", + "6 - 8", + "8 - 10", + ] + assert result.values.tolist() == [ + [ + "A", + 0.06666666666666667, + 0.06666666666666667, + 0.13333333333333333, + 0.13333333333333333, + 0.2, + ], + [ + "B", + 0.0, + 0.06666666666666667, + 0.06666666666666667, + 0.13333333333333333, + 0.13333333333333333, + ], + ] + + +def test_histogram_with_non_numeric_column(): + try: + histogram(data, "b", ["group"], bins) + except ValueError as e: + assert str(e) == "The column 'b' must be numeric."