/
categorical.ts
79 lines (72 loc) 路 2.09 KB
/
categorical.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
export interface ICategory {
value: string;
color?: string;
label?: string;
}
export type ICategories = readonly ICategory[];
export interface ICategoryBin extends Required<ICategory> {
count: number;
/**
* accumulated count
*/
acc: number;
percentage: number;
}
export type ICategoryBins = readonly ICategoryBin[];
function colorGen(dark?: boolean) {
// from ColorBrewer
const schemeDark2 = ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d'];
const schemeSet2 = ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494'];
const set = dark ? schemeDark2.concat(schemeSet2) : schemeSet2.concat(schemeDark2);
let acc = 0;
return () => {
return set[acc++ % set.length];
};
}
function bin(hist: ICategoryBin[], values: readonly string[]) {
const map = new Map(hist.map((bin) => [bin.value, 0]));
values.forEach((value) => {
if (value == null) {
return;
}
const key = value.toString();
if (!map.has(key)) {
return;
}
map.set(key, map.get(key)! + 1);
});
return map;
}
export function categoricalHistogram(
values: readonly string[],
categories: readonly (string | ICategory)[],
base?: readonly string[],
dark: boolean = false
): readonly ICategoryBin[] {
const nextColor = colorGen(dark);
const generateCat = (value: string) => {
return {
value,
label: value.length > 0 ? `${value[0].toUpperCase()}${value.slice(1)}` : value,
color: nextColor(),
};
};
const hist: ICategoryBin[] = categories.map((cat) => {
return Object.assign(
{ count: 0, acc: 0, percentage: 0 },
generateCat(typeof cat === 'string' ? cat : cat.value),
typeof cat === 'string' ? {} : cat
);
});
const map = bin(hist, values);
const baseMap = base ? bin(hist, base) : null;
const total = Array.from(map.values()).reduce((acc, v) => acc + v, 0);
let acc = 0;
hist.forEach((bin) => {
bin.acc = acc;
bin.count = map.get(bin.value)!;
bin.percentage = bin.count / total;
acc += baseMap ? baseMap.get(bin.value)! : bin.count;
});
return hist;
}