Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(mock-data, demo): add stable randomness #1033

Merged
merged 5 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/visx-demo/src/sandboxes/visx-axis/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { curveMonotoneX } from '@visx/curve';
import { scaleUtc, scaleLinear, scaleLog, scaleBand, ScaleInput, coerceNumber } from '@visx/scale';
import { Orientation, SharedAxisProps, AxisScale } from '@visx/axis';
import { AnimatedAxis, AnimatedGridRows, AnimatedGridColumns } from '@visx/react-spring';
import { getSeededRandom } from '@visx/mock-data';
import { LinearGradient } from '@visx/gradient';
import { timeFormat } from 'd3-time-format';

Expand All @@ -12,6 +13,7 @@ const axisColor = '#fff';
const tickLabelColor = '#fff';
export const labelColor = '#340098';
const gridColor = '#6e0fca';
const seededRandom = getSeededRandom(0.5);
const margin = {
top: 40,
right: 150,
Expand Down Expand Up @@ -167,7 +169,7 @@ export default function Example({
('bandwidth' in scale && typeof scale!.bandwidth !== 'undefined'
? scale.bandwidth!() / 2
: 0),
yScale(10 + Math.random() * 90),
yScale(10 + seededRandom() * 90),
])}
yScale={yScale}
curve={curveMonotoneX}
Expand Down
11 changes: 6 additions & 5 deletions packages/visx-demo/src/sandboxes/visx-curve/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ type CurveType = keyof typeof allCurves;

const curveTypes = Object.keys(allCurves);
const lineCount = 5;
const series = new Array(lineCount)
.fill(null)
.map(_ =>
generateDateValue(25).sort((a: DateValue, b: DateValue) => a.date.getTime() - b.date.getTime()),
);
const series = new Array(lineCount).fill(null).map((_, i) =>
// vary each series value deterministically
generateDateValue(25, /* seed= */ i / 72).sort(
(a: DateValue, b: DateValue) => a.date.getTime() - b.date.getTime(),
),
);
const allData = series.reduce((rec, d) => rec.concat(d), []);

// data accessors
Expand Down
2 changes: 1 addition & 1 deletion packages/visx-demo/src/sandboxes/visx-dots/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withToolti
import { voronoi, VoronoiPolygon } from '@visx/voronoi';
import { localPoint } from '@visx/event';

const points: PointsRange[] = genRandomNormalPoints(600).filter((d, i) => i < 600);
const points: PointsRange[] = genRandomNormalPoints(600, /* seed= */ 0.5).filter((_, i) => i < 600);

const x = (d: PointsRange) => d[0];
const y = (d: PointsRange) => d[1];
Expand Down
17 changes: 12 additions & 5 deletions packages/visx-demo/src/sandboxes/visx-drag-i/generateCircles.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { getSeededRandom } from '@visx/mock-data';

export interface Circle {
id: string;
radius: number;
x: number;
y: number;
}

