Distributions#

Several functions generate values using statistical distributions, giving you control over the shape of your random data.

Choosing a Distribution#

I want to model…ShapeUse
Random IDsFlatuniform.*
Human heights, test scoresBell curvenorm.*
Response latencies, salariesRight-skewed taillognorm.*
Time between eventsSteep drop-offexp.*
Page views, city populationsPower-lawzipf.* / pareto.*
Coin flips, A/B test conversionsDiscrete bellbinomial.*
Requests per second, errors per hourDiscrete, peaks at λpoisson.*
Probabilities, percentagesConfigurablebeta.*
Queue wait times, rainfallSkewed humpgamma.*
Hardware lifespan, churnConfigurable skewweibull.*
Replay production samplesMirrors inputempirical.*
Age vs income, height vs weightMulti-dimensionalmvnorm
Stock prices, sensor readingsDrifting walkrwalk / rwalk_f
User session flows, status changesMarkov chainmarkov
Weighted country or status selectionDistribution dependent*.set variants

Supported distributions#

Uniform Distribution#

Every value in the range is equally likely. Use for random IDs, test data without realistic skew, or anywhere you need a flat spread.

ParameterDescription
minLower bound of the range (inclusive)
maxUpper bound of the range (inclusive)
precisionNumber of decimal places (float variant only)
edg fuzz "uniform.int(1, 100)"

Distribution:
     1.006 -     10.9  █████████████████████████████████████ 997 (10.0%)
      10.9 -     20.8  ████████████████████████████████████████ 1051 (10.5%)
      20.8 -     30.7  ███████████████████████████████████████ 1029 (10.3%)
      30.7 -    40.59  █████████████████████████████████████ 997 (10.0%)
     40.59 -    50.49  █████████████████████████████████████ 995 (10.0%)
     50.49 -    60.39  ██████████████████████████████████████ 1010 (10.1%)
     60.39 -    70.29  █████████████████████████████████████ 979 (9.8%)
     70.29 -    80.18  ██████████████████████████████████████ 1009 (10.1%)
     80.18 -    90.08  ███████████████████████████████████ 943 (9.4%)
     90.08 -    99.98  █████████████████████████████████████ 990 (9.9%)

See all uniform functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Normal (Gaussian) Distribution#

Bell curve centered on mean. Values cluster around the center and thin out symmetrically toward the tails. Use for human measurements (height, weight), test scores, or any naturally symmetric phenomenon.

ParameterDescription
meanCenter of the bell curve - the most likely value
stddevStandard deviation - controls the width of the curve. For an unclamped normal, ~68% of values fall within one stddev of the mean, ~95% within two - clamping via min/max will shift these percentages
minLower bound - values below this are clamped
maxUpper bound - values above this are clamped
precisionNumber of decimal places (float variant only)
edg fuzz "norm.int(50, 15, 0, 100)"   

Distribution:
         0 -       10   30 (0.3%)
        10 -       20  ██ 174 (1.7%)
        20 -       30  ██████████ 664 (6.6%)
        30 -       40  █████████████████████████ 1570 (15.7%)
        40 -       50  ████████████████████████████████████████ 2486 (24.9%)
        50 -       60  ███████████████████████████████████████ 2483 (24.8%)
        60 -       70  ██████████████████████████ 1630 (16.3%)
        70 -       80  ███████████ 708 (7.1%)
        80 -       90  ███ 208 (2.1%)
        90 -      100   47 (0.5%)

See all norm functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Log-normal Distribution#

Right-skewed with a long tail. The log of the values is normally distributed, so most values cluster at the low end but a few are very large. Use for salaries, response latencies, file sizes.

ParameterDescription
muMean of the underlying normal distribution (in log-space). Controls where the peak of the output distribution sits. Higher mu shifts the peak rightward
sigmaStandard deviation of the underlying normal distribution (in log-space). Controls how heavy the right tail is - larger sigma means more extreme outliers
minLower bound - values below this are clamped
maxUpper bound - values above this are clamped
precisionNumber of decimal places (float variant only)
edg fuzz "lognorm.float(2.0, 0.8, 0, 100, 2)"

Distribution:
      0.33 -    10.27  ████████████████████████████████████████ 6639 (66.4%)
     10.27 -     20.2  ██████████████ 2360 (23.6%)
      20.2 -    30.14  ███ 623 (6.2%)
     30.14 -    40.07  █ 212 (2.1%)
     40.07 -    50.01   86 (0.9%)
     50.01 -    59.95   42 (0.4%)
     59.95 -    69.88   17 (0.2%)
     69.88 -    79.82   9 (0.1%)
     79.82 -    89.75   7 (0.1%)
     89.75 -    99.69   5 (0.1%)

See all lognorm functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Exponential Distribution#

Steep drop-off from min. Models the time between independent events - short intervals are common, long ones are rare. Use for arrival times, time-to-next-click, session gaps.

ParameterDescription
rateHow quickly the probability drops off. Higher values concentrate values closer to min. A rate of 1.0 gives a mean of 1.0; a rate of 0.1 gives a mean of 10.0
minLower bound of the range
maxUpper bound of the range
precisionNumber of decimal places (float variant only)
edg fuzz "exp.int(0.5, 0, 100)"  

Distribution:
         0 -      1.9  ████████████████████████████████████████ 5240 (52.4%)
       1.9 -      3.8  ███████████████████████ 3032 (30.3%)
       3.8 -      5.7  ████████ 1108 (11.1%)
       5.7 -      7.6  ███ 393 (3.9%)
       7.6 -      9.5  █ 148 (1.5%)
       9.5 -     11.4   51 (0.5%)
      11.4 -     13.3   18 (0.2%)
      13.3 -     15.2   6 (0.1%)
      15.2 -     17.1   3 (0.0%)
      17.1 -       19   1 (0.0%)

See all exp functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Zipfian Distribution#

Power-law skew where low values dominate dramatically. The first value is by far the most common, with a steep falloff. Use for page views, word frequencies, hot keys in a cache.

