-
-
Notifications
You must be signed in to change notification settings - Fork 1k
feat(number): add distributor functions #3375
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
base: next
Are you sure you want to change the base?
Changes from all commits
34e62e3
d3236f4
294fa79
b56abb7
2d02ceb
9e60210
a769fb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,35 @@ | ||||||||||||||||||||||||||||||
| import type { Randomizer } from '../randomizer'; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
| * A function that determines the distribution of generated values. | ||||||||||||||||||||||||||||||
| * Values generated by a randomizer are considered uniformly distributed, distributor functions can be used to change this. | ||||||||||||||||||||||||||||||
| * If many results are collected the results form a limited distribution between `0` and `1`. | ||||||||||||||||||||||||||||||
| * So an exponential distributor will values resemble a limited exponential distribution. | ||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * Common examples of distributor functions are: | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * - Uniform distributor: All values have the same likelihood. | ||||||||||||||||||||||||||||||
| * - Normal distributor: Values are more likely to be close to a specific value. | ||||||||||||||||||||||||||||||
| * - Exponential distributor: Values are more likely to be close to 0. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * Distributor functions can be used by some faker functions such as `faker.number.int()` and `faker.number.float()`. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * Please note that the result from the distributor function is processed further by the function accepting it. | ||||||||||||||||||||||||||||||
| * E.g. a distributor result of `0.5` within a call to `faker.number.int({ min: 10, max: 20 })` will result in `15`. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @param randomizer The randomizer to use for generating values. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @returns Generates a random float between 0 (inclusive) and 1 (exclusive). | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @example | ||||||||||||||||||||||||||||||
| * import { Distributor, Randomizer, faker } from '@faker-js/faker'; | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * const alwaysMin: Distributor = () => 0; | ||||||||||||||||||||||||||||||
| * const uniform: Distributor = (randomizer: Randomizer) => randomizer.next(); | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * faker.number.int({ min: 2, max: 10, distributor: alwaysMin }); // 2 | ||||||||||||||||||||||||||||||
| * faker.number.int({ min: 0, max: 10, distributor: uniform }); // 5 | ||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+31
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The exemplary calls should probably be repeated to better visualize the results:
Suggested change
|
||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @since 9.6.0 | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| export type Distributor = (randomizer: Randomizer) => number; | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||
| import { FakerError } from '../errors/faker-error'; | ||||||||
| import type { Distributor } from './distributor'; | ||||||||
| import { uniformDistributor } from './uniform'; | ||||||||
|
|
||||||||
| /** | ||||||||
| * Creates a new function that generates power-law/exponentially distributed values. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can add a more accessible explanation what this means as well.
Suggested change
This needs to be put in the second signature as well.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT this is a power law distribution and not a exponential distribution in the mathematical sense.
Or something similar. |
||||||||
| * This function uses `(base ** next() - 1) / (base - 1)` to spread the values. | ||||||||
| * | ||||||||
| * The following table shows the rough distribution of values generated using `exponentialDistributor({ base: x })`: | ||||||||
| * | ||||||||
| * | Result | Base 0.1 | Base 0.5 | Base 1 | Base 2 | Base 10 | | ||||||||
| * | :-------: | -------: | -------: | -----: | -----: | ------: | | ||||||||
| * | 0.0 - 0.1 | 4.1% | 7.4% | 10.0% | 13.8% | 27.8% | | ||||||||
| * | 0.1 - 0.2 | 4.5% | 7.8% | 10.0% | 12.5% | 16.9% | | ||||||||
| * | 0.2 - 0.3 | 5.0% | 8.2% | 10.0% | 11.5% | 12.1% | | ||||||||
| * | 0.3 - 0.4 | 5.7% | 8.7% | 10.0% | 10.7% | 9.4% | | ||||||||
| * | 0.4 - 0.5 | 6.6% | 9.3% | 10.0% | 10.0% | 7.8% | | ||||||||
| * | 0.5 - 0.6 | 7.8% | 9.9% | 10.0% | 9.3% | 6.6% | | ||||||||
| * | 0.6 - 0.7 | 9.4% | 10.7% | 10.0% | 8.8% | 5.7% | | ||||||||
| * | 0.7 - 0.8 | 12.1% | 11.5% | 10.0% | 8.2% | 5.0% | | ||||||||
| * | 0.8 - 0.9 | 16.9% | 12.6% | 10.0% | 7.8% | 4.5% | | ||||||||
| * | 0.9 - 1.0 | 27.9% | 13.8% | 10.0% | 7.5% | 4.1% | | ||||||||
| * | ||||||||
| * The following table shows the rough distribution of values generated using `exponentialDistributor({ bias: x })`: | ||||||||
| * | ||||||||
| * | Result | Bias -9 | Bias -1 | Bias 0 | Bias 1 | Bias 9 | | ||||||||
| * | :-------: | ------: | ------: | -----: | -----: | -----: | | ||||||||
| * | 0.0 - 0.1 | 27.9% | 13.7% | 10.0% | 7.4% | 4.1% | | ||||||||
| * | 0.1 - 0.2 | 16.9% | 12.5% | 10.0% | 7.8% | 4.5% | | ||||||||
| * | 0.2 - 0.3 | 12.1% | 11.6% | 10.0% | 8.3% | 5.1% | | ||||||||
| * | 0.3 - 0.4 | 9.5% | 10.7% | 10.0% | 8.8% | 5.7% | | ||||||||
| * | 0.4 - 0.5 | 7.8% | 10.0% | 10.0% | 9.3% | 6.6% | | ||||||||
| * | 0.5 - 0.6 | 6.6% | 9.3% | 10.0% | 9.9% | 7.7% | | ||||||||
| * | 0.6 - 0.7 | 5.7% | 8.8% | 10.0% | 10.7% | 9.5% | | ||||||||
| * | 0.7 - 0.8 | 5.0% | 8.2% | 10.0% | 11.5% | 12.1% | | ||||||||
| * | 0.8 - 0.9 | 4.5% | 7.8% | 10.0% | 12.6% | 16.8% | | ||||||||
| * | 0.9 - 1.0 | 4.1% | 7.4% | 10.0% | 13.7% | 27.9% | | ||||||||
| * | ||||||||
| * @param options The options for generating the distributor. | ||||||||
| * @param options.base The base of the exponential distribution. Should be greater than 0. Defaults to `2`. | ||||||||
| * The higher/more above `1` the `base`, the more likely the number will be closer to the minimum value. | ||||||||
| * The lower/closer to zero the `base`, the more likely the number will be closer to the maximum value. | ||||||||
| * Values of `1` will generate a uniform distributor. | ||||||||
| * Can alternatively be configured using the `bias` option. | ||||||||
| * @param options.bias An alternative way to specify the `base`. Also accepts values below zero. Defaults to `-1`. | ||||||||
| * The higher/more positive the `bias`, the more likely the number will be closer to the maximum value. | ||||||||
| * The lower/more negative the `bias`, the more likely the number will be closer to the minimum value. | ||||||||
| * Values of `0` will generate a uniform distributor. | ||||||||
| * Can alternatively be configured using the `base` option. | ||||||||
| * | ||||||||
| * @example | ||||||||
| * import { exponentialDistributor, generateMersenne53Randomizer } from '@faker-js/faker'; | ||||||||
| * | ||||||||
| * const randomizer = generateMersenne53Randomizer(); | ||||||||
| * const distributor = exponentialDistributor(); | ||||||||
| * distributor(randomizer) // 0.04643770898904198 | ||||||||
| * distributor(randomizer) // 0.13436127925491848 | ||||||||
| * distributor(randomizer) // 0.4202905589842396 | ||||||||
| * distributor(randomizer) // 0.5164955927828387 | ||||||||
| * distributor(randomizer) // 0.3476359433171099 | ||||||||
| * | ||||||||
| * @since 9.6.0 | ||||||||
| */ | ||||||||
| export function exponentialDistributor( | ||||||||
| options?: | ||||||||
| | { | ||||||||
| /** | ||||||||
| * The base of the exponential distribution. Should be greater than 0. | ||||||||
| * The higher/more above `1` the `base`, the more likely the number will be closer to the minimum value. | ||||||||
| * The lower/closer to zero the `base`, the more likely the number will be closer to the maximum value. | ||||||||
| * Values of `1` will generate a uniform distribution. | ||||||||
| * Can alternatively be configured using the `bias` option. | ||||||||
| * | ||||||||
| * @default 2 | ||||||||
| */ | ||||||||
| base?: number; | ||||||||
| } | ||||||||
| | { | ||||||||
| /** | ||||||||
| * An alternative way to specify the `base`. Also accepts values below zero. | ||||||||
| * The higher/more positive the `bias`, the more likely the number will be closer to the maximum value. | ||||||||
| * The lower/more negative the `bias`, the more likely the number will be closer to the minimum value. | ||||||||
| * Values of `0` will generate a uniform distribution. | ||||||||
| * Can alternatively be configured using the `base` option. | ||||||||
| * | ||||||||
| * @default -1 | ||||||||
| */ | ||||||||
| bias?: number; | ||||||||
| } | ||||||||
| ): Distributor; | ||||||||
|
Comment on lines
+64
to
+90
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was there a reason why we didn't made these two dedicated functions? The advantages would be that it should be more obvious to the end user that you can not pass bias AND base at the same time. Technically this is already the way, but the signatures could more detailed examples based on the input that is possible. The signature containing the bias argument could only contain table with the bias values. Same with the base. The disadvantage would be that we would still require a third combined signature (same as we have right now) for our documentation generation.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Splitting the options into two separate signatures is fine. |
||||||||
| /** | ||||||||
| * Creates a new function that generates exponentially distributed values. | ||||||||
| * This function uses `(base ** next() - 1) / (base - 1)` to spread the values. | ||||||||
| * | ||||||||
| * @param options The options for generating the distributor. | ||||||||
| * @param options.base The base of the exponential distribution. Should be greater than 0. Defaults to `2`. | ||||||||
| * The higher/more above `1` the `base`, the more likely the number will be closer to the minimum value. | ||||||||
| * The lower/closer to zero the `base`, the more likely the number will be closer to the maximum value. | ||||||||
| * Values of `1` will generate a uniform distributor. | ||||||||
| * Can alternatively be configured using the `bias` option. | ||||||||
| * @param options.bias An alternative way to specify the `base`. Also accepts values below zero. Defaults to `-1`. | ||||||||
| * The higher/more positive the `bias`, the more likely the number will be closer to the maximum value. | ||||||||
| * The lower/more negative the `bias`, the more likely the number will be closer to the minimum value. | ||||||||
| * Values of `0` will generate a uniform distributor. | ||||||||
| * Can alternatively be configured using the `base` option. | ||||||||
| */ | ||||||||
| export function exponentialDistributor( | ||||||||
| options: { | ||||||||
| base?: number; | ||||||||
| bias?: number; | ||||||||
| } = {} | ||||||||
| ): Distributor { | ||||||||
| const { bias = -1, base = bias <= 0 ? -bias + 1 : 1 / (bias + 1) } = options; | ||||||||
|
|
||||||||
| if (base === 1) { | ||||||||
| return uniformDistributor(); | ||||||||
| } else if (base <= 0) { | ||||||||
| throw new FakerError('Base should be greater than 0.'); | ||||||||
| } | ||||||||
|
|
||||||||
| return ({ next }) => (base ** next() - 1) / (base - 1); | ||||||||
| } | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import type { Distributor } from './distributor'; | ||
|
|
||
| /** | ||
| * Creates a new function that generates uniformly distributed values. | ||
| * The likelihood of each value is the same. | ||
| * | ||
| * The following table shows the rough distribution of values generated using `uniformDistributor()`: | ||
| * | ||
| * | Result | Uniform | | ||
| * | :-------: | ------: | | ||
| * | 0.0 - 0.1 | 10.0% | | ||
| * | 0.1 - 0.2 | 10.0% | | ||
| * | 0.2 - 0.3 | 10.0% | | ||
| * | 0.3 - 0.4 | 10.0% | | ||
| * | 0.4 - 0.5 | 10.0% | | ||
| * | 0.5 - 0.6 | 10.0% | | ||
| * | 0.6 - 0.7 | 10.0% | | ||
| * | 0.7 - 0.8 | 10.0% | | ||
| * | 0.8 - 0.9 | 10.0% | | ||
| * | 0.9 - 1.0 | 10.0% | | ||
| * | ||
| * @returns A new uniform distributor function. | ||
| * | ||
| * @example | ||
| * import { generateMersenne53Randomizer, uniformDistributor } from '@faker-js/faker'; | ||
| * | ||
| * const randomizer = generateMersenne53Randomizer(); | ||
| * const distributor = uniformDistributor(); | ||
| * distributor(randomizer) // 0.9100215692561207 | ||
| * distributor(randomizer) // 0.791632947887336 | ||
| * distributor(randomizer) // 0.14770035310214324 | ||
| * distributor(randomizer) // 0.28282249581185814 | ||
| * distributor(randomizer) // 0.017890944117802343 | ||
| * | ||
| * @since 9.6.0 | ||
| */ | ||
| export function uniformDistributor(): Distributor { | ||
| return UNIFORM_DISTRIBUTOR; | ||
| } | ||
|
|
||
| const UNIFORM_DISTRIBUTOR: Distributor = ({ next }) => next(); | ||
xDivisionByZerox marked this conversation as resolved.
Show resolved
Hide resolved
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.