const generateCircles = ({ width, height }: { width: number; height: number }) =>
new Array(width < 360 ? 40 : 185).fill(1).map((d, i) => {
const radius = 25 - Math.random() * 20;
const generateCircles = ({ width, height }: { width: number; height: number }) => {
const radiusRandom = getSeededRandom(0.2);
const xRandom = getSeededRandom(0.3);
const yRandom = getSeededRandom(0.4);

return new Array(width < 360 ? 40 : 185).fill(1).map((d, i) => {
const radius = 25 - radiusRandom() * 20;
return {
id: `${i}`,
radius,
x: Math.round(Math.random() * (width - radius * 2) + radius),
y: Math.round(Math.random() * (height - radius * 2) + radius),
x: Math.round(xRandom() * (width - radius * 2) + radius),
y: Math.round(yRandom() * (height - radius * 2) + radius),
};
});
};

export default generateCircles;
1 change: 1 addition & 0 deletions packages/visx-demo/src/sandboxes/visx-drag-i/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@types/react-dom": "^16",
"@visx/drag": "latest",
"@visx/gradient": "latest",
"@visx/mock-data": "latest",
"@visx/responsive": "latest",
"@visx/scale": "latest",
"react": "^16",
Expand Down
2 changes: 1 addition & 1 deletion packages/visx-demo/src/sandboxes/visx-glyph/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const Glyphs = [
),
];

const data: DateValue[] = genDateValue(Glyphs.length * 2);
const data: DateValue[] = genDateValue(Glyphs.length * 2, 0.91);

// accessors
const date = (d: DateValue) => d.date.valueOf();
Expand Down
10 changes: 9 additions & 1 deletion packages/visx-demo/src/sandboxes/visx-heatmap/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ import { Group } from '@visx/group';
import genBins, { Bin, Bins } from '@visx/mock-data/lib/generators/genBins';
import { scaleLinear } from '@visx/scale';
import { HeatmapCircle, HeatmapRect } from '@visx/heatmap';
import { getSeededRandom } from '@visx/mock-data';

const hot1 = '#77312f';
const hot2 = '#f33d15';
const cool1 = '#122549';
const cool2 = '#b4fbde';
export const background = '#28272c';

const binData = genBins(/* length = */ 16, /* height = */ 16);
const seededRandom = getSeededRandom(0.41);

const binData = genBins(
/* length = */ 16,
/* height = */ 16,
/** binFunc */ idx => 150 * idx,
/** countFunc */ (i, number) => 25 * (number - i) * seededRandom(),
);

function max<Datum>(data: Datum[], value: (d: Datum) => number): number {
return Math.max(...data.map(value));
Expand Down
2 changes: 1 addition & 1 deletion packages/visx-demo/src/sandboxes/visx-responsive/Lines.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { scaleTime, scaleLinear } from '@visx/scale';
import { extent, max } from 'd3-array';

const lineCount = 12;
const series = new Array(lineCount).fill(null).map(_ => generateDateValue(25));
const series = new Array(lineCount).fill(null).map((_, i) => generateDateValue(25, i / 47));
const allData = series.reduce((rec, d) => rec.concat(d), []);

// data accessors
Expand Down
6 changes: 5 additions & 1 deletion packages/visx-demo/src/sandboxes/visx-stats/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import { ViolinPlot, BoxPlot } from '@visx/stats';
import { LinearGradient } from '@visx/gradient';
import { scaleBand, scaleLinear } from '@visx/scale';
import genStats, { Stats } from '@visx/mock-data/lib/generators/genStats';
import { getSeededRandom, getRandomNormal } from '@visx/mock-data';
import { withTooltip, Tooltip, defaultStyles as defaultTooltipStyles } from '@visx/tooltip';
import { WithTooltipProvidedProps } from '@visx/tooltip/lib/enhancers/withTooltip';
import { PatternLines } from '@visx/pattern';

const data: Stats[] = genStats(5);
// seeded randomness
const seededRandom = getSeededRandom(0.1);
const randomNormal = getRandomNormal.source(getSeededRandom(0.789))(4, 3);
const data: Stats[] = genStats(5, randomNormal, () => 10 * seededRandom());

// accessors
const x = (d: Stats) => d.boxPlot.x;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { getSeededRandom } from '@visx/mock-data';

const random = getSeededRandom(0.65);

const getPoints = (array: number[], pointCount: number) => {
const x = 1 / (0.1 + Math.random());
const y = 2 * Math.random() - 0.5;
const z = 10 / (0.1 + Math.random());
const x = 1 / (0.1 + random());
const y = 2 * random() - 0.5;
const z = 10 / (0.1 + random());
for (let i = 0; i < pointCount; i += 1) {
const w = (i / pointCount - y) * z;
array[i] += x * Math.exp(-w * w);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
"@types/d3-array": "^2.0.0",
"@types/react": "^16",
"@types/react-dom": "^16",
"@visx/responsive": "latest",
"@visx/mock-data": "latest",
"@visx/pattern": "latest",
"@visx/responsive": "latest",
"@visx/scale": "latest",
"@visx/shape": "latest",
"d3-array": "^2.4.0",
Expand Down
11 changes: 8 additions & 3 deletions packages/visx-demo/src/sandboxes/visx-voronoi/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { GradientOrangeRed, GradientPinkRed } from '@visx/gradient';
import { RectClipPath } from '@visx/clip-path';
import { voronoi, VoronoiPolygon } from '@visx/voronoi';
import { localPoint } from '@visx/event';
import { getSeededRandom } from '@visx/mock-data';

type Datum = {
x: number;
y: number;
id: string;
};

const seededRandom = getSeededRandom(0.88);

const data: Datum[] = new Array(150).fill(null).map(() => ({
x: Math.random(),
y: Math.random(),
x: seededRandom(),
y: seededRandom(),
id: Math.random()
.toString(36)
.slice(2),
Expand All @@ -34,7 +37,7 @@ export type VoronoiProps = {
margin?: { top: number; right: number; bottom: number; left: number };
};

export default ({ width, height, margin = defaultMargin }: VoronoiProps) => {
const Example = ({ width, height, margin = defaultMargin }: VoronoiProps) => {
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;

Expand Down Expand Up @@ -121,3 +124,5 @@ export default ({ width, height, margin = defaultMargin }: VoronoiProps) => {
</svg>
);
};

export default Example;
1 change: 1 addition & 0 deletions packages/visx-demo/src/sandboxes/visx-voronoi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@visx/event": "latest",
"@visx/gradient": "latest",
"@visx/group": "latest",
"@visx/mock-data": "latest",
"@visx/responsive": "latest",
"@visx/voronoi": "latest",
"react": "^16.8",
Expand Down
4 changes: 2 additions & 2 deletions packages/visx-demo/src/sandboxes/visx-zoom-i/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export type ZoomIProps = {
export default function ZoomI({ width, height }: ZoomIProps) {
const [showMiniMap, setShowMiniMap] = useState<boolean>(true);

const genenerator: GenPhyllotaxisFunction = genPhyllotaxis({ radius: 10, width, height });
const phyllotaxis: PhyllotaxisPoint[] = points.map((d, i) => genenerator(i));
const generator: GenPhyllotaxisFunction = genPhyllotaxis({ radius: 10, width, height });
const phyllotaxis: PhyllotaxisPoint[] = points.map((d, i) => generator(i));

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions packages/visx-mock-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"access": "public"
},
"dependencies": {
"@types/d3-random": "^1.1.2",
"d3-random": "^1.0.3"
"@types/d3-random": "^2.2.0",
"d3-random": "^2.2.2"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

support for seeds was just added 🎉

}
}
16 changes: 13 additions & 3 deletions packages/visx-mock-data/src/generators/genDateValue.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import getSeededRandom from './getSeededRandom';

export interface DateValue {
date: Date;
value: number;
}

export default function genDateValue(length: number): DateValue[] {
export default function genDateValue(
length: number,
/** Optional random seed in the interval [0, 1). */
seed?: number,
/** Optional start time in ms UTC. */
startTimeMs?: number,
): DateValue[] {
const random = seed == null ? Math.random : getSeededRandom(seed);
const startDateMs = startTimeMs == null ? Date.now() : new Date(startTimeMs).valueOf();
return new Array(length).fill(1).map((_, idx: number) => {
return {
date: new Date(Date.now() - idx * 3600000),
date: new Date(startDateMs - idx * 3600000),
// eslint-disable-next-line no-bitwise
value: Math.max(250, (Math.random() * 3000) | 0),
value: (random() * 3000) | 0,
};
});
}
16 changes: 11 additions & 5 deletions packages/visx-mock-data/src/generators/genRandomNormalPoints.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { randomNormal } from 'd3-random';
import getSeededRandom from './getSeededRandom';

export type PointConfig = [number, number, number];
export type PointsRange = [number, number, number];

const random = randomNormal(0, 0.2);
const sqrt3: number = Math.sqrt(3);

function range(length: number): number[] {
Expand All @@ -13,16 +13,22 @@ function range(length: number): number[] {
export function genPointsRange(
length: number,
[offsetX, offsetY, index]: PointConfig,
random: () => number = randomNormal(0, 0.2),
): PointsRange[] {
return range(length).map(() => {
return [random() + offsetX, random() + offsetY, index];
});
}

export default function genPoints(count: number = 300): PointsRange[] {
export default function genPoints(
count: number = 300,
/** Optional random seed in the interval [0, 1). */
seed: number | undefined = undefined,
): PointsRange[] {
const random = seed == null ? undefined : randomNormal.source(getSeededRandom(seed))(0, 0.2);
return [
...genPointsRange(count, [sqrt3, 1, 0]),
...genPointsRange(count, [-sqrt3, 1, 1]),
...genPointsRange(count, [0, -1, 2]),
...genPointsRange(count, [sqrt3, 1, 0], random),
...genPointsRange(count, [-sqrt3, 1, 1], random),
...genPointsRange(count, [0, -1, 2], random),
];
}
13 changes: 10 additions & 3 deletions packages/visx-mock-data/src/generators/genStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ export interface Stats {
binData: BinData[];
}

const random = randomNormal(4, 3);
const randomOffset = () => Math.random() * 10;
const defaultRandom = randomNormal(4, 3);
const defaultRandomOffset = () => Math.random() * 10;
const sampleSize = 1000;

export default function genStats(number: number): Stats[] {
export default function genStats(
/** Number of stat distributions to generate. */
number: number,
/** Function which generates a random number. */
random: () => number = defaultRandom,
/** Function which generates an offset for each data point / invocation of random. */
randomOffset: () => number = defaultRandomOffset,
): Stats[] {
const data = [];

for (let i = 0; i < number; i += 1) {
Expand Down
9 changes: 9 additions & 0 deletions packages/visx-mock-data/src/generators/getSeededRandom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { randomLcg } from 'd3-random';

// returns a seeded random number generator
export default function getSeededRandom(
/** Seed in the interval [0, 1). */
seed: number,
) {
return randomLcg(seed);
}
2 changes: 2 additions & 0 deletions packages/visx-mock-data/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { default as genDateValue } from './generators/genDateValue';
export { default as genRandomNormalPoints } from './generators/genRandomNormalPoints';
export { default as getSeededRandom } from './generators/getSeededRandom';
export { randomNormal as getRandomNormal } from 'd3-random';
export { default as genBin } from './generators/genBin';
export { default as genBins } from './generators/genBins';
export { default as genPhyllotaxis } from './generators/genPhyllotaxis';
Expand Down
30 changes: 25 additions & 5 deletions packages/visx-mock-data/test/genBin.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
import { genBin } from '../src';
import { genBin, getSeededRandom } from '../src';

describe('generators/genBin', () => {
test('it should be defined', () => {
it('should be defined', () => {
expect(genBin).toBeDefined();
});

test('it should have shape [{ bin, count }]', () => {
it('should have shape [{ bin, count }]', () => {
const bin = genBin(1);
expect(bin).toHaveLength(1);
expect(bin[0].bin).toBeDefined();
expect(bin[0].count).toBeDefined();
});

test('it should take optional bin function', () => {
it('should take optional bin function', () => {
const bin = genBin(1, i => i);
expect(bin[0].bin).toEqual(0);
});

test('it should take an optional count function', () => {
it('should take an optional count function', () => {
const bin = genBin(1, undefined, i => i);
expect(bin[0].count).toEqual(0);
expect(bin[0].bin).toEqual(0);
});

it('should support seeded randomness', () => {
const n = 3;
const seededRandom1 = getSeededRandom(0.5);
const seededRandom2 = getSeededRandom(0.5);

expect(
genBin(
n,
i => i,
(i, number) => 25 * (number - i) * seededRandom1(),
),
).toEqual(
genBin(
n,
i => i,
(i, number) => 25 * (number - i) * seededRandom2(),
),
);
});
});
Loading