ParameterDescription
sExponent controlling the skew (must be > 1). Higher values make the distribution more extreme - 1.1 gives moderate skew, 2.0 gives very heavy skew toward value 0
vOffset parameter (typically 1.0). Controls the relative weight of low-ranked vs high-ranked values
maxUpper bound of the output range
edg fuzz "zipf.int(1.1, 1.0, 999)"

Distribution:
         0 -     99.9  ████████████████████████████████████████ 7632 (76.3%)
      99.9 -    199.8  ████ 785 (7.8%)
     199.8 -    299.7  ██ 399 (4.0%)
     299.7 -    399.6  █ 295 (2.9%)
     399.6 -    499.5  █ 235 (2.4%)
     499.5 -    599.4   178 (1.8%)
     599.4 -    699.3   162 (1.6%)
     699.3 -    799.2   124 (1.2%)
     799.2 -    899.1   100 (1.0%)
     899.1 -      999   90 (0.9%)

See all zipf functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Pareto Distribution#

Continuous power-law (the “80/20 rule”). Most values cluster near zero with a long tail of rare large values. Use for city populations, wealth distribution, file access patterns.

ParameterDescription
alphaShape parameter controlling concentration. Higher values pack more probability at the low end - alpha=1 is very spread out, alpha=3 is strongly concentrated near 0
minLower bound of the output range (pareto.float only)
maxUpper bound of the output range
precisionNumber of decimal places (float variant only)
edg fuzz "pareto.int(2.0, 999)"

Distribution:
         0 -      5.8  ████████████████████████████████████████ 9774 (97.7%)
       5.8 -     11.6   173 (1.7%)
      11.6 -     17.4   28 (0.3%)
      17.4 -     23.2   12 (0.1%)
      23.2 -       29   3 (0.0%)
        29 -     34.8   4 (0.0%)
      34.8 -     40.6   4 (0.0%)
      40.6 -     46.4   0 (0.0%)
      46.4 -     52.2   1 (0.0%)
      52.2 -       58   1 (0.0%)

See all pareto functions for float, n, set, ref, seq, vector, timestamp, and obj_n variants.

Beta Distribution#

Extremely flexible shape controlled by two parameters. Can produce uniform (1,1), U-shaped (0.5,0.5), bell-shaped (5,5), or left/right-skewed distributions. Use for probabilities, percentages, proportions.

ParameterDescription
alphaFirst shape parameter. When alpha < 1, values pile up near 0. When alpha > 1, values move away from 0. Combined with beta, controls the shape
betaSecond shape parameter. When beta < 1, values pile up near max. When beta > 1, values move away from max. The peak sits at (alpha-1)/(alpha+beta-2) of the range
minLower bound of the output range. The draw is scaled onto the range, not clamped to it
maxUpper bound of the output range. The draw is scaled onto the range, not clamped to it
precisionNumber of decimal places (float variant only)

Beta’s support is [0, 1], so min/max scale rather than clamp: the sample x is mapped as min + x*(max-min). beta.int(2, 5, 0, 100) therefore spans the full 0-100 range, and beta.set / beta.ref / beta.seq / beta.vector / beta.timestamp / beta.obj_n pick across their full range too. Beta is the only distribution whose support is a fixed finite interval, so it is the only one that scales - every other distribution rejects and resamples out-of-range draws instead.

edg fuzz "beta.float(0.5, 0.5, 0, 1, 2)"

Distribution:
         0 -      0.1  ██████████████████████████████████████ 2011 (20.1%)
       0.1 -      0.2  █████████████████ 912 (9.1%)
       0.2 -      0.3  ███████████████ 837 (8.4%)
       0.3 -      0.4  ███████████ 623 (6.2%)
       0.4 -      0.5  ███████████ 627 (6.3%)
       0.5 -      0.6  ████████████ 658 (6.6%)
       0.6 -      0.7  ███████████ 623 (6.2%)
       0.7 -      0.8  ████████████ 682 (6.8%)
       0.8 -      0.9  █████████████████ 916 (9.2%)
       0.9 -        1  ████████████████████████████████████████ 2111 (21.1%)

See all beta functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Gamma Distribution#

Right-skewed hump. The shape depends on two parameters - small shape values give steep decay (like exponential), larger values give a pronounced hump. Use for queue wait times, rainfall amounts, insurance claims.

ParameterDescription
shapeControls the shape of the curve. shape=1 is exponential. Higher values produce a more pronounced hump that moves rightward. The mean of the distribution is shape/rate
rateControls the scale (inverse of scale parameter). Higher values compress the distribution leftward. The mean of the distribution is shape/rate
minLower bound - values below this are clamped
maxUpper bound - values above this are clamped
precisionNumber of decimal places (float variant only)
edg fuzz "gamma.float(2, 0.5, 0, 100, 2)"

Distribution:
      0.03 -    2.213  ██████████████████████████████████ 3011 (30.1%)
     2.213 -    4.396  ████████████████████████████████████████ 3468 (34.7%)
     4.396 -    6.579  ██████████████████████ 1966 (19.7%)
     6.579 -    8.762  ██████████ 895 (8.9%)
     8.762 -    10.94  ████ 394 (3.9%)
     10.94 -    13.13  █ 155 (1.6%)
     13.13 -    15.31   64 (0.6%)
     15.31 -    17.49   26 (0.3%)
     17.49 -    19.68   15 (0.1%)
     19.68 -    21.86   6 (0.1%)

See all gamma functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Weibull Distribution#

Configurable skew used in reliability engineering. When shape < 1, failure rate decreases over time (infant mortality). When shape = 1, it reduces to exponential. When shape > 1, failure rate increases (wear-out). Use for hardware lifespan, churn modeling, time-to-failure.

ParameterDescription
shapeControls the failure rate curve. < 1: decreasing failure rate (infant mortality). = 1: constant rate (exponential). > 1: increasing rate (wear-out). ≈ 3.6: approximately normal
scaleCharacteristic life - the value at which ~63.2% of observations have occurred. Stretches or compresses the distribution horizontally
minLower bound - values below this are clamped
maxUpper bound - values above this are clamped
precisionNumber of decimal places (float variant only)
edg fuzz "weibull.float(1.5, 50, 0, 100, 2)"

