@kanaries/graphic-walker 0.2.16 → 0.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +3 -4
- package/dist/assets/viewQuery.worker-ffefc111.js.map +1 -1
- package/dist/dataSource/index.d.ts +0 -1
- package/dist/fields/encodeFields/singleEncodeEditor.d.ts +4 -4
- package/dist/graphic-walker.es.js +29716 -34642
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +218 -256
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/interfaces.d.ts +31 -16
- package/dist/lib/inferMeta.d.ts +2 -9
- package/dist/lib/insights/explainByChildren.d.ts +16 -0
- package/dist/lib/insights/explainBySelection.d.ts +5 -0
- package/dist/lib/insights/explainValue.d.ts +2 -0
- package/dist/lib/insights/utils.d.ts +11 -0
- package/dist/lib/interfaces.d.ts +2 -1
- package/dist/lib/op/aggregate.d.ts +1 -1
- package/dist/lib/op/bin.d.ts +1 -1
- package/dist/lib/op/fold.d.ts +1 -1
- package/dist/lib/viewQuery.d.ts +1 -2
- package/dist/services.d.ts +1 -30
- package/dist/store/visualSpecStore.d.ts +1 -2
- package/dist/utils/autoMark.d.ts +1 -1
- package/dist/utils/dataPrep.d.ts +1 -2
- package/dist/vis/react-vega.d.ts +1 -0
- package/dist/vis/spec/encode.d.ts +1 -0
- package/dist/vis/spec/mark.d.ts +1 -1
- package/dist/vis/spec/tooltip.d.ts +4 -0
- package/dist/vis/spec/view.d.ts +9 -3
- package/package.json +2 -4
- package/src/App.tsx +55 -68
- package/src/dataSource/index.tsx +0 -17
- package/src/fields/aestheticFields.tsx +1 -2
- package/src/fields/encodeFields/singleEncodeEditor.tsx +11 -12
- package/src/fields/fieldsContext.tsx +1 -0
- package/src/interfaces.ts +75 -63
- package/src/lib/inferMeta.ts +26 -29
- package/src/lib/insights/explainByChildren.ts +50 -0
- package/src/lib/insights/explainBySelection.ts +47 -0
- package/src/lib/insights/explainValue.ts +30 -0
- package/src/lib/insights/utils.ts +21 -0
- package/src/lib/interfaces.ts +3 -9
- package/src/lib/op/aggregate.ts +1 -1
- package/src/lib/op/bin.ts +1 -1
- package/src/lib/op/fold.ts +1 -1
- package/src/lib/viewQuery.ts +1 -2
- package/src/locales/en-US.json +2 -1
- package/src/locales/zh-CN.json +2 -1
- package/src/renderer/specRenderer.tsx +2 -0
- package/src/services.ts +65 -67
- package/src/store/visualSpecStore.ts +2 -4
- package/src/utils/autoMark.ts +1 -1
- package/src/utils/dataPrep.ts +1 -2
- package/src/vis/react-vega.tsx +5 -0
- package/src/vis/spec/encode.ts +1 -0
- package/src/vis/spec/mark.ts +1 -1
- package/src/vis/spec/tooltip.ts +16 -0
- package/src/vis/spec/view.ts +12 -14
- package/dist/assets/explainer.worker-8428eb12.js.map +0 -1
- package/dist/insightBoard/index.d.ts +0 -3
- package/dist/insightBoard/mainBoard.d.ts +0 -11
- package/dist/insightBoard/radioGroupButtons.d.ts +0 -12
- package/dist/insightBoard/selectionSpec.d.ts +0 -13
- package/dist/insightBoard/std2vegaSpec.d.ts +0 -12
- package/dist/insightBoard/utils.d.ts +0 -8
- package/dist/insights.d.ts +0 -61
- package/src/insightBoard/index.tsx +0 -31
- package/src/insightBoard/mainBoard.tsx +0 -224
- package/src/insightBoard/radioGroupButtons.tsx +0 -57
- package/src/insightBoard/selectionSpec.ts +0 -113
- package/src/insightBoard/std2vegaSpec.ts +0 -184
- package/src/insightBoard/utils.ts +0 -32
- package/src/insights.ts +0 -408
- package/src/workers/explainer.worker.js +0 -76
package/src/interfaces.ts
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import { StatFuncName } from "visual-insights/build/esm/statistics";
|
|
2
|
-
import { AggFC } from 'cube-core/built/types';
|
|
3
|
-
import { IAnalyticType, ISemanticType } from 'visual-insights';
|
|
4
|
-
|
|
5
1
|
export type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
6
2
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
7
3
|
};
|
|
4
|
+
export type ISemanticType = 'quantitative' | 'nominal' | 'ordinal' | 'temporal';
|
|
5
|
+
export type IDataType = 'number' | 'integer' | 'boolean' | 'date' | 'string';
|
|
6
|
+
export type IAnalyticType = 'dimension' | 'measure';
|
|
8
7
|
|
|
9
8
|
export interface IRow {
|
|
10
9
|
[key: string]: any;
|
|
11
10
|
}
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated
|
|
14
|
-
*/
|
|
15
|
-
export type SemanticType = 'quantitative' | 'nominal' | 'ordinal' | 'temporal';
|
|
16
11
|
|
|
12
|
+
export type IAggregator = 'sum' | 'count' | 'max' | 'min' | 'mean' | 'median' | 'variance' | 'stdev';
|
|
13
|
+
export interface Specification {
|
|
14
|
+
position?: string[];
|
|
15
|
+
color?: string[];
|
|
16
|
+
size?: string[];
|
|
17
|
+
shape?: string[];
|
|
18
|
+
opacity?: string[];
|
|
19
|
+
facets?: string[];
|
|
20
|
+
page?: string[];
|
|
21
|
+
filter?: string[];
|
|
22
|
+
highFacets?: string[];
|
|
23
|
+
geomType?: string[];
|
|
24
|
+
aggregate?: boolean;
|
|
25
|
+
}
|
|
17
26
|
export interface Filters {
|
|
18
27
|
[key: string]: any[];
|
|
19
28
|
}
|
|
@@ -25,7 +34,7 @@ export interface IMutField {
|
|
|
25
34
|
disable?: boolean;
|
|
26
35
|
semanticType: ISemanticType;
|
|
27
36
|
analyticType: IAnalyticType;
|
|
28
|
-
}
|
|
37
|
+
}
|
|
29
38
|
|
|
30
39
|
export interface IUncertainMutField {
|
|
31
40
|
fid: string;
|
|
@@ -36,21 +45,23 @@ export interface IUncertainMutField {
|
|
|
36
45
|
analyticType: IAnalyticType | '?';
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
export type IExpParamter =
|
|
49
|
+
| {
|
|
50
|
+
type: 'field';
|
|
51
|
+
value: string;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
type: 'value';
|
|
55
|
+
value: any;
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
type: 'expression';
|
|
59
|
+
value: IExpression;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
type: 'constant';
|
|
63
|
+
value: any;
|
|
64
|
+
};
|
|
54
65
|
|
|
55
66
|
export interface IExpression {
|
|
56
67
|
op: 'bin' | 'log2' | 'log10' | 'one' | 'binCount';
|
|
@@ -83,13 +94,6 @@ export interface IViewField extends IField {
|
|
|
83
94
|
sort?: 'none' | 'ascending' | 'descending';
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
export interface Measure extends IField {
|
|
87
|
-
aggregator?: AggFC;
|
|
88
|
-
minWidth?: number;
|
|
89
|
-
formatter?: (value: number | undefined) => number | string;
|
|
90
|
-
[key: string]: any;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
97
|
export interface DataSet {
|
|
94
98
|
id: string;
|
|
95
99
|
name: string;
|
|
@@ -104,7 +108,20 @@ export interface IFieldNeighbor {
|
|
|
104
108
|
|
|
105
109
|
export interface IMeasure {
|
|
106
110
|
key: string;
|
|
107
|
-
op:
|
|
111
|
+
op: IAggregator;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface IPredicate {
|
|
115
|
+
key: string;
|
|
116
|
+
type: "discrete" | "continuous";
|
|
117
|
+
range: Set<any> | [number, number];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface IExplainProps {
|
|
121
|
+
dataSource: IRow[];
|
|
122
|
+
predicates: IPredicate[];
|
|
123
|
+
viewFields: IField[];
|
|
124
|
+
metas: IField[];
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
export interface IDataSet {
|
|
@@ -119,12 +136,12 @@ export interface IDataSet {
|
|
|
119
136
|
export interface IDataSetInfo {
|
|
120
137
|
name: string;
|
|
121
138
|
rawFields: IMutField[];
|
|
122
|
-
dataSource: IRow[]
|
|
139
|
+
dataSource: IRow[];
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
export interface IDataSource {
|
|
126
143
|
id: string;
|
|
127
|
-
data: IRow[]
|
|
144
|
+
data: IRow[];
|
|
128
145
|
}
|
|
129
146
|
|
|
130
147
|
export interface IFilterField extends IViewField {
|
|
@@ -149,39 +166,34 @@ export interface DraggableFieldState {
|
|
|
149
166
|
|
|
150
167
|
export interface IDraggableStateKey {
|
|
151
168
|
id: keyof DraggableFieldState;
|
|
152
|
-
mode: number
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export type IFilterRule = {
|
|
156
|
-
type: 'range';
|
|
157
|
-
value: readonly [number, number];
|
|
158
|
-
} | {
|
|
159
|
-
type: 'temporal range';
|
|
160
|
-
value: readonly [number, number];
|
|
161
|
-
} | {
|
|
162
|
-
type: 'one of';
|
|
163
|
-
value: Set<string | number>;
|
|
164
|
-
};
|
|
169
|
+
mode: number;
|
|
170
|
+
}
|
|
165
171
|
|
|
166
|
-
export
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
export type IFilterRule =
|
|
173
|
+
| {
|
|
174
|
+
type: 'range';
|
|
175
|
+
value: readonly [number, number];
|
|
176
|
+
}
|
|
177
|
+
| {
|
|
178
|
+
type: 'temporal range';
|
|
179
|
+
value: readonly [number, number];
|
|
180
|
+
}
|
|
181
|
+
| {
|
|
182
|
+
type: 'one of';
|
|
183
|
+
value: Set<string | number>;
|
|
184
|
+
};
|
|
171
185
|
|
|
172
|
-
export const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'y',
|
|
176
|
-
] as const;
|
|
186
|
+
export const EXPLORATION_TYPES = ['none', 'brush', 'point'] as const;
|
|
187
|
+
|
|
188
|
+
export const BRUSH_DIRECTIONS = ['default', 'x', 'y'] as const;
|
|
177
189
|
|
|
178
190
|
export type IStackMode = 'none' | 'stack' | 'normalize';
|
|
179
|
-
export type IExplorationType =
|
|
180
|
-
export type IBrushDirection =
|
|
191
|
+
export type IExplorationType = typeof EXPLORATION_TYPES[number];
|
|
192
|
+
export type IBrushDirection = typeof BRUSH_DIRECTIONS[number];
|
|
181
193
|
|
|
182
194
|
export interface IVisualConfig {
|
|
183
195
|
defaultAggregated: boolean;
|
|
184
|
-
geoms:
|
|
196
|
+
geoms: string[];
|
|
185
197
|
stack: IStackMode;
|
|
186
198
|
showActions: boolean;
|
|
187
199
|
interactiveScale: boolean;
|
|
@@ -190,7 +202,7 @@ export interface IVisualConfig {
|
|
|
190
202
|
mode: 'auto' | 'fixed';
|
|
191
203
|
width: number;
|
|
192
204
|
height: number;
|
|
193
|
-
}
|
|
205
|
+
};
|
|
194
206
|
exploration: {
|
|
195
207
|
mode: IExplorationType;
|
|
196
208
|
/** works when mode is 'brush' */
|
|
@@ -207,8 +219,8 @@ export interface IVisSpec {
|
|
|
207
219
|
|
|
208
220
|
export enum ISegmentKey {
|
|
209
221
|
vis = 'vis',
|
|
210
|
-
data = 'data'
|
|
222
|
+
data = 'data',
|
|
211
223
|
}
|
|
212
224
|
|
|
213
225
|
export type IThemeKey = 'vega' | 'g2';
|
|
214
|
-
export type IDarkMode = 'media' | 'light' | 'dark';
|
|
226
|
+
export type IDarkMode = 'media' | 'light' | 'dark';
|
package/src/lib/inferMeta.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { IAnalyticType, ISemanticType,
|
|
2
|
-
import { IMutField, IRow, IUncertainMutField } from "../interfaces";
|
|
1
|
+
import { IAnalyticType, IMutField, IRow, ISemanticType, IUncertainMutField } from '../interfaces';
|
|
3
2
|
|
|
4
3
|
const COMMON_TIME_FORMAT: RegExp[] = [
|
|
5
4
|
/^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
|
|
@@ -8,13 +7,13 @@ const COMMON_TIME_FORMAT: RegExp[] = [
|
|
|
8
7
|
/^\d{4}\/\d{2}\/\d{2}$/, // YYYY/MM/DD
|
|
9
8
|
/^\d{4}\.\d{2}\.\d{2}$/, // YYYY.MM.DD
|
|
10
9
|
/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/, // YYYY-MM-DD HH:MM:SS
|
|
11
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}
|
|
10
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/, // YYYY-MM-DDTHH:MM:SS (ISO-8601)
|
|
12
11
|
];
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* check if this array is a date time array based on some common date format
|
|
16
15
|
* @param data string array
|
|
17
|
-
* @returns
|
|
16
|
+
* @returns
|
|
18
17
|
*/
|
|
19
18
|
export function isDateTimeArray(data: string[]): boolean {
|
|
20
19
|
let isDateTime = true;
|
|
@@ -34,6 +33,19 @@ export function isDateTimeArray(data: string[]): boolean {
|
|
|
34
33
|
return isDateTime;
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
export function isNumericArray(data: any[]): boolean {
|
|
37
|
+
return data.every((item) => {
|
|
38
|
+
// Check if the item is already a number
|
|
39
|
+
if (typeof item === 'number') {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if the item can be converted into a number
|
|
44
|
+
const number = parseFloat(item);
|
|
45
|
+
return !isNaN(number) && isFinite(item);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
function inferAnalyticTypeFromSemanticType(semanticType: ISemanticType): IAnalyticType {
|
|
38
50
|
switch (semanticType) {
|
|
39
51
|
case 'quantitative':
|
|
@@ -43,37 +55,22 @@ function inferAnalyticTypeFromSemanticType(semanticType: ISemanticType): IAnalyt
|
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
/**
|
|
47
|
-
* 这里目前暂时包一层,是为了解耦具体的推断实现。后续这里要调整推断的逻辑。
|
|
48
|
-
* 需要讨论这一层是否和交互层有关,如果没有关系,这一层包裹可以不存在这里,而是在visual-insights中。
|
|
49
|
-
* @param data 原始数据
|
|
50
|
-
* @param fid 字段id
|
|
51
|
-
* @returns semantic type 列表
|
|
52
|
-
*/
|
|
53
58
|
export function inferSemanticType(data: IRow[], fid: string): ISemanticType {
|
|
54
|
-
|
|
59
|
+
const values = data.map((row) => row[fid]);
|
|
60
|
+
|
|
61
|
+
let st: ISemanticType = isNumericArray(values) ? 'quantitative' : 'nominal';
|
|
55
62
|
if (st === 'nominal') {
|
|
56
63
|
if (isDateTimeArray(data.map((row) => `${row[fid]}`))) st = 'temporal';
|
|
57
|
-
} else if (st === 'ordinal') {
|
|
58
|
-
const valueSet: Set<number> = new Set();
|
|
59
|
-
let _max = -Infinity;
|
|
60
|
-
let _min = Infinity;
|
|
61
|
-
for (let v of valueSet) {
|
|
62
|
-
_max = Math.max(_max, v);
|
|
63
|
-
_min = Math.max(_min, v);
|
|
64
|
-
}
|
|
65
|
-
if (_max - _min + 1 !== valueSet.size) {
|
|
66
|
-
st = 'quantitative';
|
|
67
|
-
}
|
|
68
64
|
}
|
|
69
65
|
return st;
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
export function inferMeta
|
|
68
|
+
export function inferMeta(props: { dataSource: IRow[]; fields: IUncertainMutField[] }): IMutField[] {
|
|
73
69
|
const { dataSource, fields } = props;
|
|
74
|
-
const finalFieldMetas: IMutField[] = []
|
|
70
|
+
const finalFieldMetas: IMutField[] = [];
|
|
75
71
|
for (let field of fields) {
|
|
76
|
-
let semanticType: ISemanticType =
|
|
72
|
+
let semanticType: ISemanticType =
|
|
73
|
+
field.semanticType === '?' ? inferSemanticType(dataSource, field.fid) : field.semanticType;
|
|
77
74
|
let analyticType: IAnalyticType = inferAnalyticTypeFromSemanticType(semanticType);
|
|
78
75
|
|
|
79
76
|
finalFieldMetas.push({
|
|
@@ -82,7 +79,7 @@ export function inferMeta (props: { dataSource: IRow[]; fields: IUncertainMutFie
|
|
|
82
79
|
name: field.name ? field.name : field.fid,
|
|
83
80
|
analyticType,
|
|
84
81
|
semanticType,
|
|
85
|
-
})
|
|
82
|
+
});
|
|
86
83
|
}
|
|
87
|
-
return finalFieldMetas
|
|
88
|
-
}
|
|
84
|
+
return finalFieldMetas;
|
|
85
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { IMeasure, IRow } from '../../interfaces';
|
|
2
|
+
import { IPredicate, checkChildOutlier, checkMajorFactor, filterByPredicates } from '../../utils';
|
|
3
|
+
import { aggregate } from '../op/aggregate';
|
|
4
|
+
|
|
5
|
+
export function explainByChildren(
|
|
6
|
+
dataSource: IRow[],
|
|
7
|
+
predicates: IPredicate[],
|
|
8
|
+
dimensions: string[],
|
|
9
|
+
measures: IMeasure[]
|
|
10
|
+
) {
|
|
11
|
+
// 1. find most relative dimensions(topK)
|
|
12
|
+
// 2. for each dimension, we check all the dim member in it. find the member whos distribution is most close to current one.
|
|
13
|
+
// here we do not nomorlize all the dim member's distribution, we use the relative distribution instead.
|
|
14
|
+
// 3. the dim member we found can be used to explain current one as major factor.
|
|
15
|
+
// const predicates: IPredicate[] = selection === 'all' ? [] : getPredicates(selection, dimensions, []);
|
|
16
|
+
const viewData = aggregate(dataSource, {
|
|
17
|
+
groupBy: dimensions,
|
|
18
|
+
op: 'aggregate',
|
|
19
|
+
agg: Object.fromEntries(measures.map((mea) => [mea.key, mea.op])),
|
|
20
|
+
});
|
|
21
|
+
const measureIds = measures.map((m) => m.key);
|
|
22
|
+
const parentData = filterByPredicates(viewData, predicates);
|
|
23
|
+
|
|
24
|
+
const majorList: Array<{ key: string; score: number; dimensions: string[]; measures: IMeasure[] }> = [];
|
|
25
|
+
const outlierList: Array<{ key: string; score: number; dimensions: string[]; measures: IMeasure[] }> = [];
|
|
26
|
+
for (let extendDim of dimensions) {
|
|
27
|
+
const data = aggregate(dataSource, {
|
|
28
|
+
groupBy: dimensions.concat(extendDim),
|
|
29
|
+
op: 'aggregate',
|
|
30
|
+
agg: Object.fromEntries(measures.map((mea) => [mea.key, mea.op])),
|
|
31
|
+
});
|
|
32
|
+
let groups: Map<any, IRow[]> = new Map();
|
|
33
|
+
for (let record of data) {
|
|
34
|
+
if (!groups.has(record[extendDim])) {
|
|
35
|
+
groups.set(record[extendDim], []);
|
|
36
|
+
}
|
|
37
|
+
groups.get(record[extendDim])?.push(record);
|
|
38
|
+
}
|
|
39
|
+
const { majorKey, majorSum } = checkMajorFactor(parentData, groups, dimensions, measureIds);
|
|
40
|
+
majorList.push({ key: majorKey, score: majorSum, dimensions: [extendDim], measures });
|
|
41
|
+
const { outlierKey, outlierSum } = checkChildOutlier(parentData, groups, dimensions, measureIds);
|
|
42
|
+
outlierList.push({ key: outlierKey, score: outlierSum, dimensions: [extendDim], measures });
|
|
43
|
+
}
|
|
44
|
+
majorList.sort((a, b) => a.score - b.score);
|
|
45
|
+
outlierList.sort((a, b) => b.score - a.score);
|
|
46
|
+
return {
|
|
47
|
+
majorList,
|
|
48
|
+
outlierList,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IExplainProps, IField } from '../../interfaces';
|
|
2
|
+
import { filterByPredicates } from '../../utils';
|
|
3
|
+
import { compareDistribution, normalizeWithParent } from '../../utils/normalization';
|
|
4
|
+
import { aggregate } from '../op/aggregate';
|
|
5
|
+
import { complementaryFields, groupByAnalyticTypes, meaList2AggProps } from './utils';
|
|
6
|
+
|
|
7
|
+
export function explainBySelection(props: IExplainProps) {
|
|
8
|
+
const { metas, dataSource, viewFields, predicates } = props;
|
|
9
|
+
const { dimensions: dimsInView, measures: measInView } = groupByAnalyticTypes(viewFields);
|
|
10
|
+
const complementaryDimensions = complementaryFields({
|
|
11
|
+
all: metas.filter((f) => f.analyticType === 'dimension'),
|
|
12
|
+
selection: dimsInView,
|
|
13
|
+
});
|
|
14
|
+
const outlierList: Array<{ score: number; viiewFields: IField[] }> = complementaryDimensions.map(extendDim => {
|
|
15
|
+
const overallData = aggregate(dataSource, {
|
|
16
|
+
groupBy: [extendDim.fid],
|
|
17
|
+
op: 'aggregate',
|
|
18
|
+
agg: meaList2AggProps(measInView),
|
|
19
|
+
});
|
|
20
|
+
const viewData = aggregate(dataSource, {
|
|
21
|
+
groupBy: dimsInView.map((f) => f.fid),
|
|
22
|
+
op: 'aggregate',
|
|
23
|
+
agg: meaList2AggProps(measInView),
|
|
24
|
+
});
|
|
25
|
+
const subData = filterByPredicates(viewData, predicates);
|
|
26
|
+
|
|
27
|
+
let outlierNormalization = normalizeWithParent(
|
|
28
|
+
subData,
|
|
29
|
+
overallData,
|
|
30
|
+
measInView.map((mea) => mea.fid),
|
|
31
|
+
false
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
let outlierScore = compareDistribution(
|
|
35
|
+
outlierNormalization.normalizedData,
|
|
36
|
+
outlierNormalization.normalizedParentData,
|
|
37
|
+
[extendDim.fid],
|
|
38
|
+
measInView.map((mea) => mea.fid)
|
|
39
|
+
);
|
|
40
|
+
return {
|
|
41
|
+
viiewFields: measInView.concat(extendDim),
|
|
42
|
+
score: outlierScore,
|
|
43
|
+
}
|
|
44
|
+
}).sort((a, b) => b.score - a.score)
|
|
45
|
+
|
|
46
|
+
return outlierList;
|
|
47
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IAggregator, IExplainProps } from '../../interfaces';
|
|
2
|
+
import { filterByPredicates } from '../../utils';
|
|
3
|
+
import { aggregate } from '../op/aggregate';
|
|
4
|
+
import { complementaryFields, groupByAnalyticTypes } from './utils';
|
|
5
|
+
|
|
6
|
+
export function explainValue(props: IExplainProps): number[] {
|
|
7
|
+
const { viewFields, dataSource, predicates } = props;
|
|
8
|
+
const { dimensions: dimsInView, measures: measInView } = groupByAnalyticTypes(viewFields);
|
|
9
|
+
const viewData = aggregate(dataSource, {
|
|
10
|
+
groupBy: dimsInView.map((f) => f.fid),
|
|
11
|
+
op: 'aggregate',
|
|
12
|
+
agg: Object.fromEntries(measInView.map((mea) => [mea.fid, (mea.aggName ?? 'sum') as IAggregator])),
|
|
13
|
+
});
|
|
14
|
+
const selection = filterByPredicates(viewData, predicates);
|
|
15
|
+
const cmps: number[] = [];
|
|
16
|
+
for (let mea of measInView) {
|
|
17
|
+
const values = viewData.map((r) => r[mea.fid]).sort((a, b) => a - b);
|
|
18
|
+
const selectionValues = selection.map((r) => r[mea.fid]);
|
|
19
|
+
const lowerBoundary: number = values[Math.floor(values.length * 0.15)];
|
|
20
|
+
const higherBoundary: number = values[Math.min(Math.ceil(values.length * 0.85), values.length - 1)];
|
|
21
|
+
if (selectionValues.some((v) => v >= higherBoundary)) {
|
|
22
|
+
cmps.push(1);
|
|
23
|
+
} else if (selectionValues.some((v) => v <= lowerBoundary)) {
|
|
24
|
+
cmps.push(-1);
|
|
25
|
+
} else {
|
|
26
|
+
cmps.push(0);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return cmps;
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IAggregator, IField } from '../../interfaces';
|
|
2
|
+
import { IAggQuery } from '../interfaces';
|
|
3
|
+
|
|
4
|
+
export function groupByAnalyticTypes(fields: IField[]) {
|
|
5
|
+
const dimensions = fields.filter((f) => f.analyticType === 'dimension');
|
|
6
|
+
const measures = fields.filter((f) => f.analyticType === 'measure');
|
|
7
|
+
return {
|
|
8
|
+
dimensions,
|
|
9
|
+
measures,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function meaList2AggProps(measures: IField[]): IAggQuery['agg'] {
|
|
14
|
+
return Object.fromEntries(measures.map((mea) => [mea.fid, (mea.aggName ?? 'sum') as IAggregator]));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function complementaryFields(props: { selection: IField[]; all: IField[] }): IField[] {
|
|
18
|
+
return props.all
|
|
19
|
+
.filter((f) => f.analyticType === 'dimension')
|
|
20
|
+
.filter((f) => !props.selection.find((vf) => vf.fid === f.fid));
|
|
21
|
+
}
|
package/src/lib/interfaces.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
+
import { IAggregator } from "../interfaces";
|
|
2
|
+
|
|
1
3
|
export interface IAggQuery {
|
|
2
4
|
op: 'aggregate';
|
|
3
5
|
groupBy: string[];
|
|
4
6
|
agg: {
|
|
5
|
-
[field: string]:
|
|
6
|
-
| 'sum'
|
|
7
|
-
| 'count'
|
|
8
|
-
| 'max'
|
|
9
|
-
| 'min'
|
|
10
|
-
| 'mean'
|
|
11
|
-
| 'median'
|
|
12
|
-
| 'variance'
|
|
13
|
-
| 'stdev';
|
|
7
|
+
[field: string]: IAggregator;
|
|
14
8
|
};
|
|
15
9
|
}
|
|
16
10
|
|
package/src/lib/op/aggregate.ts
CHANGED
package/src/lib/op/bin.ts
CHANGED
package/src/lib/op/fold.ts
CHANGED
package/src/lib/viewQuery.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { IRow } from "
|
|
2
|
-
import { IMutField } from "../interfaces";
|
|
1
|
+
import { IMutField, IRow } from "../interfaces";
|
|
3
2
|
import { aggregate } from "./op/aggregate";
|
|
4
3
|
import { fold } from "./op/fold";
|
|
5
4
|
import { IAggQuery, IBinQuery, IFoldQuery, IRawQuery } from "./interfaces";
|
package/src/locales/en-US.json
CHANGED
package/src/locales/zh-CN.json
CHANGED
|
@@ -31,6 +31,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
31
31
|
const theta = draggableFieldState.theta;
|
|
32
32
|
const radius = draggableFieldState.radius;
|
|
33
33
|
const sizeChannel = draggableFieldState.size;
|
|
34
|
+
const details = draggableFieldState.details;
|
|
34
35
|
|
|
35
36
|
const rowLeftFacetFields = useMemo(() => rows.slice(0, -1).filter((f) => f.analyticType === 'dimension'), [rows]);
|
|
36
37
|
const colLeftFacetFields = useMemo(
|
|
@@ -102,6 +103,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
102
103
|
shape={shape[0]}
|
|
103
104
|
opacity={opacity[0]}
|
|
104
105
|
size={sizeChannel[0]}
|
|
106
|
+
details={details}
|
|
105
107
|
showActions={showActions}
|
|
106
108
|
width={size.width - 12 * 4}
|
|
107
109
|
height={size.height - 12 * 4}
|