Skip to content

Commit 0a7db5b

Browse files
authored
Merge branch 'main' into feat/rect-canvas
2 parents ca61db2 + 17c45ec commit 0a7db5b

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

src/lib/transforms/normalize.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,35 @@ describe('normalizeY', () => {
3838
]);
3939
});
4040

41+
it('excepts options as object', () => {
42+
const { data: normalized, ...channels } = normalizeY(
43+
{
44+
data,
45+
y: 'y',
46+
z: 'group'
47+
},
48+
{ basis: 'sum' }
49+
);
50+
51+
expect(channels.y).toBe('__y');
52+
expect(normalized).toHaveLength(data.length);
53+
54+
const result = normalized.map((d) => ({
55+
id: d.id,
56+
group: d.group,
57+
y: d[channels.y]
58+
}));
59+
60+
// For group A: y values are 2 and 4, sum = 6
61+
// For group B: y values are 6 and 8, sum = 14
62+
expect(result).toEqual([
63+
{ id: 'a1', group: 'A', y: 2 / 6 },
64+
{ id: 'a2', group: 'A', y: 4 / 6 },
65+
{ id: 'b1', group: 'B', y: 6 / 14 },
66+
{ id: 'b2', group: 'B', y: 8 / 14 }
67+
]);
68+
});
69+
4170
it('normalizes to [0, 1] range using extent as basis', () => {
4271
const { data: normalized, ...channels } = normalizeY(
4372
{

src/lib/transforms/normalize.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { mapX, mapY } from './map.js';
2-
import type { TransformArg, RawValue, MapIndexObject } from '$lib/types/index.js';
2+
import type { TransformArg, RawValue, MapIndexObject, MapIndexFunction } from '$lib/types/index.js';
33
import { min, max, mean, median, sum, deviation, extent } from 'd3-array';
44
import { sort } from './sort.js';
55

6+
type BasisFunction = (I: number[], S: RawValue[]) => number;
7+
68
type NormalizeBasis =
79
| 'deviation'
810
| 'first'
@@ -13,23 +15,33 @@ type NormalizeBasis =
1315
| 'median'
1416
| 'sum'
1517
| 'extent'
18+
| BasisFunction
1619
| MapIndexObject;
1720

21+
type NormalizeOptions = NormalizeBasis | { basis: NormalizeBasis };
22+
1823
/**
1924
* Normalizes the x values based on the specified basis. Uses mapX.
2025
*/
21-
export function normalizeX<T>(args: TransformArg<T>, basis: NormalizeBasis) {
22-
return mapX(args, normalize(basis));
26+
export function normalizeX<T>(args: TransformArg<T>, options: NormalizeOptions) {
27+
return mapX(args, normalize(options));
2328
}
2429

2530
/**
2631
* Normalizes the y values based on the specified basis. Uses mapY.
2732
*/
28-
export function normalizeY<T>(args: TransformArg<T>, basis: NormalizeBasis) {
29-
return mapY(args, normalize(basis));
33+
export function normalizeY<T>(args: TransformArg<T>, options: NormalizeOptions) {
34+
return mapY(args, normalize(options));
3035
}
3136

32-
function normalize(basis: NormalizeBasis): MapIndexObject {
37+
function isMapIndex(obj: any): obj is MapIndexObject {
38+
return obj && typeof obj.mapIndex === 'function';
39+
}
40+
41+
function normalize(options: NormalizeOptions): MapIndexObject {
42+
if (isMapIndex(options)) return options;
43+
if (typeof options === 'object' && isMapIndex(options?.basis)) return options?.basis;
44+
const basis = typeof options === 'object' ? options.basis : options;
3345
if (basis === undefined) return normalizeFirst;
3446
if (typeof basis === 'function') return normalizeBasis(basis);
3547
// if (/^p\d{2}$/i.test(basis)) return normalizeAccessor(percentile(basis));
@@ -56,7 +68,7 @@ function normalize(basis: NormalizeBasis): MapIndexObject {
5668
throw new Error(`invalid basis: ${basis}`);
5769
}
5870

59-
function normalizeBasis(basis: (I: number[], S: RawValue[]) => number): MapIndexObject {
71+
function normalizeBasis(basis: BasisFunction): MapIndexObject {
6072
return {
6173
mapIndex(I, S, T) {
6274
const b = +basis(I, S);
@@ -96,7 +108,7 @@ const normalizeLast = normalizeBasis((I, S) => {
96108
});
97109

98110
const normalizeDeviation = {
99-
mapIndex(I, S, T) {
111+
mapIndex(I: number[], S: RawValue[], T: number[]) {
100112
const m = mean(I, (i) => S[i]);
101113
const d = deviation(I, (i) => S[i]);
102114
for (const i of I) {

src/lib/transforms/rename.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type RenameChannelsOptions = Partial<Record<ScaledChannelName, ScaledChannelName
55
type ReplaceChannelsOptions = Partial<Record<ScaledChannelName, ScaledChannelName[]>>;
66

77
// using a symbol doesn't work because channels are spread into components
8-
export const RENAME = '__renamed__';
8+
export const RENAME = Symbol('renamed');
99

1010
/**
1111
* renames a channel without modifying the data

src/lib/types/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ export type AutoMarginStores = {
6969
autoMarginBottom: Writable<Map<string, number>>;
7070
};
7171

72+
export type MapIndexFunction = (I: number[], S: RawValue[], T: RawValue[]) => void;
73+
7274
export type MapIndexObject = {
73-
mapIndex: (I: number[], S: RawValue[], T: RawValue[]) => void;
75+
mapIndex: MapIndexFunction;
7476
};
7577

7678
export type MapMethod =

0 commit comments

Comments
 (0)