Distribution:
      0.04 -    10.03  ████████████████████████ 945 (9.4%)
     10.03 -    20.03  █████████████████████████████████████ 1483 (14.8%)
     20.03 -    30.02  ████████████████████████████████████████ 1567 (15.7%)
     30.02 -    40.02  █████████████████████████████████████ 1479 (14.8%)
     40.02 -    50.01  ██████████████████████████████ 1206 (12.1%)
     50.01 -    60.01  ███████████████████████████ 1084 (10.8%)
     60.01 -       70  █████████████████████ 830 (8.3%)
        70 -       80  ████████████████ 637 (6.4%)
        80 -    89.99  ████████████ 473 (4.7%)
     89.99 -    99.99  ███████ 296 (3.0%)

See all weibull functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Binomial Distribution#

Discrete bell-shaped distribution. Models the count of successes in n independent trials, each with probability p. Use for A/B test conversions, defect counts, coin-flip experiments.

ParameterDescription
nNumber of independent trials. Output ranges from 0 to n. Only used with binomial.int - for other domains (set, ref, seq, vector, timestamp), n is implicit from the collection size
pProbability of success on each trial (0.0 to 1.0). The mean output is n * p
edg fuzz "binomial.int(20, 0.3)"

Distribution:
         0 -      1.4  █ 96 (1.0%)
       1.4 -      2.8  ███ 270 (2.7%)
       2.8 -      4.2  ███████████████████████████ 1993 (19.9%)
       4.2 -      5.6  ████████████████████████ 1759 (17.6%)
       5.6 -        7  ██████████████████████████ 1902 (19.0%)
         7 -      8.4  ████████████████████████████████████████ 2857 (28.6%)
       8.4 -      9.8  ████████ 636 (6.4%)
       9.8 -     11.2  ██████ 437 (4.4%)
      11.2 -     12.6   38 (0.4%)
      12.6 -       14   12 (0.1%)

See all binomial functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Poisson Distribution#

Discrete distribution that peaks at λ. Models the number of events in a fixed interval when events occur independently at a constant average rate. Use for requests per second, errors per hour, arrivals per minute.

ParameterDescription
lambdaAverage number of events per interval. The distribution peaks at this value, with variance also equal to lambda - higher lambda gives a wider, more symmetric shape
edg fuzz "poisson.int(5.0)"

Distribution:
         0 -      1.5  █████ 454 (4.5%)
       1.5 -        3  ██████████ 813 (8.1%)
         3 -      4.5  ████████████████████████████████████████ 3154 (31.5%)
       4.5 -        6  ██████████████████████ 1778 (17.8%)
         6 -      7.5  ███████████████████████████████ 2463 (24.6%)
       7.5 -        9  ████████ 652 (6.5%)
         9 -     10.5  ███████ 560 (5.6%)
      10.5 -       1285 (0.9%)
        12 -     13.5   37 (0.4%)
      13.5 -       15   4 (0.0%)

See all poisson functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Empirical Distribution#

Replays observed data rather than fitting a mathematical curve. You provide a sample array and values are drawn by interpolating the empirical cumulative distribution function (CDF). Use when you have production data and want to reproduce its exact shape.

ParameterDescription
samplesArray of observed numeric values. Values that appear more often in the array are more likely to be generated. The distribution is constructed by sorting these values and interpolating between them
precisionNumber of decimal places (float variant only)
edg fuzz "empirical.int([5, 10, 10, 20, 30, 30, 30, 50, 80, 95])"

Distribution:
     5.003 -       14  █████████████████████████████████ 2609 (26.1%)
        14 -       23  ████████████ 999 (10.0%)
        23 -       32  ████████████████████████████████████████ 3083 (30.8%)
        32 -       41  ███████ 570 (5.7%)
        41 -    49.99  ██████ 490 (4.9%)
     49.99 -    58.99  ████ 316 (3.2%)
     58.99 -    67.99  ████ 336 (3.4%)
     67.99 -    76.99  ████ 324 (3.2%)
     76.99 -    85.99  ███████ 562 (5.6%)
     85.99 -    94.98  █████████ 711 (7.1%)

See all empirical functions for n, set, ref, seq, vector, timestamp, and obj_n variants.

Multivariate Normal Distribution#

Generates correlated values across multiple columns. Define means, standard deviations, and a correlation matrix, then reference each dimension by index. Use for age-vs-income, height-vs-weight, or any pair of related numeric columns.

ParameterDescription
groupName that ties multiple columns together. All mvnorm calls with the same group name share a single correlated draw per row
indexWhich dimension to return (0-based). Each column in the correlated set gets its own index
meansArray of mean values, one per dimension. E.g. [100, 50] for a 2D distribution
stddevsArray of standard deviations, one per dimension. Controls the spread of each individual dimension
correlationsUpper-triangle correlation coefficients. For 2 dimensions, this is a single value [r] where r ranges from -1.0 (perfectly inversely correlated) to 1.0 (perfectly correlated). For 3 dimensions: [r12, r13, r23]

See standalone distribution functions for the full signature reference.

Random Walk#

Stateful Brownian motion. Each call advances the walk by one step - the value drifts from the previous value rather than being drawn independently. Use for stock prices, sensor readings, temperature over time.

ParameterDescription
groupName identifying this walk. Multiple references to the same group share the same stateful position, advancing it with each call
startInitial value on the first call. Subsequent calls drift from the previous value
driftMean step size per call. Positive values trend upward over time, negative values trend downward, 0.0 is a pure random walk
volatilityStandard deviation of each step. Controls how noisy the walk is - higher values produce wilder swings
precisionNumber of decimal places (rwalk_f only)

See standalone distribution functions for the full signature reference.

Markov Chain#

