Skip to content

Commit

Permalink
generalize compute hist + raw version
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 8, 2018
1 parent 6cfd343 commit 7e53fa8
Show file tree
Hide file tree
Showing 20 changed files with 304 additions and 261 deletions.
7 changes: 5 additions & 2 deletions src/internal/interable.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@

export interface IForEachAble<T> extends Iterable<T> {
forEach(callback: (v: T, i: number) => void): void;

}

export interface ISequence<T> extends Iterable<T> {
export interface ISequence<T> extends IForEachAble<T> {
readonly length: number;
filter(callback: (v: T, i: number) => boolean): ISequence<T>;
map<U>(callback: (v: T, i: number) => U): ISequence<U>;
forEach(callback: (v: T, i: number) => void): void;

some(callback: (v: T, i: number) => boolean): boolean;
every(callback: (v: T, i: number) => boolean): boolean;
Expand Down
48 changes: 26 additions & 22 deletions src/internal/math.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ICategory} from '../model';
import {ISequence} from './interable';
import {ISequence, IForEachAble} from './interable';
import {bisectLeft} from 'd3-array';

export interface INumberBin {
Expand Down Expand Up @@ -264,7 +264,7 @@ function cached() {
* @returns {{min: number, max: number, count: number, hist: histogram.Bin<number>[]}}
* @internal
*/
export function computeNormalizedStats(arr: ISequence<number>, numberOfBins?: number): IStatistics & IAdvancedBoxPlotData {
export function computeNormalizedStats(arr: ISequence<IForEachAble<number> | number>, numberOfBins?: number): IStatistics & IAdvancedBoxPlotData {

const bins: INumberBin[] = [];

Expand Down Expand Up @@ -345,7 +345,7 @@ function computeGranularity(min: Date | null, max: Date | null) {
return {hist, histGranularity: EDateHistogramGranularity.MONTH};
}

export function computeDateStats(arr: ISequence<Date | null>, template?: IDateStatistics): IDateStatistics {
export function computeDateStats(arr: ISequence<IForEachAble<Date | null>>, template?: IDateStatistics): IDateStatistics {
// filter out NaN
let min: Date | null = null;
let max: Date | null = null;
Expand All @@ -354,21 +354,26 @@ export function computeDateStats(arr: ISequence<Date | null>, template?: IDateSt

// yyyymmdd, count
const byDay = new Map<number, number>();
arr.forEach((v) => {
arr.forEach((vs) => {
count += 1;
if (!v) {
if (!vs) {
missing += 1;
return;
}

if (min == null || v < min) {
min = v;
}
if (max == null || v > max) {
max = v;
}
const key = v.getFullYear() * 10000 + v.getMonth() * 100 + v.getDate();
byDay.set(key, (byDay.get(key) || 0) + 1);
vs.forEach((v) => {
if (!v) {
missing += 1;
return;
}
if (min == null || v < min) {
min = v;
}
if (max == null || v > max) {
max = v;
}
const key = v.getFullYear() * 10000 + v.getMonth() * 100 + v.getDate();
byDay.set(key, (byDay.get(key) || 0) + 1);
});
});

const {histGranularity, hist} = template ? {
Expand All @@ -393,22 +398,22 @@ export function computeDateStats(arr: ISequence<Date | null>, template?: IDateSt
* @returns {{hist: {cat: string, y: number}[]}}
* @internal
*/
export function computeHist(arr: ISequence<Set<ICategory> | null | ICategory>, categories: ICategory[]): ICategoricalStatistics {
export function computeHist(arr: ISequence<IForEachAble<ICategory | null>>, categories: ICategory[]): ICategoricalStatistics {
const m = new Map<string, number>();
let missingCount = 0;
categories.forEach((cat) => m.set(cat.name, 0));

arr.forEach((vs) => {
if (vs == null || (vs instanceof Set && vs.size === 0)) {
if (vs == null) {
missingCount += 1;
return;
}
if (!(vs instanceof Set)) {
m.set(vs.name, (m.get(vs.name) || 0) + 1);
return;
}
vs.forEach((v) => {
m.set(v.name, (m.get(v.name) || 0) + 1);
if (v == null) {
missingCount += 1;
} else {
m.set(v.name, (m.get(v.name) || 0) + 1);
}
});
});

Expand Down Expand Up @@ -452,7 +457,6 @@ export function similar(a: number, b: number, delta = 0.5) {
return Math.abs(a - b) < delta;
}

export declare type IValueStatistics = ICategoricalStatistics | IDateStatistics | IStatistics;

export function isPromiseLike<T>(value: any): value is PromiseLike<T> {
return value instanceof Promise || typeof value.then === 'function';
Expand Down
4 changes: 4 additions & 0 deletions src/model/CategoricalColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ export default class CategoricalColumn extends ValueColumn<string> implements IC
return r;
}

iterCategory(row: IDataRow) {
return [this.getCategory(row)];
}

dump(toDescRef: (desc: any) => any): any {
const r = super.dump(toDescRef);
r.filter = this.currentFilter;
Expand Down
10 changes: 7 additions & 3 deletions src/model/CategoricalMapColumn.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ICategoricalDesc, ICategory, toCategories} from './ICategoricalColumn';
import {ICategoricalDesc, ICategory, toCategories, ICategoricalLikeColumn} from './ICategoricalColumn';
import {IDataRow} from './interfaces';
import MapColumn, {IMapColumnDesc} from './MapColumn';
import {ICategoricalColorMappingFunction, DEFAULT_COLOR_FUNCTION, restoreColorMapping} from './CategoricalColorMappingFunction';
Expand All @@ -19,7 +19,7 @@ export declare type ICategoricalMapColumnDesc = ICategoricalDesc & IMapColumnDes
export declare function colorMappingChanged(previous: ICategoricalColorMappingFunction, current: ICategoricalColorMappingFunction): void;

@toolbar('colorMappedCategorical')
export default class CategoricalMapColumn extends MapColumn<string | null> {
export default class CategoricalMapColumn extends MapColumn<string | null> implements ICategoricalLikeColumn {
static readonly EVENT_COLOR_MAPPING_CHANGED = CategoricalColumn.EVENT_COLOR_MAPPING_CHANGED;

readonly categories: ICategory[];
Expand Down Expand Up @@ -73,7 +73,7 @@ export default class CategoricalMapColumn extends MapColumn<string | null> {
}

getColors(row: IDataRow) {
return this.getCategories(row).map(({key, value}) => ({key, value: value ? this.colorMapping.apply(value): Column.DEFAULT_COLOR}));
return this.getCategories(row).map(({key, value}) => ({key, value: value ? this.colorMapping.apply(value) : Column.DEFAULT_COLOR}));
}

getValue(row: IDataRow) {
Expand Down Expand Up @@ -109,4 +109,8 @@ export default class CategoricalMapColumn extends MapColumn<string | null> {
super.restore(dump, factory);
this.colorMapping = restoreColorMapping(dump.colorMapping, this.categories);
}

iterCategory(row: IDataRow) {
return this.getCategories(row).map((d) => d.value);
}
}
10 changes: 7 additions & 3 deletions src/model/CategoricalsColumn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ArrayColumn, {IArrayColumnDesc} from './ArrayColumn';
import {ICategoricalDesc, ICategory, toCategories} from './ICategoricalColumn';
import {ICategoricalDesc, ICategory, toCategories, ICategoricalLikeColumn} from './ICategoricalColumn';
import {IDataRow} from './interfaces';
import {toolbar} from './annotations';
import CategoricalColumn from './CategoricalColumn';
Expand All @@ -21,7 +21,7 @@ export declare function colorMappingChanged(previous: ICategoricalColorMappingFu
* a string column with optional alignment
*/
@toolbar('colorMappedCategorical')
export default class CategoricalsColumn extends ArrayColumn<string | null> {
export default class CategoricalsColumn extends ArrayColumn<string | null> implements ICategoricalLikeColumn {
static readonly EVENT_COLOR_MAPPING_CHANGED = CategoricalColumn.EVENT_COLOR_MAPPING_CHANGED;

readonly categories: ICategory[];
Expand Down Expand Up @@ -70,13 +70,17 @@ export default class CategoricalsColumn extends ArrayColumn<string | null> {
}

getColors(row: IDataRow) {
return this.getCategories(row).map((d) => d ? this.colorMapping.apply(d): Column.DEFAULT_COLOR);
return this.getCategories(row).map((d) => d ? this.colorMapping.apply(d) : Column.DEFAULT_COLOR);
}

getSet(row: IDataRow) {
return new Set(this.getCategories(row));
}

iterCategory(row: IDataRow) {
return this.getCategories(row);
}

getValues(row: IDataRow) {
return this.getCategories(row).map((v) => v ? v.name : null);
}
Expand Down
16 changes: 12 additions & 4 deletions src/model/ICategoricalColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@ import {colorPool} from './internal';
import {FIRST_IS_MISSING} from './missing';
import {IValueColumnDesc} from './ValueColumn';
import {ICategoricalColorMappingFunction} from './CategoricalColorMappingFunction';
import {ISequence, isSeqEmpty} from '../internal/interable';
import {ISequence, isSeqEmpty, IForEachAble} from '../internal/interable';

export interface ICategoricalDesc {
categories: (string | Partial<ICategory>)[];
}

export declare type ICategoricalColumnDesc = IValueColumnDesc<string> & ICategoricalDesc;

export interface ISetColumn extends IArrayColumn<boolean> {
readonly categories: ICategory[];

getSet(row: IDataRow): Set<ICategory>;
export interface ICategoricalLikeColumn extends Column {
readonly categories: ICategory[];

getColorMapping(): ICategoricalColorMappingFunction;
setColorMapping(mapping: ICategoricalColorMappingFunction): void;

iterCategory(row: IDataRow): IForEachAble<ICategory | null>;
}

export interface ISetColumn extends IArrayColumn<boolean>, ICategoricalLikeColumn {
getSet(row: IDataRow): Set<ICategory>;
}

export function isSetColumn(col: Column): col is ISetColumn {
Expand Down Expand Up @@ -169,6 +174,9 @@ export function toCategories(desc: ICategoricalDesc) {
return cats.sort(compareCategory);
}

export function isCategoricalLikeColumn(col: Column): col is ICategoricalLikeColumn {
return typeof (<ICategoricalLikeColumn>col).categories !== 'undefined' && typeof (<ICategoricalLikeColumn>col).iterCategory === 'function';
}
/**
* checks whether the given column or description is a categorical column, i.e. the value is a list of categories
* @param col
Expand Down
Loading

0 comments on commit 7e53fa8

Please sign in to comment.