Skip to content

Commit 8a5c596

Browse files
committed
feat(dashboard): render regression-info with group_by_key
1 parent 35b3adc commit 8a5c596

File tree

2 files changed

+125
-49
lines changed
  • dashboard/src/plugins/viz-components/regression-chart/toolbox/regression-description

2 files changed

+125
-49
lines changed

dashboard/src/plugins/viz-components/regression-chart/toolbox/regression-description/desc.tsx

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import { IRegressionChartConf } from '../../type';
33
// @ts-expect-error type lib for d3-regression
44
import * as d3Regression from 'd3-regression';
55
import numbro from 'numbro';
6+
import _ from 'lodash';
7+
import { ReactNode } from 'react';
8+
9+
export type TDescription = {
10+
name: string;
11+
expression: ReactNode;
12+
rSquared: number;
13+
adjustedRSquared: number;
14+
};
615

716
/**
817
* calculate Adjusted RSquared
@@ -15,11 +24,17 @@ function calculateAdjustedRSquared(r: number, n: number, k: number) {
1524
return 1 - ((1 - r) * (n - 1)) / (n - k - 1);
1625
}
1726

18-
function getLinearDescription(rawData: TVizData, basisData: [number, number][], conf: IRegressionChartConf) {
27+
function getLinearDescription(
28+
name: string,
29+
rawData: TVizData,
30+
basisData: [number, number][],
31+
conf: IRegressionChartConf,
32+
): TDescription {
1933
const { x_axis, y_axis } = conf;
2034
const result = d3Regression.regressionLinear()(basisData);
2135
const { a, b, rSquared } = result;
2236
return {
37+
name,
2338
expression: (
2439
<Group position="center" noWrap spacing={10}>
2540
<Text>{y_axis.name}</Text>
@@ -40,10 +55,16 @@ function getLinearDescription(rawData: TVizData, basisData: [number, number][],
4055
};
4156
}
4257

43-
function getExponentialDescription(rawData: TVizData, basisData: [number, number][], conf: IRegressionChartConf) {
58+
function getExponentialDescription(
59+
name: string,
60+
rawData: TVizData,
61+
basisData: [number, number][],
62+
conf: IRegressionChartConf,
63+
): TDescription {
4464
const { x_axis, y_axis } = conf;
4565
const { a, b, rSquared } = d3Regression.regressionExp()(basisData);
4666
return {
67+
name,
4768
expression: (
4869
<Group position="center" noWrap spacing={10}>
4970
<Text>{y_axis.name}</Text>
@@ -68,10 +89,16 @@ function getExponentialDescription(rawData: TVizData, basisData: [number, number
6889
};
6990
}
7091

71-
function getLogarithmicDescription(rawData: TVizData, basisData: [number, number][], conf: IRegressionChartConf) {
92+
function getLogarithmicDescription(
93+
name: string,
94+
rawData: TVizData,
95+
basisData: [number, number][],
96+
conf: IRegressionChartConf,
97+
): TDescription {
7298
const { x_axis, y_axis } = conf;
7399
const { a, b, rSquared } = d3Regression.regressionLog()(basisData);
74100
return {
101+
name,
75102
expression: (
76103
<Group position="center" noWrap spacing={10}>
77104
<Text>{y_axis.name}</Text>
@@ -96,45 +123,66 @@ function getLogarithmicDescription(rawData: TVizData, basisData: [number, number
96123
};
97124
}
98125

99-
function getPolynomialDescription(rawData: TVizData, basisData: [number, number][], conf: IRegressionChartConf) {
126+
function getPolynomialDescription(
127+
name: string,
128+
rawData: TVizData,
129+
basisData: [number, number][],
130+
conf: IRegressionChartConf,
131+
): TDescription {
100132
const { x_axis, y_axis, regression } = conf;
101133
const result = d3Regression.regressionPoly().order(regression.transform.config.order)(basisData);
102134
const { rSquared } = result;
103135
console.log(result);
104136
return {
137+
name,
105138
expression: '',
106139
rSquared,
107140
adjustedRSquared: calculateAdjustedRSquared(rSquared, rawData.length, 1),
108141
};
109142
}
110143

111-
export function getRegressionDescription(data: TVizData, conf?: IRegressionChartConf) {
112-
if (!conf) {
113-
return {
114-
expression: '',
115-
rSquared: 0,
116-
adjustedRSquared: 0,
117-
};
118-
}
119-
const { regression, x_axis, y_axis } = conf;
120-
const dataSource: [number, number][] = data.map((d) => [d[x_axis.data_key], d[regression.y_axis_data_key]]);
144+
function getDescription(name: string, rawData: TVizData, conf: IRegressionChartConf): TDescription {
145+
const { regression, x_axis } = conf;
146+
const dataSource: [number, number][] = rawData.map((d) => [d[x_axis.data_key], d[regression.y_axis_data_key]]);
121147

122148
if (regression.transform.config.method === 'linear') {
123-
return getLinearDescription(data, dataSource, conf);
149+
return getLinearDescription(name, rawData, dataSource, conf);
124150
}
125151
if (regression.transform.config.method === 'exponential') {
126-
return getExponentialDescription(data, dataSource, conf);
152+
return getExponentialDescription(name, rawData, dataSource, conf);
127153
}
128154

129155
if (regression.transform.config.method === 'logarithmic') {
130-
return getLogarithmicDescription(data, dataSource, conf);
156+
return getLogarithmicDescription(name, rawData, dataSource, conf);
131157
}
132158
if (regression.transform.config.method === 'polynomial') {
133-
return getPolynomialDescription(data, dataSource, conf);
159+
return getPolynomialDescription(name, rawData, dataSource, conf);
134160
}
135161
return {
162+
name,
136163
expression: '',
137164
rSquared: 0,
138165
adjustedRSquared: 0,
139166
};
140167
}
168+
169+
export function getRegressionDescription(data: TVizData, conf?: IRegressionChartConf): TDescription[] {
170+
if (!conf) {
171+
return [
172+
{
173+
name: '',
174+
expression: '',
175+
rSquared: 0,
176+
adjustedRSquared: 0,
177+
},
178+
];
179+
}
180+
if (!conf.regression.group_by_key) {
181+
return [getDescription('', data, conf)];
182+
}
183+
184+
const groupedData = _.groupBy(data, conf.regression.group_by_key);
185+
return Object.entries(groupedData).map(([group, subData]) => {
186+
return getDescription(group, subData, conf);
187+
});
188+
}

dashboard/src/plugins/viz-components/regression-chart/toolbox/regression-description/index.tsx

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { Button, HoverCard, Sx, Table, Text } from '@mantine/core';
1+
import { Button, HoverCard, Sx, Table, Tabs, Text } from '@mantine/core';
22
import { IconInfoCircle } from '@tabler/icons';
33
import { IRegressionChartConf } from '../../type';
44
import numbro from 'numbro';
55
import { useMemo } from 'react';
6-
import { getRegressionDescription } from './desc';
6+
import { TDescription, getRegressionDescription } from './desc';
7+
import { ErrorBoundary } from '~/utils/error-boundary';
78

89
const TableSx: Sx = {
10+
marginTop: '10px',
911
'tbody th, tbody td': {
1012
padding: '7px 10px',
1113
},
@@ -14,16 +16,64 @@ const TableSx: Sx = {
1416
},
1517
};
1618

19+
function DescriptionContent({ desc }: { desc: TDescription }) {
20+
const { expression, rSquared, adjustedRSquared } = desc;
21+
if (!expression) {
22+
return <Text>Unavailable for this regression method</Text>;
23+
}
24+
return (
25+
<Table fontSize={14} sx={TableSx}>
26+
<tbody>
27+
<tr>
28+
<td colSpan={2}>
29+
<Text align="center">{expression}</Text>
30+
</td>
31+
</tr>
32+
<tr>
33+
<td>R-Sq</td>
34+
<td style={{ textAlign: 'right' }}>{numbro(rSquared).format({ output: 'percent', mantissa: 1 })}</td>
35+
</tr>
36+
<tr>
37+
<td>R-Sq(Adjusted)</td>
38+
<td style={{ textAlign: 'right' }}>{numbro(adjustedRSquared).format({ output: 'percent', mantissa: 1 })}</td>
39+
</tr>
40+
</tbody>
41+
</Table>
42+
);
43+
}
44+
1745
export interface IRegressionDescription {
1846
conf: IRegressionChartConf;
1947
data: TVizData;
2048
}
2149

22-
export function RegressionDescription({ conf, data }: IRegressionDescription) {
23-
const { expression, rSquared, adjustedRSquared } = useMemo(() => {
24-
return getRegressionDescription(data, conf);
25-
}, [conf, data]);
50+
function DescriptionInTabs({ conf, data }: IRegressionDescription) {
51+
const desc = useMemo(() => getRegressionDescription(data, conf), [conf, data]);
52+
53+
if (!conf.regression.group_by_key) {
54+
return <DescriptionContent desc={desc[0]} />;
55+
}
56+
console.log(desc);
2657

58+
return (
59+
<Tabs defaultValue={desc[0]?.name} color="gray">
60+
<Tabs.List grow>
61+
{desc.map((item) => (
62+
<Tabs.Tab key={item.name} value={item.name}>
63+
{item.name}
64+
</Tabs.Tab>
65+
))}
66+
</Tabs.List>
67+
{desc.map((item) => (
68+
<Tabs.Panel key={item.name} value={item.name}>
69+
<DescriptionContent desc={item} />
70+
</Tabs.Panel>
71+
))}
72+
</Tabs>
73+
);
74+
}
75+
76+
export function RegressionDescription({ conf, data }: IRegressionDescription) {
2777
return (
2878
<HoverCard shadow="md" withinPortal zIndex={320}>
2979
<HoverCard.Target>
@@ -32,31 +82,9 @@ export function RegressionDescription({ conf, data }: IRegressionDescription) {
3282
</Button>
3383
</HoverCard.Target>
3484
<HoverCard.Dropdown>
35-
<Table fontSize={14} sx={TableSx}>
36-
<tbody>
37-
{expression && (
38-
<tr>
39-
<td colSpan={2}>
40-
<Text align="center">{expression}</Text>
41-
</td>
42-
</tr>
43-
)}
44-
{rSquared && (
45-
<>
46-
<tr>
47-
<td>R-Sq</td>
48-
<td style={{ textAlign: 'right' }}>{numbro(rSquared).format({ output: 'percent', mantissa: 1 })}</td>
49-
</tr>
50-
<tr>
51-
<td>R-Sq(Adjusted)</td>
52-
<td style={{ textAlign: 'right' }}>
53-
{numbro(adjustedRSquared).format({ output: 'percent', mantissa: 1 })}
54-
</td>
55-
</tr>
56-
</>
57-
)}
58-
</tbody>
59-
</Table>
85+
<ErrorBoundary>
86+
<DescriptionInTabs conf={conf} data={data} />
87+
</ErrorBoundary>
6088
</HoverCard.Dropdown>
6189
</HoverCard>
6290
);

0 commit comments

Comments
 (0)