Stateful state machine. Each call transitions from the current state to the next based on a transition probability matrix. Use for user session flows, order status changes, network state modeling.

ParameterDescription
groupName identifying this chain. Multiple references to the same group share the same current state
statesArray of state labels, e.g. ['idle', 'active', 'closed']
matrixFlat row-major transition probabilities. Each row corresponds to a source state and must sum to 1.0. For 3 states, provide 9 values: rows 1–3 of a 3×3 matrix

See Markov Chains for detailed usage.

Distribution namespaces#

Each distribution provides a consistent set of domain functions. Not every distribution supports every domain.

DomainReturnsDescription
.floatfloatRandom float with decimal precision
.intintRandom integer in a range
.nstringN unique random values as a comma-separated string
.obj_n[]mapGenerate N object instances with distribution-controlled count
.refmapPick a row from a named dataset (access fields with .name, .id, etc.)
.seqintDistributed value from a named global sequence
.setanyPick from a predefined set of values
.timestampstringDistributed timestamp between min and max (RFC3339)
.vectorstringpgvector-compatible clustered vector literal

The .n domain takes minN, maxN as its final two arguments. A count is chosen uniformly at random in [minN, maxN] and that many distinct values are drawn. It is an error if minN < 1, if maxN < minN, or if that many distinct values can’t be found within 10,000 draws - which is what happens when the distribution’s support is smaller than N. Typical use is unique item IDs for multi-item order lines, e.g. TPC-C New-Order: zipf.n(1.1, 1.0, 100000, 5, 15).

uniform#

Flat distribution - every value equally likely.

FunctionSignatureDescription
uniform.floatuniform.float(min, max, precision)Uniform random float with precision
uniform.intuniform.int(min, max)Uniform random integer in [min, max]
uniform.nuniform.n(min, max, minN, maxN)Random number (between minN and maxN) of unique uniform values
uniform.obj_nuniform.obj_n(name, min, max)Generate N object instances (uniform count)
uniform.refuniform.ref(name)Uniform random row from a named dataset
uniform.sequniform.seq(name)Uniform random value from a global sequence
uniform.setuniform.set(values, weights)Uniform or weighted random selection from a set
uniform.timestampuniform.timestamp(min, max)Random timestamp between min and max
uniform.vectoruniform.vector(dims, clusters, spread)Clustered vector literal (uniform centroid selection)

Aliases: set(), ref(), vector(), timestamp(), and obj_n() are top-level aliases for their uniform.* equivalents. ref_weighted(name, weights) provides weighted random row selection with one integer weight per row.

norm PRO#

Bell curve centered on mean.

FunctionSignatureDescription
norm.floatnorm.float(mean, stddev, min, max, precision)Normal-distributed random float
norm.intnorm.int(mean, stddev, min, max)Normal-distributed random integer
norm.nnorm.n(mean, stddev, min, max, minN, maxN)Random number (between minN and maxN) of unique normal-distributed values
norm.obj_nnorm.obj_n(name, mean, stddev, min, max)Generate N object instances (normal-distributed count)
norm.refnorm.ref(name, mean, stddev)Pick a row using normal distribution
norm.seqnorm.seq(name, mean, stddev)Normal-distributed value from a global sequence
norm.setnorm.set(values, mean, stddev)Pick from a set using normal distribution
norm.timestampnorm.timestamp(min, max, mean, stddev)Normal-distributed timestamp
norm.vectornorm.vector(dims, clusters, spread, mean, stddev)Clustered vector with normal centroid selection

lognorm PRO#

Right-skewed with a long tail - models salaries, response latencies.

FunctionSignatureDescription
lognorm.floatlognorm.float(mu, sigma, min, max, precision)Log-normal random float
lognorm.intlognorm.int(mu, sigma, min, max)Log-normal random integer
lognorm.nlognorm.n(mu, sigma, min, max, minN, maxN)Random number (between minN and maxN) of unique log-normal values
lognorm.obj_nlognorm.obj_n(name, mu, sigma, min, max)Generate N object instances (log-normal count)
lognorm.reflognorm.ref(name, mu, sigma)Pick a row using log-normal distribution
lognorm.seqlognorm.seq(name, mu, sigma)Log-normal value from a global sequence
lognorm.setlognorm.set(values, mu, sigma)Pick from a set using log-normal distribution
lognorm.timestamplognorm.timestamp(min, max, mu, sigma)Log-normal distributed timestamp
lognorm.vectorlognorm.vector(dims, clusters, spread, mu, sigma)Clustered vector with log-normal centroid selection

exp PRO#

Exponential decay - models time between events.

FunctionSignatureDescription
exp.floatexp.float(rate, min, max, precision)Exponential random float
exp.intexp.int(rate, min, max)Exponential random integer
exp.nexp.n(rate, min, max, minN, maxN)Random number (between minN and maxN) of unique exponential values
exp.obj_nexp.obj_n(name, rate, min, max)Generate N object instances (exponential count)
exp.refexp.ref(name, rate)Pick a row using exponential distribution
exp.seqexp.seq(name, rate)Exponential value from a global sequence
exp.setexp.set(values, rate)Pick from a set using exponential distribution
exp.timestampexp.timestamp(min, max, rate)Exponential distributed timestamp
exp.vectorexp.vector(dims, clusters, spread, rate)Clustered vector with exponential centroid selection

zipf PRO#

Power-law skew - low values dominate (hot keys, popular pages).

FunctionSignatureDescription
zipf.intzipf.int(s, v, max)Zipfian random integer in [0, max]
zipf.nzipf.n(s, v, imax, minN, maxN)Random number (between minN and maxN) of unique Zipfian values
zipf.obj_nzipf.obj_n(name, s, v, min, max)Generate N object instances (Zipfian count)
zipf.refzipf.ref(name, s, v)Pick a row using Zipfian distribution
zipf.seqzipf.seq(name, s, v)Zipfian value from a global sequence
zipf.setzipf.set(values, s, v)Pick from a set using Zipfian distribution
zipf.timestampzipf.timestamp(min, max, s, v)Zipfian distributed timestamp
zipf.vectorzipf.vector(dims, clusters, spread, s, v)Clustered vector with Zipfian centroid selection

