从 22.10 开始,ClickHouse 增加了强大的功能来生成具有高度灵活性的随机数据。
均匀随机分布
randCanonical
ClickHouse 具有所有数据库和编程语言都具有的规范随机函数。使用randCanonical
函数可以返回 [0, 1) 区间均匀分布的伪随机值:
SELECT randCanonical()
randUniform
生成 X…Y 范围内的随机数,区间是左闭右开,使用randUniform
函数可以返回 [X, Y) 区间均匀分布的 float 类型值:
SELECT randUniform(5,10)
floor
生成随机整数,可以使用floor()
函数进行取整,可以返回 [X, Y) 区间的整数
SELECT floor(randUniform(X, Y)) AS r
非均匀随机分布
ClickHouse 的 22.10 版本提供了能够生成非均匀(和连续)分布的随机函数。
randNormal
该函数将平均值作为第一个参数,将方差作为第二个参数,输出平均值附近的浮点数。以 randNormal(X, Y)
为例看下生成的数据分布情况:
SELECT
floor(randNormal(100, 5)) AS k,
count(*) AS c,
bar(c, 0, 50000, 100)
FROM numbers(100000) GROUP BY k ORDER BY k ASC
执行结果:
┌───k─┬────c─┬─bar(count(), 0, 50000, 100)─┐
│ 79 │ 1 │ │
│ 80 │ 5 │ │
│ 81 │ 8 │ │
│ 82 │ 17 │ │
│ 83 │ 35 │ │
│ 84 │ 80 │ ▏ │
│ 85 │ 130 │ ▎ │
│ 86 │ 208 │ ▍ │
│ 87 │ 362 │ ▋ │
│ 88 │ 559 │ █ │
│ 89 │ 868 │ █▋ │
│ 90 │ 1347 │ ██▋ │
│ 91 │ 1798 │ ███▌ │
│ 92 │ 2648 │ █████▎ │
│ 93 │ 3471 │ ██████▊ │
│ 94 │ 4365 │ ████████▋ │
│ 95 │ 5285 │ ██████████▌ │
│ 96 │ 6081 │ ████████████▏ │
│ 97 │ 7019 │ ██████████████ │
│ 98 │ 7599 │ ███████████████▏ │
│ 99 │ 8072 │ ████████████████▏ │
│ 100 │ 7909 │ ███████████████▋ │
│ 101 │ 7565 │ ███████████████▏ │
│ 102 │ 6994 │ █████████████▊ │
│ 103 │ 6240 │ ████████████▍ │
│ 104 │ 5384 │ ██████████▋ │
│ 105 │ 4411 │ ████████▋ │
│ 106 │ 3507 │ ███████ │
│ 107 │ 2595 │ █████▏ │
│ 108 │ 1890 │ ███▋ │
│ 109 │ 1337 │ ██▋ │
│ 110 │ 890 │ █▋ │
│ 111 │ 538 │ █ │
│ 112 │ 336 │ ▋ │
│ 113 │ 221 │ ▍ │
│ 114 │ 103 │ ▏ │
│ 115 │ 63 │ ▏ │
│ 116 │ 24 │ │
│ 117 │ 26 │ │
│ 118 │ 4 │ │
│ 119 │ 4 │ │
│ 121 │ 1 │ │
└─────┴──────┴─────────────────────────────┘
这里生成 10W 个随机数randNormal()
,对它们进行四舍五入并计算每个数字出现的次数。我们看到大多数时候,该函数会生成一个更接近给定均值的随机数,这正是正态分布的工作原理。当我们对自变量求和时(例如聚合系统中的错误类型),就会出现正态分布。
randBinomial
二项分布,经常用于模拟一系列是或否问题中成功次数的概率。在对抛硬币建模时,通常用于对正面朝上的总数建模。可视化时类似于正态分布。
SELECT
floor(randBinomial(100, 0.85)) AS k,
bar(count(*), 0, 50000, 100) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1──────────────────────┐
│ 68 │ │
│ 70 │ │
│ 71 │ │
│ 72 │ │
│ 73 │ ▏ │
│ 74 │ ▎ │
│ 75 │ ▋ │
│ 76 │ █▏ │
│ 77 │ █▊ │
│ 78 │ ███▍ │
│ 79 │ █████▎ │
│ 80 │ ███████▊ │
│ 81 │ ███████████▏ │
│ 82 │ ███████████████ │
│ 83 │ ██████████████████▏ │
│ 84 │ █████████████████████ │
│ 85 │ ██████████████████████▏ │
│ 86 │ █████████████████████▋ │
│ 87 │ ███████████████████▋ │
│ 88 │ ████████████████▋ │
│ 89 │ ████████████▋ │
│ 90 │ █████████ │
│ 91 │ █████▍ │
│ 92 │ ██▊ │
│ 93 │ █▍ │
│ 94 │ ▌ │
│ 95 │ ▏ │
│ 96 │ │
│ 97 │ │
│ 98 │ │
└────┴─────────────────────────┘
randNegativeBinomial
负二项式分布,但用于模拟实现特定二元事件的尝试次数,例如在序列中获得指定数量的反面所需的抛硬币次数。
SELECT
floor(randNegativeBinomial(100, 0.85)) AS k,
bar(count(*), 0, 50000, 100) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1─────────────────┐
│ 3 │ │
│ 4 │ │
│ 5 │ │
│ 6 │ ▎ │
│ 7 │ ▋ │
│ 8 │ █▍ │
│ 9 │ ██▋ │
│ 10 │ ████▎ │
│ 11 │ ██████▎ │
│ 12 │ ████████▊ │
│ 13 │ ███████████▌ │
│ 14 │ █████████████▊ │
│ 15 │ ████████████████▏ │
│ 16 │ █████████████████ │
│ 17 │ █████████████████▋ │
│ 18 │ █████████████████▍ │
│ 19 │ ███████████████▊ │
│ 20 │ ██████████████▏ │
│ 21 │ ████████████▏ │
│ 22 │ ██████████▎ │
│ 23 │ ███████▋ │
│ 24 │ ██████▎ │
│ 25 │ ████▍ │
│ 26 │ ███▎ │
│ 27 │ ██▍ │
│ 28 │ █▋ │
│ 29 │ ▊ │
│ 30 │ ▋ │
│ 31 │ ▍ │
│ 32 │ ▏ │
│ 33 │ ▏ │
│ 34 │ │
│ 35 │ │
│ 36 │ │
│ 37 │ │
│ 38 │ │
│ 39 │ │
│ 40 │ │
│ 41 │ │
│ 43 │ │
└────┴────────────────────┘
randLogNormal
对数正态分布,通常可用于模拟自然现象,例如失败率、游戏时长和收入分布。
SELECT
floor(randLogNormal(1 / 100, 0.75)) AS k,
bar(count(*), 0, 50000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1─────────┐
│ 0 │ █████████▊ │
│ 1 │ ██████▍ │
│ 2 │ ██▏ │
│ 3 │ ▋ │
│ 4 │ ▎ │
│ 5 │ ▏ │
│ 6 │ │
│ 7 │ │
│ 8 │ │
│ 9 │ │
│ 10 │ │
│ 11 │ │
│ 12 │ │
│ 13 │ │
│ 14 │ │
│ 15 │ │
│ 16 │ │
│ 17 │ │
│ 18 │ │
│ 22 │ │
│ 23 │ │
│ 25 │ │
│ 31 │ │
│ 34 │ │
└────┴────────────┘
randExponential
指数分布,可用于为客户的电话通话时长或销售总额建模。
SELECT
floor(randExponential(1 / 2)) AS k,
bar(count(*), 0, 50000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1───────┐
│ 0 │ ███████▋ │
│ 1 │ ████▋ │
│ 2 │ ██▊ │
│ 3 │ █▋ │
│ 4 │ █ │
│ 5 │ ▋ │
│ 6 │ ▍ │
│ 7 │ ▏ │
│ 8 │ ▏ │
│ 9 │ │
│ 10 │ │
│ 11 │ │
│ 12 │ │
│ 13 │ │
│ 14 │ │
│ 15 │ │
│ 16 │ │
│ 17 │ │
│ 18 │ │
│ 19 │ │
│ 20 │ │
│ 21 │ │
│ 22 │ │
│ 26 │ │
└────┴──────────┘
randChiSquared
卡方分布,k 个独立标准正态随机变量的平方和的分布。这主要用于测试统计假设,特别是数据集是否与分布匹配。
SELECT
floor(randChiSquared(10)) AS k,
bar(count(*), 0, 10000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1─────────┐
│ 0 │ │
│ 1 │ ▎ │
│ 2 │ █▍ │
│ 3 │ ███▍ │
│ 4 │ █████▋ │
│ 5 │ ███████▋ │
│ 6 │ ████████▊ │
│ 7 │ █████████▌ │
│ 8 │ █████████▋ │
│ 9 │ █████████ │
│ 10 │ ████████▏ │
│ 11 │ ███████▎ │
│ 12 │ ██████▏ │
│ 13 │ ████▊ │
│ 14 │ ████ │
│ 15 │ ███▏ │
│ 16 │ ██▌ │
│ 17 │ █▊ │
│ 18 │ █▍ │
│ 19 │ █ │
│ 20 │ ▋ │
│ 21 │ ▌ │
│ 22 │ ▍ │
│ 23 │ ▎ │
│ 24 │ ▏ │
│ 25 │ ▏ │
│ 26 │ ▏ │
│ 27 │ │
│ 28 │ │
│ 29 │ │
│ 30 │ │
│ 31 │ │
│ 32 │ │
│ 33 │ │
│ 34 │ │
│ 35 │ │
│ 36 │ │
│ 37 │ │
│ 38 │ │
│ 39 │ │
└────┴────────────┘
randStudentT
学生T分布。
SELECT
floor(randStudentT(4.5)) AS k,
bar(count(*), 0, 10000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌───k─┬─b1─────────┐
│ -18 │ │
│ -17 │ │
│ -15 │ │
│ -14 │ │
│ -12 │ │
│ -11 │ │
│ -10 │ │
│ -9 │ │
│ -8 │ │
│ -7 │ │
│ -6 │ ▏ │
│ -5 │ ▎ │
│ -4 │ █▏ │
│ -3 │ ███▋ │
│ -2 │ ██████████ │
│ -1 │ ██████████ │
│ 0 │ ██████████ │
│ 1 │ ██████████ │
│ 2 │ ███▋ │
│ 3 │ █▏ │
│ 4 │ ▎ │
│ 5 │ ▏ │
│ 6 │ │
│ 7 │ │
│ 8 │ │
│ 9 │ │
│ 10 │ │
│ 11 │ │
│ 12 │ │
│ 13 │ │
│ 14 │ │
│ 17 │ │
│ 20 │ │
└─────┴────────────┘
randFisherF
F分布,主要用于统计检验,以评估两个总体的变异在分布方面是否相同。
SELECT
floor(randFisherF(3, 20)) AS k,
bar(count(*), 0, 10000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1─────────┐
│ 0 │ ██████████ │
│ 1 │ ██████████ │
│ 2 │ █████████ │
│ 3 │ ███▏ │
│ 4 │ █▎ │
│ 5 │ ▍ │
│ 6 │ ▏ │
│ 7 │ │
│ 8 │ │
│ 9 │ │
│ 10 │ │
│ 11 │ │
│ 12 │ │
│ 13 │ │
│ 21 │ │
└────┴────────────┘
randPoisson
泊松分布,可用于模拟一些随时间变化的特定事件(例如足球比赛中的进球)或事件之间的间隔(例如日志消息)。
SELECT
floor(randPoisson(10)) AS k,
bar(count(*), 0, 15000, 10) AS b1
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌──k─┬─b1────────┐
│ 0 │ │
│ 1 │ │
│ 2 │ ▏ │
│ 3 │ ▌ │
│ 4 │ █▏ │
│ 5 │ ██▌ │
│ 6 │ ████▏ │
│ 7 │ ██████ │
│ 8 │ ███████▌ │
│ 9 │ ████████▎ │
│ 10 │ ████████▍ │
│ 11 │ ███████▌ │
│ 12 │ ██████▎ │
│ 13 │ ████▋ │
│ 14 │ ███▌ │
│ 15 │ ██▎ │
│ 16 │ █▍ │
│ 17 │ ▋ │
│ 18 │ ▍ │
│ 19 │ ▏ │
│ 20 │ │
│ 21 │ │
│ 22 │ │
│ 23 │ │
│ 24 │ │
│ 25 │ │
└────┴───────────┘
randBernoulli
薄伯努利分布,可用于对特定操作的失败和成功进行建模。
SELECT
floor(randBernoulli(0.75)) AS k,
count(*) AS c
FROM numbers(100000)
GROUP BY k
ORDER BY k ASC
执行结果:
┌─k─┬─────c─┐
│ 0 │ 24976 │
│ 1 │ 75024 │
└───┴───────┘
生成随机数据
我们可以根据我们的要求使用任何给定的随机生成器,并用测试数据填充我们的表。让我们填充一个 purchases
代表产品销售的表:
CREATE TABLE purchases
(
`dt` DateTime,
`customer_id` UInt32,
`total_spent` Float32
)
ENGINE = MergeTree
ORDER BY dt
我们将使用 randExponential()
函数为列生成数据 total_spent
以模拟客户销售的分布:
INSERT INTO purchases SELECT
now() - randUniform(1, 1000000.),
number,
15 + round(randExponential(1 / 10), 2)
FROM numbers(1000000)
我们使用客户 ID 的序列号和统一的时间随机偏移来传播数据。我们可以看到 total_spent
价值按照指数规律分布,趋向于价值 15(假设 $15.00
是可以花费的最低价值):
SELECT
floor(total_spent) AS s,
count(*) AS n,
bar(n, 0, 350000, 50)
FROM purchases
GROUP BY s
ORDER BY s ASC
执行结果:
┌───s─┬─────n─┬─bar(count(), 0, 350000, 50)─┐
│ 15 │ 94520 │ █████████████▌ │
│ 16 │ 86711 │ ████████████▍ │
│ 17 │ 77290 │ ███████████ │
│ 18 │ 70446 │ ██████████ │
│ 19 │ 63580 │ █████████ │
│ 20 │ 57635 │ ████████▏ │
│ 21 │ 52518 │ ███████▌ │
│ 22 │ 47777 │ ██████▋ │
│ 23 │ 42808 │ ██████ │
│ 24 │ 38729 │ █████▌ │
│ 25 │ 34912 │ ████▊ │
│ 26 │ 31604 │ ████▌ │
│ 27 │ 28542 │ ████ │
│ 28 │ 26128 │ ███▋ │
│ 29 │ 23520 │ ███▎ │
│ 30 │ 21383 │ ███ │
│ 31 │ 19207 │ ██▋ │
│ 32 │ 17156 │ ██▍ │
│ 33 │ 15656 │ ██▏ │
│ 34 │ 14171 │ ██ │
│ 35 │ 12855 │ █▋ │
│ 36 │ 11772 │ █▋ │
│ 37 │ 10481 │ █▍ │
│ 38 │ 9542 │ █▎ │
│ 39 │ 8538 │ █▏ │
│ 40 │ 7854 │ █ │
│ 41 │ 7064 │ █ │
│ 42 │ 6467 │ ▊ │
│ 43 │ 5901 │ ▋ │
│ 44 │ 5418 │ ▋ │
│ 45 │ 4838 │ ▋ │
│ 46 │ 4198 │ ▌ │
│ 47 │ 3760 │ ▌ │
│ 48 │ 3542 │ ▌ │
│ 49 │ 3188 │ ▍ │
│ 50 │ 2858 │ ▍ │
│ 51 │ 2631 │ ▍ │
│ 52 │ 2347 │ ▎ │
│ 53 │ 2175 │ ▎ │
│ 54 │ 1896 │ ▎ │
│ 55 │ 1723 │ ▏ │
│ 56 │ 1611 │ ▏ │
│ 57 │ 1408 │ ▏ │
│ 58 │ 1253 │ ▏ │
│ 59 │ 1246 │ ▏ │
│ 60 │ 1089 │ ▏ │
│ 61 │ 976 │ ▏ │
│ 62 │ 859 │ │
│ 63 │ 785 │ │
│ 64 │ 741 │ │
│ 65 │ 666 │ │
│ 66 │ 553 │ │
│ 67 │ 524 │ │
│ 68 │ 479 │ │
│ 69 │ 394 │ │
│ 70 │ 386 │ │
│ 71 │ 356 │ │
│ 72 │ 305 │ │
│ 73 │ 307 │ │
│ 74 │ 244 │ │
│ 75 │ 233 │ │
│ 76 │ 214 │ │
│ 77 │ 189 │ │
│ 78 │ 160 │ │
│ 79 │ 154 │ │
│ 80 │ 136 │ │
│ 81 │ 131 │ │
│ 82 │ 118 │ │
│ 83 │ 121 │ │
│ 84 │ 110 │ │
│ 85 │ 90 │ │
│ 86 │ 67 │ │
│ 87 │ 76 │ │
│ 88 │ 62 │ │
│ 89 │ 73 │ │
│ 90 │ 60 │ │
│ 91 │ 52 │ │
│ 92 │ 45 │ │
│ 93 │ 41 │ │
│ 94 │ 44 │ │
│ 95 │ 23 │ │
│ 96 │ 22 │ │
│ 97 │ 24 │ │
│ 98 │ 14 │ │
│ 99 │ 16 │ │
│ 100 │ 16 │ │
│ 101 │ 28 │ │
│ 102 │ 16 │ │
│ 103 │ 16 │ │
│ 104 │ 10 │ │
│ 105 │ 17 │ │
│ 106 │ 10 │ │
│ 107 │ 8 │ │
│ 108 │ 5 │ │
│ 109 │ 12 │ │
│ 110 │ 6 │ │
│ 111 │ 3 │ │
│ 112 │ 5 │ │
│ 113 │ 3 │ │
│ 114 │ 4 │ │
│ 115 │ 4 │ │
│ 116 │ 6 │ │
│ 117 │ 4 │ │
│ 118 │ 2 │ │
│ 119 │ 3 │ │
│ 120 │ 3 │ │
│ 121 │ 2 │ │
│ 122 │ 1 │ │
│ 123 │ 2 │ │
│ 124 │ 1 │ │
│ 125 │ 1 │ │
│ 128 │ 2 │ │
│ 129 │ 2 │ │
│ 130 │ 1 │ │
│ 131 │ 2 │ │
│ 132 │ 1 │ │
│ 135 │ 1 │ │
│ 137 │ 1 │ │
│ 141 │ 1 │ │
│ 154 │ 1 │ │
│ 155 │ 2 │ │
└─────┴───────┴─────────────────────────────┘
请注意我们如何使用指数分布来逐渐减少总支出。我们可以使用正态分布(使用 randNormal()
函数)或任何其他分布来获得不同的峰值和形式。
生成时间分布的数据
虽然在我们之前的示例中,我们使用随机分布对值进行建模,但我们也可以对时间进行建模。假设我们将客户端事件收集到下表中:
CREATE TABLE events
(
`dt` DateTime,
`event` String
)
ENGINE = MergeTree
ORDER BY dt
实际上,一天中的特定时间可能会发生更多事件。泊松分布是对时间上的一系列独立事件建模的好方法。要模拟时间分布,我们只需将生成的随机值添加到时间列:
INSERT INTO events SELECT
toDateTime('2022-12-12 12:00:00') - (((12 + randPoisson(12)) * 60) * 60),
'click'
FROM numbers(100000)
在这里,我们插入了 10 万个点击事件,这些事件分布在大约 24 小时内,中午是事件高峰期(在我们的示例中为 12:00):
SELECT
toStartOfHour(dt) AS hour,
count(*) AS c,
bar(c, 0, 15000, 50)
FROM events
GROUP BY hour
ORDER BY hour ASC
执行结果:
┌────────────────hour─┬─────c─┬─bar(count(), 0, 15000, 50)──────────────┐
│ 2022-12-10 16:00:00 │ 1 │ │
│ 2022-12-10 20:00:00 │ 3 │ │
│ 2022-12-10 21:00:00 │ 13 │ │
│ 2022-12-10 22:00:00 │ 19 │ │
│ 2022-12-10 23:00:00 │ 42 │ ▏ │
│ 2022-12-11 00:00:00 │ 71 │ ▏ │
│ 2022-12-11 01:00:00 │ 183 │ ▌ │
│ 2022-12-11 02:00:00 │ 289 │ ▊ │
│ 2022-12-11 03:00:00 │ 543 │ █▋ │
│ 2022-12-11 04:00:00 │ 971 │ ███▏ │
│ 2022-12-11 05:00:00 │ 1606 │ █████▎ │
│ 2022-12-11 06:00:00 │ 2662 │ ████████▋ │
│ 2022-12-11 07:00:00 │ 3830 │ ████████████▋ │
│ 2022-12-11 08:00:00 │ 5342 │ █████████████████▋ │
│ 2022-12-11 09:00:00 │ 7214 │ ████████████████████████ │
│ 2022-12-11 10:00:00 │ 8896 │ █████████████████████████████▋ │
│ 2022-12-11 11:00:00 │ 10563 │ ███████████████████████████████████▏ │
│ 2022-12-11 12:00:00 │ 11502 │ ██████████████████████████████████████▎ │
│ 2022-12-11 13:00:00 │ 11532 │ ██████████████████████████████████████▍ │
│ 2022-12-11 14:00:00 │ 10581 │ ███████████████████████████████████▎ │
│ 2022-12-11 15:00:00 │ 8729 │ █████████████████████████████ │
│ 2022-12-11 16:00:00 │ 6618 │ ██████████████████████ │
│ 2022-12-11 17:00:00 │ 4304 │ ██████████████▎ │
│ 2022-12-11 18:00:00 │ 2536 │ ████████▍ │
│ 2022-12-11 19:00:00 │ 1220 │ ████ │
│ 2022-12-11 20:00:00 │ 516 │ █▋ │
│ 2022-12-11 21:00:00 │ 165 │ ▌ │
│ 2022-12-11 22:00:00 │ 41 │ ▏ │
│ 2022-12-11 23:00:00 │ 7 │ │
│ 2022-12-12 00:00:00 │ 1 │ │
└─────────────────────┴───────┴─────────────────────────────────────────┘
在这种情况下,我们没有生成值,而是使用随机函数在计算的时间点插入新记录。
生成时间相关值
在前面的示例的基础上,我们可以使用分布来生成依赖于时间的值。例如,假设我们要将硬件指标收集(如 CPU 利用率或 RAM 使用率)模拟到下表中:
CREATE TABLE metrics
(
`name` String,
`dt` DateTime,
`val` Float32
)
ENGINE = MergeTree
ORDER BY (name, dt)
在实际情况下,我们肯定会遇到 CPU 满载时的高峰时段和负载较低的时段。为了对此建模,我们可以使用所需分布的随机函数来计算度量值和时间点值:
INSERT INTO metrics SELECT
'cpu',
t + ((60 * 60) * randCanonical()) AS t,
round(v * (0.95 + (randCanonical() / 20)), 2) AS v
FROM
(
SELECT
toDateTime('2022-12-12 12:00:00') - toIntervalHour(k) AS t,
round((100 * c) / m, 2) AS v
FROM
(
SELECT
k,
c,
max(c) OVER () AS m
FROM
(
SELECT
floor(randBinomial(24, 0.5) - 12) AS k,
count(*) AS c
FROM numbers(1000)
GROUP BY k
ORDER BY k ASC
)
)
) AS a
INNER JOIN numbers(1000000) AS b ON 1 = 1
在这里,我们生成 1K 个二项式分布的随机值以获得每个生成的数字及其关联的计数。然后我们使用窗口最大值函数计算这些值的最大值,将其作为列添加到每个结果中。最后,在外部查询中,我们根据该计数除以最大值生成一个度量值,以获得 0...100
范围内的随机值,对应于可能的 CPU 负载数据。我们还向数字添加噪声 time
,对 val
使用 randCanonical()
,并与 numbers 表做 join,以生成 1m 度量事件。
SELECT
toStartOfHour(dt) AS h,
round(avg(val), 2) AS v,
bar(v, 0, 100)
FROM metrics
GROUP BY h
ORDER BY h ASC
执行结果:
┌───────────────────h─┬─────v─┬─bar(round(avg(val), 2), 0, 100)────────────────────────────────────────────────┐
│ 2022-12-12 04:00:00 │ 1.78 │ █▍ │
│ 2022-12-12 05:00:00 │ 0.59 │ ▍ │
│ 2022-12-12 06:00:00 │ 5.35 │ ████▎ │
│ 2022-12-12 07:00:00 │ 10.11 │ ████████ │
│ 2022-12-12 08:00:00 │ 32.11 │ █████████████████████████▋ │
│ 2022-12-12 09:00:00 │ 50.53 │ ████████████████████████████████████████▍ │
│ 2022-12-12 10:00:00 │ 65.39 │ ████████████████████████████████████████████████████▎ │
│ 2022-12-12 11:00:00 │ 93.34 │ ██████████████████████████████████████████████████████████████████████████▋ │
│ 2022-12-12 12:00:00 │ 97.5 │ ██████████████████████████████████████████████████████████████████████████████ │
│ 2022-12-12 13:00:00 │ 87.98 │ ██████████████████████████████████████████████████████████████████████▍ │
│ 2022-12-12 14:00:00 │ 58.86 │ ███████████████████████████████████████████████ │
│ 2022-12-12 15:00:00 │ 51.13 │ ████████████████████████████████████████▉ │
│ 2022-12-12 16:00:00 │ 23.18 │ ██████████████████▌ │
│ 2022-12-12 17:00:00 │ 13.07 │ ██████████▍ │
│ 2022-12-12 18:00:00 │ 2.97 │ ██▍ │
│ 2022-12-12 21:00:00 │ 0.59 │ ▍ │
└─────────────────────┴───────┴────────────────────────────────────────────────────────────────────────────────┘
生成多模态分布
我们之前的所有示例都生成了具有单个峰值或最优值的数据。多模态分布包含多个峰值,可用于模拟真实世界的事件,例如多个季节性销售高峰。我们可以通过按特定序列号对生成的值进行分组来重复我们生成的数据来实现这一点:
SELECT
floor(randBinomial(24, 0.75)) AS k,
count(*) AS c,
number % 3 AS ord,
bar(c, 0, 10000)
FROM numbers(100000)
GROUP BY
k,
ord
ORDER BY
ord ASC,
k ASC
这将重复我们的二项分布数据三次:
┌──k─┬────c─┬─ord─┬─bar(count(), 0, 10000)─────────────────────────────┐
│ 7 │ 1 │ 0 │ │
│ 8 │ 1 │ 0 │ │
│ 9 │ 5 │ 0 │ │
│ 10 │ 12 │ 0 │ │
│ 11 │ 44 │ 0 │ ▎ │
│ 12 │ 162 │ 0 │ █▎ │
│ 13 │ 440 │ 0 │ ███▌ │
│ 14 │ 1059 │ 0 │ ████████▍ │
│ 15 │ 2282 │ 0 │ ██████████████████▎ │
│ 16 │ 3802 │ 0 │ ██████████████████████████████▍ │
│ 17 │ 5380 │ 0 │ ███████████████████████████████████████████ │
│ 18 │ 6126 │ 0 │ █████████████████████████████████████████████████ │
│ 19 │ 5793 │ 0 │ ██████████████████████████████████████████████▎ │
│ 20 │ 4372 │ 0 │ ██████████████████████████████████▊ │
│ 21 │ 2542 │ 0 │ ████████████████████▎ │
│ 22 │ 1002 │ 0 │ ████████ │
│ 23 │ 277 │ 0 │ ██▏ │
│ 24 │ 34 │ 0 │ ▎ │
│ 8 │ 1 │ 1 │ │
│ 9 │ 2 │ 1 │ │
│ 10 │ 10 │ 1 │ │
│ 11 │ 39 │ 1 │ ▎ │
│ 12 │ 153 │ 1 │ █▏ │
│ 13 │ 435 │ 1 │ ███▍ │
│ 14 │ 1120 │ 1 │ ████████▊ │
│ 15 │ 2220 │ 1 │ █████████████████▋ │
│ 16 │ 3768 │ 1 │ ██████████████████████████████▏ │
│ 17 │ 5352 │ 1 │ ██████████████████████████████████████████▋ │
│ 18 │ 6080 │ 1 │ ████████████████████████████████████████████████▋ │
│ 19 │ 5988 │ 1 │ ███████████████████████████████████████████████▊ │
│ 20 │ 4318 │ 1 │ ██████████████████████████████████▌ │
│ 21 │ 2537 │ 1 │ ████████████████████▎ │
│ 22 │ 994 │ 1 │ ███████▊ │
│ 23 │ 285 │ 1 │ ██▎ │
│ 24 │ 31 │ 1 │ ▏ │
│ 8 │ 1 │ 2 │ │
│ 9 │ 1 │ 2 │ │
│ 10 │ 17 │ 2 │ ▏ │
│ 11 │ 52 │ 2 │ ▍ │
│ 12 │ 211 │ 2 │ █▋ │
│ 13 │ 474 │ 2 │ ███▋ │
│ 14 │ 1110 │ 2 │ ████████▊ │
│ 15 │ 2242 │ 2 │ █████████████████▊ │
│ 16 │ 3741 │ 2 │ █████████████████████████████▊ │
│ 17 │ 5306 │ 2 │ ██████████████████████████████████████████▍ │
│ 18 │ 6256 │ 2 │ ██████████████████████████████████████████████████ │
│ 19 │ 5779 │ 2 │ ██████████████████████████████████████████████▏ │
│ 20 │ 4360 │ 2 │ ██████████████████████████████████▊ │
│ 21 │ 2461 │ 2 │ ███████████████████▋ │
│ 22 │ 1035 │ 2 │ ████████▎ │
│ 23 │ 255 │ 2 │ ██ │
│ 24 │ 32 │ 2 │ ▎ │
└────┴──────┴─────┴────────────────────────────────────────────────────┘
模拟二进制状态
该 randBernoulli()
函数返回 0
或 1
基于给定的概率,例如,如果我们想获得 1
90% 的情况,我们使用:
SELECT randBernoulli(0.9)
这在为二进制状态(例如失败或成功的交易)生成数据时非常有用:
SELECT
If(randBernoulli(0.95), 'success', 'failure') AS status,
count(*) AS c
FROM numbers(1000)
GROUP BY status
执行结果:
┌─status──┬───c─┐
│ failure │ 52 │
│ success │ 948 │
└─────────┴─────┘
在这里,我们生成 95% 的 success
状态和仅 5% 的 failure
。
为枚举生成随机值
我们可以结合使用数组和随机函数来从某个子集中获取值,并使用它来填充 ENUM 列:
SELECT
['200', '404', '502', '403'][toInt32(randBinomial(4, 0.1)) + 1] AS http_code,
count(*) AS c
FROM numbers(1000)
GROUP BY http_code
执行结果:
┌─http_code─┬───c─┐
│ 403 │ 3 │
│ 502 │ 49 │
│ 200 │ 685 │
│ 404 │ 263 │
└───────────┴─────┘
在这里,我们使用二项分布来获取具有 4 种可能的 HTTP 响应代码之一的请求数。我们通常会期望 200 多于错误,因此会这样建模。
生成随机字符串
ClickHouse 还允许使用 randomString()
,randomStringUTF8()
和 randomPrintableASCII()
函数生成随机字符串。所有函数都接受字符串长度作为参数。要创建具有随机字符串的数据集,我们可以将字符串生成与随机函数结合起来以获得任意长度的字符串。下面我们使用这种方法生成 10 个随机字符串,可读字符,长度为 5 到 25 个符号:
SELECT
randomPrintableASCII(randUniform(5, 25)) AS s,
length(s) AS length
FROM numbers(10)
执行结果:
┌─s──────────────────┬─length─┐
│ 3{
{mc │ 5 │
│ F7g S)a:i*q/)_'g: │ 17 │
│ :ccV^f4{vpwgr'Qq#M │ 18 │
│ G=G': │ 5 │
│ }6o,0yMDo*x`!BqnY$ │ 18 │
│ \7Y5]" │ 6 │
│ kkS3q?fE+%4hD6ItJA │ 18 │
│ .<S<+n&eyu59=*6g │ 16 │
│ [!cBR │ 5 │
│ +,hD}`7#B+HYv$ │ 14 │
└────────────────────┴────────┘
生成噪声数据
在现实世界中,数据总是包含错误。这可以在 ClickHouse 中使用 fuzzBits()
函数进行模拟。此函数可以通过以指定概率随机移位位,根据用户指定的有效值生成错误数据。假设我们要向字符串字段值添加错误。以下将根据我们的初始值随机生成错误:
SELECT fuzzBits('Good string', 0.01)
FROM numbers(10)
执行结果:
┌─fuzzBits('Good string', 0.01)─┐
│ Good string │
│ Good string │
│ Eood string │
│ Good string │
│ Good string │
│ Good strkng │
│ Good string │
│ G/od string │
│ Good!strino │
│ Good string │
└───────────────────────────────┘
请务必调整概率,因为生成的错误数量取决于您传递给函数的值的长度。使用较低的值以获得较少错误的概率:
SELECT
IF(fuzzBits('Good string', 0.001) = 'Good string', 1, 0) AS has_errors,
count(*)
FROM numbers(1000)
GROUP BY has_errors
执行结果:
┌─has_errors─┬─count()─┐
│ 0 │ 279 │
│ 1 │ 721 │
└────────────┴─────────┘
在这里,我们使用 0.001 的概率得到约 25% 的错误值。
生成真实数据集
总结一下,让我们模拟一个 30 天的点击流,该点击流在一天内具有接近真实世界的分布,峰值在中午。我们将为此使用正态分布。每个事件也将具有两种可能状态 success
或 fail
,使用伯努利函数分布。我们的表:
CREATE TABLE click_events
(
`dt` DateTime,
`event` String,
`status` Enum8('success' = 1, 'fail' = 2)
)
ENGINE = MergeTree
ORDER BY dt
让我们用 1000 万个事件填充此表:
INSERT INTO click_events SELECT
(parseDateTimeBestEffortOrNull('12:00') - toIntervalHour(randNormal(0, 3))) - toIntervalDay(number % 30),
'Click',
['fail', 'success'][randBernoulli(0.9) + 1]
FROM numbers(10000000)
我们使用 randBernoulli()
了 90% 的成功概率,因此我们将在 10 次中 success
的第 9 列中获得值。status
我们已经习惯于randNormal()
生成事件的分布。让我们使用以下查询可视化该数据:
SELECT
dt,
count(*) AS c,
bar(c, 0, 100000)
FROM click_events
GROUP BY dt
ORDER BY dt ASC
执行结果:
┌──────────────────dt─┬─────c─┬─bar(count(), 0, 100000)────────────────────────────────────────────────┐
│ 1999-12-02 22:00:00 │ 1 │ │
│ 1999-12-02 23:00:00 │ 1 │ │
│ 1999-12-03 00:00:00 │ 6 │ │
│ 1999-12-03 01:00:00 │ 27 │ │
│ 1999-12-03 02:00:00 │ 110 │ │
│ 1999-12-03 03:00:00 │ 321 │ ▎ │
│ 1999-12-03 04:00:00 │ 866 │ ▋ │
│ 1999-12-03 05:00:00 │ 1965 │ █▌ │
│ 1999-12-03 06:00:00 │ 4417 │ ███▌ │
│ 1999-12-03 07:00:00 │ 8372 │ ██████▋ │
│ 1999-12-03 08:00:00 │ 14392 │ ███████████▌ │
│ 1999-12-03 09:00:00 │ 22358 │ █████████████████▉ │
│ 1999-12-03 10:00:00 │ 31178 │ ████████████████████████▉ │
│ 1999-12-03 11:00:00 │ 39093 │ ███████████████████████████████▎ │
│ 1999-12-03 12:00:00 │ 87328 │ █████████████████████████████████████████████████████████████████████▊ │
│ 1999-12-03 13:00:00 │ 38772 │ ███████████████████████████████ │
│ 1999-12-03 14:00:00 │ 31445 │ █████████████████████████▏ │
│ 1999-12-03 15:00:00 │ 22470 │ █████████████████▉ │
│ 1999-12-03 16:00:00 │ 14399 │ ███████████▌ │
│ 1999-12-03 17:00:00 │ 8251 │ ██████▌ │
│ 1999-12-03 18:00:00 │ 4296 │ ███▍ │
│ 1999-12-03 19:00:00 │ 1973 │ █▌ │
│ 1999-12-03 20:00:00 │ 823 │ ▋ │
│ 1999-12-03 21:00:00 │ 322 │ ▎ │
│ 1999-12-03 22:00:00 │ 106 │ │
│ 1999-12-03 23:00:00 │ 30 │ │
│ 1999-12-04 00:00:00 │ 22 │ │
......
│ 2000-01-01 00:00:00 │ 18 │ │
│ 2000-01-01 01:00:00 │ 36 │ │
│ 2000-01-01 02:00:00 │ 120 │ │
│ 2000-01-01 03:00:00 │ 302 │ ▏ │
│ 2000-01-01 04:00:00 │ 798 │ ▋ │
│ 2000-01-01 05:00:00 │ 1993 │ █▌ │
│ 2000-01-01 06:00:00 │ 4339 │ ███▍ │
│ 2000-01-01 07:00:00 │ 8280 │ ██████▌ │
│ 2000-01-01 08:00:00 │ 14686 │ ███████████▋ │
│ 2000-01-01 09:00:00 │ 22395 │ █████████████████▉ │
│ 2000-01-01 10:00:00 │ 31481 │ █████████████████████████▏ │
│ 2000-01-01 11:00:00 │ 39121 │ ███████████████████████████████▎ │
│ 2000-01-01 12:00:00 │ 86806 │ █████████████████████████████████████████████████████████████████████▍ │
│ 2000-01-01 13:00:00 │ 39060 │ ███████████████████████████████▏ │
│ 2000-01-01 14:00:00 │ 31020 │ ████████████████████████▊ │
│ 2000-01-01 15:00:00 │ 22618 │ ██████████████████ │
│ 2000-01-01 16:00:00 │ 14543 │ ███████████▋ │
│ 2000-01-01 17:00:00 │ 8235 │ ██████▌ │
│ 2000-01-01 18:00:00 │ 4263 │ ███▍ │
│ 2000-01-01 19:00:00 │ 1947 │ █▌ │
│ 2000-01-01 20:00:00 │ 809 │ ▋ │
│ 2000-01-01 21:00:00 │ 320 │ ▎ │
│ 2000-01-01 22:00:00 │ 114 │ │
│ 2000-01-01 23:00:00 │ 31 │ │
│ 2000-01-02 00:00:00 │ 3 │ │
│ 2000-01-02 01:00:00 │ 3 │ │
└─────────────────────┴───────┴────────────────────────────────────────────────────────────────────────┘
724 rows in set. Elapsed: 0.453 sec.
欢迎添加WX:xiedeyantu,讨论技术问题。