pareto PRO#

Continuous power-law - lower values dominate (city populations, wealth).

FunctionSignatureDescription
pareto.floatpareto.float(alpha, min, max, precision)Continuous Pareto random float in [min, max]
pareto.intpareto.int(alpha, max)Pareto random integer in [0, max]
pareto.npareto.n(alpha, imax, minN, maxN)Random number (between minN and maxN) of unique Pareto values
pareto.obj_npareto.obj_n(name, alpha, min, max)Generate N object instances (Pareto count)
pareto.refpareto.ref(name, alpha)Pick a row using Pareto distribution
pareto.seqpareto.seq(name, alpha)Pareto value from a global sequence
pareto.setpareto.set(values, alpha)Pick from a set using Pareto distribution
pareto.timestamppareto.timestamp(min, max, alpha)Pareto distributed timestamp
pareto.vectorpareto.vector(dims, clusters, noise, alpha)Clustered vector with Pareto centroid selection

Note: pareto.float takes (alpha, min, max, precision), following the same (params..., min, max, precision) convention as every other .float, whereas pareto.int takes (alpha, imax). Use pareto.float when the fractional part matters. There is deliberately no binomial.float, poisson.float or zipf.float - those distributions are integer-valued by definition. Reach for beta.float or gamma.float if you need a continuous skewed value.

beta PRO#

Flexible shape - uniform, U-shaped, or bell-shaped depending on alpha/beta.

FunctionSignatureDescription
beta.floatbeta.float(alpha, beta, min, max, precision)Beta-distributed random float
beta.intbeta.int(alpha, beta, min, max)Beta-distributed random integer
beta.nbeta.n(alpha, beta, min, max, minN, maxN)Random number (between minN and maxN) of unique Beta-distributed values
beta.obj_nbeta.obj_n(name, alpha, beta, min, max)Generate N object instances (Beta count)
beta.refbeta.ref(name, alpha, beta)Pick a row using Beta distribution
beta.seqbeta.seq(name, alpha, beta)Beta-distributed value from a global sequence
beta.setbeta.set(values, alpha, beta)Pick from a set using Beta distribution
beta.timestampbeta.timestamp(min, max, alpha, beta)Beta-distributed timestamp
beta.vectorbeta.vector(dims, clusters, noise, alpha, beta)Clustered vector with Beta centroid selection

gamma PRO#

Right-skewed hump - models queue wait times, rainfall.

FunctionSignatureDescription
gamma.floatgamma.float(shape, rate, min, max, precision)Gamma-distributed random float
gamma.intgamma.int(shape, rate, min, max)Gamma-distributed random integer
gamma.ngamma.n(shape, rate, min, max, minN, maxN)Random number (between minN and maxN) of unique Gamma-distributed values
gamma.obj_ngamma.obj_n(name, shape, rate, min, max)Generate N object instances (Gamma count)
gamma.refgamma.ref(name, shape, rate)Pick a row using Gamma distribution
gamma.seqgamma.seq(name, shape, rate)Gamma-distributed value from a global sequence
gamma.setgamma.set(values, shape, rate)Pick from a set using Gamma distribution
gamma.timestampgamma.timestamp(min, max, shape, rate)Gamma-distributed timestamp
gamma.vectorgamma.vector(dims, clusters, noise, shape, rate)Clustered vector with Gamma centroid selection

weibull PRO#

Configurable skew - models hardware lifespan, churn.

FunctionSignatureDescription
weibull.floatweibull.float(shape, scale, min, max, precision)Weibull-distributed random float
weibull.intweibull.int(shape, scale, min, max)Weibull-distributed random integer
weibull.nweibull.n(shape, scale, min, max, minN, maxN)Random number (between minN and maxN) of unique Weibull-distributed values
weibull.obj_nweibull.obj_n(name, shape, scale, min, max)Generate N object instances (Weibull count)
weibull.refweibull.ref(name, shape, scale)Pick a row using Weibull distribution
weibull.seqweibull.seq(name, shape, scale)Weibull-distributed value from a global sequence
weibull.setweibull.set(values, shape, scale)Pick from a set using Weibull distribution
weibull.timestampweibull.timestamp(min, max, shape, scale)Weibull-distributed timestamp
weibull.vectorweibull.vector(dims, clusters, noise, shape, scale)Clustered vector with Weibull centroid selection

binomial PRO#

Discrete bell - count of successes in n trials (A/B test conversions).

FunctionSignatureDescription
binomial.intbinomial.int(n, p)Binomial-distributed random integer
binomial.nbinomial.n(n, p, minN, maxN)Random number (between minN and maxN) of unique Binomial-distributed values
binomial.obj_nbinomial.obj_n(name, n, p, min, max)Generate N object instances (Binomial count)
binomial.refbinomial.ref(name, p)Pick a row using Binomial distribution
binomial.seqbinomial.seq(name, p)Binomial-distributed value from a global sequence
binomial.setbinomial.set(values, p)Pick from a set using Binomial distribution
binomial.timestampbinomial.timestamp(min, max, p)Binomial-distributed timestamp
binomial.vectorbinomial.vector(dims, clusters, spread, p)Clustered vector with Binomial centroid selection

poisson PRO#

Discrete, peaks at λ - models requests per second, errors per hour.

FunctionSignatureDescription
poisson.intpoisson.int(lambda)Poisson-distributed random integer
poisson.npoisson.n(lambda, minN, maxN)Random number (between minN and maxN) of unique Poisson-distributed values
poisson.obj_npoisson.obj_n(name, lambda, min, max)Generate N object instances (Poisson count)
poisson.refpoisson.ref(name, lambda)Pick a row using Poisson distribution
poisson.seqpoisson.seq(name, lambda)Poisson-distributed value from a global sequence
poisson.setpoisson.set(values, lambda)Pick from a set using Poisson distribution
poisson.timestamppoisson.timestamp(min, max, lambda)Poisson-distributed timestamp
poisson.vectorpoisson.vector(dims, clusters, noise, lambda)Clustered vector with Poisson centroid selection

empirical PRO#

Mirrors observed data - replay production samples via CDF interpolation.

FunctionSignatureDescription
empirical.floatempirical.float(samples, precision)Sample float from observed data
empirical.intempirical.int(samples)Sample integer from observed data
empirical.nempirical.n(samples, minN, maxN)Random number (between minN and maxN) of unique empirical values
empirical.obj_nempirical.obj_n(name, samples, min, max)Generate N object instances (empirical count)
empirical.refempirical.ref(name, samples)Pick a row using empirical distribution
empirical.seqempirical.seq(name, samples)Empirical value from a global sequence
empirical.setempirical.set(values, samples)Pick from a set using empirical distribution
empirical.timestampempirical.timestamp(min, max, samples)Empirical distributed timestamp
empirical.vectorempirical.vector(dims, clusters, noise, samples)Clustered vector with empirical centroid selection

Standalone functions PRO#

FunctionSignatureDescription
markovmarkov(group, states, matrix)Stateful Markov chain - see Markov Chains
mvnormmvnorm(group, index, means, stddevs, correlations)Correlated multivariate normal across columns
rwalk_frwalk_f(group, start, drift, volatility, precision)Random walk step with precision
rwalkrwalk(group, start, drift, volatility)Stateful random walk / Brownian motion

Distribution Shapes#

Sequence distributions#

Here’s a quick introduction to what each distribution looks like in practice, using sequence distributions to seed 10,000 sample rows referencing 1,000 orders:

let orders = 1000
let samples = 10000
let batch_size = 100

seq order_id(start: 1, step: 1)

up {
  create_orders `CREATE TABLE IF NOT EXISTS orders (
    id INT PRIMARY KEY,
    customer STRING NOT NULL
  )`

  create_samples `CREATE TABLE IF NOT EXISTS samples (
    id INT PRIMARY KEY DEFAULT unique_rowid(),
    uniform_val INT NOT NULL,
    zipf_val INT NOT NULL,
    norm_val INT NOT NULL,
    exp_val INT NOT NULL,
    lognorm_val INT NOT NULL
  )`
}

seed {
  seed_orders(count: orders, size: batch_size)
    `INSERT INTO orders (id, customer) __values__` (
    seq_global("order_id"),
    gen('firstname') + ' ' + gen('lastname')
  )

  seed_samples(count: samples, size: batch_size)
    `INSERT INTO samples (uniform_val, zipf_val, norm_val, exp_val, lognorm_val) __values__` (
    uniform.seq("order_id"),
    zipf.seq("order_id", 1.1, 1.0),
    norm.seq("order_id", 500, 150),
    exp.seq("order_id", 0.01),
    lognorm.seq("order_id", 5.5, 0.5)
  )
}

After seeding, the samples table will show various distributions of order ids (all with complete referential integrity to the orders table) and can be queried to show their distribution as follows:

-- Uniform distribution.
SELECT
  div(uniform_val - 1, 50) * 50 + 1 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM samples
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       1 |   481 | █████████████████████████████████████████████
      51 |   484 | █████████████████████████████████████████████
     101 |   496 | ███████████████████████████████████████████████
     151 |   465 | ████████████████████████████████████████████
     201 |   492 | ██████████████████████████████████████████████
     251 |   510 | ████████████████████████████████████████████████
     301 |   516 | ████████████████████████████████████████████████
     351 |   513 | ████████████████████████████████████████████████
     401 |   471 | ████████████████████████████████████████████
     451 |   533 | ██████████████████████████████████████████████████
     501 |   519 | █████████████████████████████████████████████████
     551 |   498 | ███████████████████████████████████████████████
     601 |   522 | █████████████████████████████████████████████████
     651 |   518 | █████████████████████████████████████████████████
     701 |   492 | ██████████████████████████████████████████████
     751 |   473 | ████████████████████████████████████████████
     801 |   522 | █████████████████████████████████████████████████
     851 |   515 | ████████████████████████████████████████████████
     901 |   472 | ████████████████████████████████████████████
     951 |   508 | ████████████████████████████████████████████████

-- Normal distribution.
SELECT
  div(norm_val - 1, 50) * 50 + 1 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM samples
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       1 |    10 |
      51 |    23 | 
     101 |    59 | ██
     151 |   142 | █████
     201 |   254 | █████████
     251 |   390 | ██████████████
     301 |   667 | ████████████████████████
     351 |   905 | █████████████████████████████████
     401 |  1159 | ██████████████████████████████████████████
     451 |  1380 | ██████████████████████████████████████████████████
     501 |  1362 | █████████████████████████████████████████████████
     551 |  1162 | ██████████████████████████████████████████
     601 |   917 | █████████████████████████████████
     651 |   652 | ████████████████████████
     701 |   440 | ████████████████
     751 |   263 | ██████████
     801 |   128 | █████
     851 |    57 | ██
     901 |    27 | 
     951 |     3 |

-- Exponential distribution.
SELECT
  div(exp_val - 1, 50) * 50 + 1 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM samples
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       1 |  3961 | ██████████████████████████████████████████████████
      51 |  2436 | ███████████████████████████████
     101 |  1475 | ███████████████████
     151 |   782 | ██████████
     201 |   532 | ███████
     251 |   329 | ████
     301 |   207 | ███
     351 |   111 | 
     401 |    66 | 
     451 |    40 | 
     501 |    26 |
     551 |    19 |
     601 |     7 |
     651 |     1 |
     701 |     1 |
     751 |     5 |
     851 |     1 |
     901 |     1 |

-- Log-normal distribution.
SELECT
  div(lognorm_val - 1, 50) * 50 + 1 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM samples
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       1 |    10 |
      51 |   342 | █████████
     101 |  1293 | ███████████████████████████████████
     151 |  1836 | ██████████████████████████████████████████████████
     201 |  1699 | ██████████████████████████████████████████████
     251 |  1425 | ███████████████████████████████████████
     301 |  1024 | ████████████████████████████
     351 |   767 | █████████████████████
     401 |   516 | ██████████████
     451 |   335 | █████████
     501 |   238 | ██████
     551 |   147 | ████
     601 |   121 | ███
     651 |    81 | ██
     701 |    71 | ██
     751 |    38 | 
     801 |    25 | 
     851 |    15 |
     901 |    13 |
     951 |     4 |

-- Zipfian distribution.
SELECT
  div(zipf_val - 1, 50) * 50 + 1 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM samples
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       1 |  6904 | ██████████████████████████████████████████████████
      51 |   778 | ██████
     101 |   458 | ███
     151 |   312 | ██
     201 |   255 | ██
     251 |   177 | 
     301 |   146 | 
     351 |   112 | 
     401 |   106 | 
     451 |    77 | 
     501 |   104 | 
     551 |    76 | 
     601 |    79 | 
     651 |    87 | 
     701 |    56 |
     751 |    64 |
     801 |    66 |
     851 |    41 |
     901 |    54 |
     951 |    48 |

Numeric distributions#

The sequence distributions above pick existing IDs from a pool. The numeric distributions below generate raw values directly - useful when the column itself is the output (latency, price, score, etc.).

up {
  create_distributions `CREATE TABLE IF NOT EXISTS distributions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    dist_type STRING NOT NULL,
    value FLOAT8 NOT NULL
  )`
}

weights {
  insert_uniform     = 10
  insert_zipfian     = 10
  insert_normal      = 10
  insert_exponential = 10
  insert_lognormal   = 10
  insert_pareto      = 10
  insert_beta        = 10
  insert_gamma       = 10
  insert_weibull     = 10
  insert_poisson     = 10
  insert_binomial    = 10
  insert_empirical   = 10
  insert_rwalk       = 10
}

run {
  insert_uniform(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('uniform', $1::FLOAT8)` (uniform.int(0, 100))

  insert_zipfian(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('zipfian', $1::FLOAT8)` (zipf.int(1.1, 1.0, 99))

  insert_normal(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('normal', $1::FLOAT8)` (norm.float(50, 15, 0, 100, 2))

  insert_exponential(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('exponential', $1::FLOAT8)` (exp.float(0.5, 0, 100, 2))

  insert_lognormal(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('lognormal', $1::FLOAT8)` (lognorm.float(2.0, 0.5, 0, 100, 2))

  insert_pareto(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('pareto', $1::FLOAT8)` (pareto.int(2.0, 99))

  insert_beta(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('beta', $1::FLOAT8)` (beta.float(2, 5, 0, 100, 2))

  insert_gamma(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('gamma', $1::FLOAT8)` (gamma.float(2, 0.5, 0, 100, 2))

  insert_weibull(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('weibull', $1::FLOAT8)` (weibull.float(1.5, 50, 0, 100, 2))

  insert_poisson(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('poisson', $1::FLOAT8)` (poisson.int(5.0))

  insert_binomial(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('binomial', $1::FLOAT8)` (binomial.int(20, 0.3))

  insert_empirical(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('empirical', $1::FLOAT8)` (empirical.int([5, 10, 10, 20, 30, 30, 30, 50, 80, 95]))

  insert_rwalk(type: exec) `INSERT INTO distributions (dist_type, value)
    VALUES ('rwalk', $1::FLOAT8)` (rwalk_f('walk', 50, 0.0, 0.5, 2))
}

After running for 10 seconds with 10 workers, query each distribution:

-- Beta: left-skewed, peak near mode = (α-1)/(α+β-2).
SELECT
  (floor(d.value * 10) / 10)::DECIMAL(2,1) AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'beta'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
     0.0 |   330 | ████████████████████████
     0.1 |   678 | ██████████████████████████████████████████████████
     0.2 |   681 | ██████████████████████████████████████████████████
     0.3 |   562 | █████████████████████████████████████████
     0.4 |   351 | ██████████████████████████
     0.5 |   225 | █████████████████
     0.6 |    97 | ███████
     0.7 |    23 | ██
     0.8 |     6 |

-- Captured before beta min/max became scaling rather than clamping:
-- beta.float(2, 5, 0, 100, 2) now spans the full 0-100 range instead of
-- beta's raw [0, 1] support.

-- Binomial: bell-shaped discrete, centered at n·p.
SELECT
  floor(d.value) AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'binomial'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |     3 |
       1 |    11 | 
       2 |    73 | ██████
       3 |   192 | █████████████████
       4 |   382 | ██████████████████████████████████
       5 |   552 | █████████████████████████████████████████████████
       6 |   564 | ██████████████████████████████████████████████████
       7 |   466 | █████████████████████████████████████████
       8 |   343 | ██████████████████████████████
       9 |   183 | ████████████████
      10 |    96 | █████████
      11 |    31 | ███
      12 |    10 | 
      13 |     3 |

-- Empirical: multi-modal, mirrors the input sample values.
SELECT
  floor(d.value / 7) * 7 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'empirical'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |   129 | ████████
       7 |   713 | ██████████████████████████████████████████
      14 |   214 | █████████████
      21 |   240 | ██████████████
      28 |   850 | ██████████████████████████████████████████████████
      35 |   112 | ███████
      42 |   109 | ██████
      49 |    94 | ██████
      56 |    77 | █████
      63 |    75 | ████
      70 |    70 | ████
      77 |   119 | ███████
      84 |   146 | █████████
      91 |   104 | ██████

-- Exponential: rapid decay from zero.
SELECT
  floor(d.value) AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'exponential'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |  1244 | ██████████████████████████████████████████████████
       1 |   759 | ███████████████████████████████
       2 |   437 | ██████████████████
       3 |   273 | ███████████
       4 |   149 | ██████
       5 |   110 | ████
       6 |    56 | ██
       7 |    43 | ██
       8 |    22 | 
       9 |    12 |
      10 |     7 |
      11 |    14 | 
      12 |     5 |
      13 |     3 |
      14 |     3 |

-- Gamma: right-skewed, models wait times.
SELECT
  floor(d.value / 2) * 2 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'gamma'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |   804 | ████████████████████████████████████████
       2 |  1007 | ██████████████████████████████████████████████████
       4 |   639 | ████████████████████████████████
       6 |   299 | ███████████████
       8 |   143 | ███████
      10 |    86 | ████
      12 |    29 | 
      14 |    16 | 
      16 |     6 |
      18 |     3 |
      20 |     1 |
      22 |     1 |

-- Log-normal: right-skewed with a long tail.
SELECT
  floor(d.value / 3) * 3 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'lognormal'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |   122 | ██████
       3 |   891 | ██████████████████████████████████████████████
       6 |   964 | ██████████████████████████████████████████████████
       9 |   514 | ███████████████████████████
      12 |   254 | █████████████
      15 |   106 | █████
      18 |    56 | ███
      21 |    31 | ██
      24 |    11 | 
      27 |     3 |
      30 |     2 |
      33 |     1 |
      48 |     1 |

-- Normal: bell curve centered on mean.
SELECT
  floor(d.value / 7) * 7 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'normal'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |     5 |
       7 |    16 | 
      14 |    70 | ██████
      21 |   134 | ███████████
      28 |   260 | ██████████████████████
      35 |   457 | ██████████████████████████████████████
      42 |   544 | ██████████████████████████████████████████████
      49 |   595 | ██████████████████████████████████████████████████
      56 |   452 | ██████████████████████████████████████
      63 |   329 | ████████████████████████████
      70 |   177 | ███████████████
      77 |    57 | █████
      84 |    33 | ███
      91 |     4 |

-- Pareto: power-law, lower values dominate.
SELECT
  floor(d.value / 3) * 3 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'pareto'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |  2790 | ██████████████████████████████████████████████████
       3 |   135 | ██
       6 |    35 | 
       9 |     9 |
      12 |     7 |
      15 |     1 |
      18 |     1 |
      21 |     1 |
      63 |     1 |

-- Poisson: discrete event counts, peaks at λ.
SELECT
  floor(d.value) AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'poisson'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |    26 | ██
       1 |   112 | ███████████
       2 |   267 | ██████████████████████████
       3 |   417 | ████████████████████████████████████████
       4 |   522 | ██████████████████████████████████████████████████
       5 |   513 | █████████████████████████████████████████████████
       6 |   441 | ██████████████████████████████████████████
       7 |   299 | █████████████████████████████
       8 |   202 | ███████████████████
       9 |    90 | █████████
      10 |    59 | ██████
      11 |    27 | ███
      12 |    16 | ██
      13 |     5 |
      14 |     1 |
      16 |     1 |

-- Random walk: stateful drift from start value.
SELECT
  floor(d.value / 3) * 3 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'rwalk'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
      18 |    13 | 
      21 |    54 | ████
      24 |    32 | ██
      27 |    25 | ██
      30 |    39 | ███
      33 |    46 | ███
      36 |    45 | ███
      39 |   270 | ███████████████████
      42 |   392 | ████████████████████████████
      45 |   542 | ██████████████████████████████████████
      48 |   663 | ███████████████████████████████████████████████
      51 |   567 | ████████████████████████████████████████
      54 |   706 | ██████████████████████████████████████████████████
      57 |   418 | ██████████████████████████████
      60 |   300 | █████████████████████
      63 |   114 | ████████

-- Uniform: flat, every value equally likely.
SELECT
  floor(d.value / 7) * 7 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'uniform'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |   211 | █████████████████████████████████████████████
       7 |   198 | ██████████████████████████████████████████
      14 |   214 | ██████████████████████████████████████████████
      21 |   222 | ████████████████████████████████████████████████
      28 |   212 | █████████████████████████████████████████████
      35 |   224 | ████████████████████████████████████████████████
      42 |   208 | █████████████████████████████████████████████
      49 |   203 | ████████████████████████████████████████████
      56 |   233 | ██████████████████████████████████████████████████
      63 |   177 | ██████████████████████████████████████
      70 |   205 | ████████████████████████████████████████████
      77 |   203 | ████████████████████████████████████████████
      84 |   191 | █████████████████████████████████████████
      91 |   204 | ████████████████████████████████████████████
      98 |    73 | ████████████████

-- Weibull: reliability / time-to-failure.
SELECT
  floor(d.value / 7) * 7 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'weibull'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |   161 | ███████████████████████
       7 |   287 | ██████████████████████████████████████████
      14 |   305 | ████████████████████████████████████████████
      21 |   318 | ██████████████████████████████████████████████
      28 |   343 | ██████████████████████████████████████████████████
      35 |   288 | ██████████████████████████████████████████
      42 |   300 | ████████████████████████████████████████████
      49 |   243 | ███████████████████████████████████
      56 |   227 | █████████████████████████████████
      63 |   159 | ███████████████████████
      70 |   128 | ███████████████████
      77 |   103 | ███████████████
      84 |   100 | ███████████████
      91 |    69 | ██████████
      98 |    17 | ██

-- Zipfian: power-law skew, low values dominate.
SELECT
  floor(d.value / 7) * 7 AS bucket,
  count(*) AS total,
  repeat('█', (count(*) * 50 / max(count(*)) OVER ())::INT) AS histogram
FROM distributions d
WHERE d.dist_type = 'zipfian'
GROUP BY 1
ORDER BY 1;

  bucket | total |                     histogram
---------+-------+-----------------------------------------------------
       0 |  2620 | ██████████████████████████████████████████████████
       7 |   574 | ███████████
      14 |   331 | ██████
      21 |   236 | █████
      28 |   187 | ████
      35 |   133 | ███
      42 |    96 | ██
      49 |    99 | ██
      56 |    85 | ██
      63 |    64 | 
      70 |    71 | 
      77 |    66 | 
      84 |    63 | 
      91 |    41 | 
      98 |    17 |