t-digest
t-digest 是一种概率性数据结构,允许您估计数据流的百分位数。
t-digest 是一种 Redis Stack 中的草图数据结构,用于使用紧凑草图从数据流或大型数据集中估计百分位数。
它可以回答诸如以下问题:
- 数据流中哪些比例的值小于给定值?
- 数据流中多少个值小于给定值?
- 数据流中小于 *p* 百分比的值的最高值是多少?(p 百分位数是多少)?
什么是 t-digest?
t-digest 是一种数据结构,它将估计百分位点,而无需存储和排序集合中的所有数据点。例如:要回答问题“99% 的数据库操作的平均延迟是多少”,我们需要存储每个用户的平均延迟,对值进行排序,剔除最后 1%,然后才能找到所有剩余值的平均值。这种过程不仅在对这些值进行排序所需的处理方面成本高昂,而且在存储这些值所需的存储空间方面也成本高昂。这正是 t-digest 解决的问题。
t-digest 还可以用于估计与百分位数相关的其他值,如截断均值。
**截断均值** 是从草图中获得的均值,不包括低于和高于截止百分位数的观测值。例如,0.1 截断均值是草图的均值,不包括最低 10% 和最高 10% 的值。
用例
硬件/软件监控
您测量在线服务器响应延迟,并且想要查询
在线游戏
数百万人在您的在线游戏平台上玩游戏,您想向每个玩家提供以下信息?
网络流量监控
您测量每秒通过网络传输的 IP 数据包,并尝试通过以下方式检测拒绝服务攻击:
预测性维护
示例
在以下示例中,您将创建一个压缩率为 100 的 t-digest 并向其添加项目。COMPRESSION
参数用于指定准确性和内存消耗之间的权衡。默认值为 100。更高的值意味着更高的准确性。注意:与其他一些概率数据结构不同,TDIGEST.ADD
命令如果键不存在,则不会创建新的结构。
> TDIGEST.CREATE bikes:sales COMPRESSION 100
OK
> TDIGEST.ADD bikes:sales 21
OK
> TDIGEST.ADD bikes:sales 150 95 75 34
OK
"""
Code samples for t-digest pages:
https://redis.ac.cn/docs/latest/develop/data-types/probabilistic/t-digest/
"""
import redis
r = redis.Redis(decode_responses=True)
res1 = r.tdigest().create("bikes:sales", 100)
print(res1) # >>> True
res2 = r.tdigest().add("bikes:sales", [21])
print(res2) # >>> OK
res3 = r.tdigest().add("bikes:sales", [150, 95, 75, 34])
print(res3) # >>> OK
res4 = r.tdigest().create("racer_ages")
print(res4) # >>> True
res5 = r.tdigest().add(
"racer_ages",
[
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63,
],
)
print(res5) # >>> OK
res6 = r.tdigest().rank("racer_ages", 50)
print(res6) # >>> [7]
res7 = r.tdigest().rank("racer_ages", 50, 40)
print(res7) # >>> [7, 4]
res8 = r.tdigest().quantile("racer_ages", 0.5)
print(res8) # >>> [44.2]
res9 = r.tdigest().byrank("racer_ages", 4)
print(res9) # >>> [42.63]
res10 = r.tdigest().min("racer_ages")
print(res10) # >>> 19.27
res11 = r.tdigest().max("racer_ages")
print(res11) # >>> 85.71
res12 = r.tdigest().reset("racer_ages")
print(res12) # >>> OK
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
const res1 = await client.tDigest.create('bikes:sales', 100);
console.log(res1); // >>> OK
const res2 = await client.tDigest.add('bikes:sales', [21]);
console.log(res2); // >>> OK
const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);
console.log(res3); // >>> OK
const res4 = await client.tDigest.create('racer_ages');
console.log(res4); // >>> OK
const res5 = await client.tDigest.add('racer_ages', [
45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63
]);
console.log(res5); // >>> OK
const res6 = await client.tDigest.rank('racer_ages', [50]);
console.log(res6); // >>> [7]
const res7 = await client.tDigest.rank('racer_ages', [50, 40]);
console.log(res7); // >>> [7, 4]
const res8 = await client.tDigest.quantile('racer_ages', [0.5]);
console.log(res8); // >>> [44.2]
const res9 = await client.tDigest.byRank('racer_ages', [4]);
console.log(res9); // >>> [42.63]
const res10 = await client.tDigest.min('racer_ages');
console.log(res10); // >>> 19.27
const res11 = await client.tDigest.max('racer_ages');
console.log(res11); // >>> 85.71
const res12 = await client.tDigest.reset('racer_ages');
console.log(res12); // >>> OK
package io.redis.examples;
public class TDigestExample {
public void run(){
UnifiedJedis unifiedJedis = new UnifiedJedis("redis://127.0.0.1:6379");
String res1 = unifiedJedis.tdigestCreate("bikes:sales", 100);
System.out.println(res1); // >>> True
String res2 = unifiedJedis.tdigestAdd("bikes:sales", 21);
System.out.println(res2); // >>> OK
String res3 = unifiedJedis.tdigestAdd("bikes:sales", 150, 95, 75, 34);
System.out.println(res3); // >>> OK
String res4 = unifiedJedis.tdigestCreate("racer_ages");
System.out.println(res4); // >>> True
String res5 = unifiedJedis.tdigestAdd("racer_ages", 45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63);
System.out.println(res5); // >>> OK
List<Long> res6 = unifiedJedis.tdigestRank("racer_ages", 50);
System.out.println(res6); // >>> [7]
List<Long> res7 = unifiedJedis.tdigestRank("racer_ages", 50, 40);
System.out.println(res7); // >>> [7, 4]
List<Double> res8 = unifiedJedis.tdigestQuantile("racer_ages", 0.5);
System.out.println(res8); // >>> [44.2]
List<Double> res9 = unifiedJedis.tdigestByRank("racer_ages", 4);
System.out.println(res9); // >>> [42.63]
double res10 = unifiedJedis.tdigestMin("racer_ages");
System.out.println(res10); // >>> 19.27
double res11 = unifiedJedis.tdigestMax("racer_ages");
System.out.println(res11); // >>> 85.71
String res12 = unifiedJedis.tdigestReset("racer_ages");
System.out.println(res12); // >>> OK
}
}
using NRedisStack.RedisStackCommands;
using NRedisStack.Tests;
using StackExchange.Redis;
public class Tdigest_tutorial
{
[SkipIfRedis(Is.OSSCluster)]
public void run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool res1 = db.TDIGEST().Create("bikes:sales", 100);
Console.WriteLine(res1); // >>> True
bool res2 = db.TDIGEST().Add("bikes:sales", 21);
Console.WriteLine(res2); // >>> True
bool res3 = db.TDIGEST().Add("bikes:sales", 150, 95, 75, 34);
Console.WriteLine(res3); // >>> true
// Tests for 'tdig_start' step.
bool res4 = db.TDIGEST().Create("racer_ages");
Console.WriteLine(res4); // >>> True
bool res5 = db.TDIGEST().Add("racer_ages",
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63
);
Console.WriteLine(res5); // >>> True
long[] res6 = db.TDIGEST().Rank("racer_ages", 50);
Console.WriteLine(string.Join(", ", res6)); // >>> 7
long[] res7 = db.TDIGEST().Rank("racer_ages", 50, 40);
Console.WriteLine(string.Join(", ", res7)); // >>> 7, 4
// Tests for 'tdig_cdf' step.
double[] res8 = db.TDIGEST().Quantile("racer_ages", 0.5); ;
Console.WriteLine(string.Join(", ", res8)); // >>> 44.2
double[] res9 = db.TDIGEST().ByRank("racer_ages", 4);
Console.WriteLine(string.Join(", ", res9)); // >>> 42.63
// Tests for 'tdig_quant' step.
double res10 = db.TDIGEST().Min("racer_ages");
Console.WriteLine(res10); // >>> 19.27
double res11 = db.TDIGEST().Max("racer_ages");
Console.WriteLine(res11); // >>> 85.71
// Tests for 'tdig_min' step.
bool res12 = db.TDIGEST().Reset("racer_ages");
Console.WriteLine(res12); // >>> True
// Tests for 'tdig_reset' step.
}
}
每当有新的观察结果可用时,您可以重复调用 TDIGEST.ADD
通过值估计分数或排名
t-digest 中另一个有用的功能是 CDF(排名的定义),它告诉我们小于或等于某个值的观测值的分数。此命令对于回答诸如“小于或等于 X 的值的观测值的百分比是多少”之类的问题非常有用。
更准确地说,TDIGEST.CDF
将返回草图中小于 X 的观测值的估计分数加上等于 X 的观测值数量的一半。我们还可以使用 TDIGEST.RANK
命令,它非常相似。它不返回分数,而是返回值的 ----估计---- 排名。 TDIGEST.RANK
命令也是可变参数的,这意味着您可以使用单个命令来检索一个或多个值的估计值。
以下是一个示例。给定一组自行车运动员的年龄,您可以问一个问题,例如“小于 50 岁的自行车运动员的百分比是多少?”
> TDIGEST.CREATE racer_ages
OK
> TDIGEST.ADD racer_ages 45.88 44.2 58.03 19.76 39.84 69.28 50.97 25.41 19.27 85.71 42.63
OK
> TDIGEST.CDF racer_ages 50
1) "0.63636363636363635"
> TDIGEST.RANK racer_ages 50
1) (integer) 7
> TDIGEST.RANK racer_ages 50 40
1) (integer) 7
2) (integer) 4
"""
Code samples for t-digest pages:
https://redis.ac.cn/docs/latest/develop/data-types/probabilistic/t-digest/
"""
import redis
r = redis.Redis(decode_responses=True)
res1 = r.tdigest().create("bikes:sales", 100)
print(res1) # >>> True
res2 = r.tdigest().add("bikes:sales", [21])
print(res2) # >>> OK
res3 = r.tdigest().add("bikes:sales", [150, 95, 75, 34])
print(res3) # >>> OK
res4 = r.tdigest().create("racer_ages")
print(res4) # >>> True
res5 = r.tdigest().add(
"racer_ages",
[
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63,
],
)
print(res5) # >>> OK
res6 = r.tdigest().rank("racer_ages", 50)
print(res6) # >>> [7]
res7 = r.tdigest().rank("racer_ages", 50, 40)
print(res7) # >>> [7, 4]
res8 = r.tdigest().quantile("racer_ages", 0.5)
print(res8) # >>> [44.2]
res9 = r.tdigest().byrank("racer_ages", 4)
print(res9) # >>> [42.63]
res10 = r.tdigest().min("racer_ages")
print(res10) # >>> 19.27
res11 = r.tdigest().max("racer_ages")
print(res11) # >>> 85.71
res12 = r.tdigest().reset("racer_ages")
print(res12) # >>> OK
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
const res1 = await client.tDigest.create('bikes:sales', 100);
console.log(res1); // >>> OK
const res2 = await client.tDigest.add('bikes:sales', [21]);
console.log(res2); // >>> OK
const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);
console.log(res3); // >>> OK
const res4 = await client.tDigest.create('racer_ages');
console.log(res4); // >>> OK
const res5 = await client.tDigest.add('racer_ages', [
45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63
]);
console.log(res5); // >>> OK
const res6 = await client.tDigest.rank('racer_ages', [50]);
console.log(res6); // >>> [7]
const res7 = await client.tDigest.rank('racer_ages', [50, 40]);
console.log(res7); // >>> [7, 4]
const res8 = await client.tDigest.quantile('racer_ages', [0.5]);
console.log(res8); // >>> [44.2]
const res9 = await client.tDigest.byRank('racer_ages', [4]);
console.log(res9); // >>> [42.63]
const res10 = await client.tDigest.min('racer_ages');
console.log(res10); // >>> 19.27
const res11 = await client.tDigest.max('racer_ages');
console.log(res11); // >>> 85.71
const res12 = await client.tDigest.reset('racer_ages');
console.log(res12); // >>> OK
package io.redis.examples;
public class TDigestExample {
public void run(){
UnifiedJedis unifiedJedis = new UnifiedJedis("redis://127.0.0.1:6379");
String res1 = unifiedJedis.tdigestCreate("bikes:sales", 100);
System.out.println(res1); // >>> True
String res2 = unifiedJedis.tdigestAdd("bikes:sales", 21);
System.out.println(res2); // >>> OK
String res3 = unifiedJedis.tdigestAdd("bikes:sales", 150, 95, 75, 34);
System.out.println(res3); // >>> OK
String res4 = unifiedJedis.tdigestCreate("racer_ages");
System.out.println(res4); // >>> True
String res5 = unifiedJedis.tdigestAdd("racer_ages", 45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63);
System.out.println(res5); // >>> OK
List<Long> res6 = unifiedJedis.tdigestRank("racer_ages", 50);
System.out.println(res6); // >>> [7]
List<Long> res7 = unifiedJedis.tdigestRank("racer_ages", 50, 40);
System.out.println(res7); // >>> [7, 4]
List<Double> res8 = unifiedJedis.tdigestQuantile("racer_ages", 0.5);
System.out.println(res8); // >>> [44.2]
List<Double> res9 = unifiedJedis.tdigestByRank("racer_ages", 4);
System.out.println(res9); // >>> [42.63]
double res10 = unifiedJedis.tdigestMin("racer_ages");
System.out.println(res10); // >>> 19.27
double res11 = unifiedJedis.tdigestMax("racer_ages");
System.out.println(res11); // >>> 85.71
String res12 = unifiedJedis.tdigestReset("racer_ages");
System.out.println(res12); // >>> OK
}
}
using NRedisStack.RedisStackCommands;
using NRedisStack.Tests;
using StackExchange.Redis;
public class Tdigest_tutorial
{
[SkipIfRedis(Is.OSSCluster)]
public void run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool res1 = db.TDIGEST().Create("bikes:sales", 100);
Console.WriteLine(res1); // >>> True
bool res2 = db.TDIGEST().Add("bikes:sales", 21);
Console.WriteLine(res2); // >>> True
bool res3 = db.TDIGEST().Add("bikes:sales", 150, 95, 75, 34);
Console.WriteLine(res3); // >>> true
// Tests for 'tdig_start' step.
bool res4 = db.TDIGEST().Create("racer_ages");
Console.WriteLine(res4); // >>> True
bool res5 = db.TDIGEST().Add("racer_ages",
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63
);
Console.WriteLine(res5); // >>> True
long[] res6 = db.TDIGEST().Rank("racer_ages", 50);
Console.WriteLine(string.Join(", ", res6)); // >>> 7
long[] res7 = db.TDIGEST().Rank("racer_ages", 50, 40);
Console.WriteLine(string.Join(", ", res7)); // >>> 7, 4
// Tests for 'tdig_cdf' step.
double[] res8 = db.TDIGEST().Quantile("racer_ages", 0.5); ;
Console.WriteLine(string.Join(", ", res8)); // >>> 44.2
double[] res9 = db.TDIGEST().ByRank("racer_ages", 4);
Console.WriteLine(string.Join(", ", res9)); // >>> 42.63
// Tests for 'tdig_quant' step.
double res10 = db.TDIGEST().Min("racer_ages");
Console.WriteLine(res10); // >>> 19.27
double res11 = db.TDIGEST().Max("racer_ages");
Console.WriteLine(res11); // >>> 85.71
// Tests for 'tdig_min' step.
bool res12 = db.TDIGEST().Reset("racer_ages");
Console.WriteLine(res12); // >>> True
// Tests for 'tdig_reset' step.
}
}
最后,TDIGEST.REVRANK key value...
与 TDIGEST.RANK 相似,但它为每个输入值返回(大于给定值的观测值 + 等于给定值的观测值数量的一半)的估计值。
通过分数或排名估计值
TDIGEST.QUANTILE key fraction...
为每个输入分数返回小于给定分数的观测值的估计值(浮点数)。 TDIGEST.BYRANK key rank...
为每个输入排名返回具有该排名的估计值(浮点数)。
> TDIGEST.QUANTILE racer_ages .5
1) "44.200000000000003"
> TDIGEST.BYRANK racer_ages 4
1) "42.630000000000003"
"""
Code samples for t-digest pages:
https://redis.ac.cn/docs/latest/develop/data-types/probabilistic/t-digest/
"""
import redis
r = redis.Redis(decode_responses=True)
res1 = r.tdigest().create("bikes:sales", 100)
print(res1) # >>> True
res2 = r.tdigest().add("bikes:sales", [21])
print(res2) # >>> OK
res3 = r.tdigest().add("bikes:sales", [150, 95, 75, 34])
print(res3) # >>> OK
res4 = r.tdigest().create("racer_ages")
print(res4) # >>> True
res5 = r.tdigest().add(
"racer_ages",
[
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63,
],
)
print(res5) # >>> OK
res6 = r.tdigest().rank("racer_ages", 50)
print(res6) # >>> [7]
res7 = r.tdigest().rank("racer_ages", 50, 40)
print(res7) # >>> [7, 4]
res8 = r.tdigest().quantile("racer_ages", 0.5)
print(res8) # >>> [44.2]
res9 = r.tdigest().byrank("racer_ages", 4)
print(res9) # >>> [42.63]
res10 = r.tdigest().min("racer_ages")
print(res10) # >>> 19.27
res11 = r.tdigest().max("racer_ages")
print(res11) # >>> 85.71
res12 = r.tdigest().reset("racer_ages")
print(res12) # >>> OK
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
const res1 = await client.tDigest.create('bikes:sales', 100);
console.log(res1); // >>> OK
const res2 = await client.tDigest.add('bikes:sales', [21]);
console.log(res2); // >>> OK
const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);
console.log(res3); // >>> OK
const res4 = await client.tDigest.create('racer_ages');
console.log(res4); // >>> OK
const res5 = await client.tDigest.add('racer_ages', [
45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63
]);
console.log(res5); // >>> OK
const res6 = await client.tDigest.rank('racer_ages', [50]);
console.log(res6); // >>> [7]
const res7 = await client.tDigest.rank('racer_ages', [50, 40]);
console.log(res7); // >>> [7, 4]
const res8 = await client.tDigest.quantile('racer_ages', [0.5]);
console.log(res8); // >>> [44.2]
const res9 = await client.tDigest.byRank('racer_ages', [4]);
console.log(res9); // >>> [42.63]
const res10 = await client.tDigest.min('racer_ages');
console.log(res10); // >>> 19.27
const res11 = await client.tDigest.max('racer_ages');
console.log(res11); // >>> 85.71
const res12 = await client.tDigest.reset('racer_ages');
console.log(res12); // >>> OK
package io.redis.examples;
public class TDigestExample {
public void run(){
UnifiedJedis unifiedJedis = new UnifiedJedis("redis://127.0.0.1:6379");
String res1 = unifiedJedis.tdigestCreate("bikes:sales", 100);
System.out.println(res1); // >>> True
String res2 = unifiedJedis.tdigestAdd("bikes:sales", 21);
System.out.println(res2); // >>> OK
String res3 = unifiedJedis.tdigestAdd("bikes:sales", 150, 95, 75, 34);
System.out.println(res3); // >>> OK
String res4 = unifiedJedis.tdigestCreate("racer_ages");
System.out.println(res4); // >>> True
String res5 = unifiedJedis.tdigestAdd("racer_ages", 45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63);
System.out.println(res5); // >>> OK
List<Long> res6 = unifiedJedis.tdigestRank("racer_ages", 50);
System.out.println(res6); // >>> [7]
List<Long> res7 = unifiedJedis.tdigestRank("racer_ages", 50, 40);
System.out.println(res7); // >>> [7, 4]
List<Double> res8 = unifiedJedis.tdigestQuantile("racer_ages", 0.5);
System.out.println(res8); // >>> [44.2]
List<Double> res9 = unifiedJedis.tdigestByRank("racer_ages", 4);
System.out.println(res9); // >>> [42.63]
double res10 = unifiedJedis.tdigestMin("racer_ages");
System.out.println(res10); // >>> 19.27
double res11 = unifiedJedis.tdigestMax("racer_ages");
System.out.println(res11); // >>> 85.71
String res12 = unifiedJedis.tdigestReset("racer_ages");
System.out.println(res12); // >>> OK
}
}
using NRedisStack.RedisStackCommands;
using NRedisStack.Tests;
using StackExchange.Redis;
public class Tdigest_tutorial
{
[SkipIfRedis(Is.OSSCluster)]
public void run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool res1 = db.TDIGEST().Create("bikes:sales", 100);
Console.WriteLine(res1); // >>> True
bool res2 = db.TDIGEST().Add("bikes:sales", 21);
Console.WriteLine(res2); // >>> True
bool res3 = db.TDIGEST().Add("bikes:sales", 150, 95, 75, 34);
Console.WriteLine(res3); // >>> true
// Tests for 'tdig_start' step.
bool res4 = db.TDIGEST().Create("racer_ages");
Console.WriteLine(res4); // >>> True
bool res5 = db.TDIGEST().Add("racer_ages",
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63
);
Console.WriteLine(res5); // >>> True
long[] res6 = db.TDIGEST().Rank("racer_ages", 50);
Console.WriteLine(string.Join(", ", res6)); // >>> 7
long[] res7 = db.TDIGEST().Rank("racer_ages", 50, 40);
Console.WriteLine(string.Join(", ", res7)); // >>> 7, 4
// Tests for 'tdig_cdf' step.
double[] res8 = db.TDIGEST().Quantile("racer_ages", 0.5); ;
Console.WriteLine(string.Join(", ", res8)); // >>> 44.2
double[] res9 = db.TDIGEST().ByRank("racer_ages", 4);
Console.WriteLine(string.Join(", ", res9)); // >>> 42.63
// Tests for 'tdig_quant' step.
double res10 = db.TDIGEST().Min("racer_ages");
Console.WriteLine(res10); // >>> 19.27
double res11 = db.TDIGEST().Max("racer_ages");
Console.WriteLine(res11); // >>> 85.71
// Tests for 'tdig_min' step.
bool res12 = db.TDIGEST().Reset("racer_ages");
Console.WriteLine(res12); // >>> True
// Tests for 'tdig_reset' step.
}
}
TDIGEST.BYREVRANK key rank...
为每个输入 **反向排名** 返回具有该反向排名的 **值**(浮点数)。
估计修剪后的平均值
使用 TDIGEST.TRIMMED_MEAN key lowFraction highFraction
来检索指定分数之间的平均值的估计值。
这对于计算忽略异常值的平均值特别有用。例如,计算第 20 百分位数和第 80 百分位数之间的平均值。
合并草图
有时合并草图很有用。例如,假设我们测量 3 台服务器的延迟,并且我们希望计算所有服务器的组合的 90%、95% 和 99% 延迟。
TDIGEST.MERGE destKey numKeys sourceKey... [COMPRESSION compression] [OVERRIDE]
将多个草图合并为单个草图。
如果 destKey
不存在,则创建一个新的草图。
如果 destKey
是一个现有的草图,则其值将与源键的值合并。要覆盖目标键的内容,请使用 OVERRIDE
。
使用 TDIGEST.MIN
和 TDIGEST.MAX
分别检索草图中的最小值和最大值。
> TDIGEST.MIN racer_ages
"19.27"
> TDIGEST.MAX racer_ages
"85.709999999999994"
"""
Code samples for t-digest pages:
https://redis.ac.cn/docs/latest/develop/data-types/probabilistic/t-digest/
"""
import redis
r = redis.Redis(decode_responses=True)
res1 = r.tdigest().create("bikes:sales", 100)
print(res1) # >>> True
res2 = r.tdigest().add("bikes:sales", [21])
print(res2) # >>> OK
res3 = r.tdigest().add("bikes:sales", [150, 95, 75, 34])
print(res3) # >>> OK
res4 = r.tdigest().create("racer_ages")
print(res4) # >>> True
res5 = r.tdigest().add(
"racer_ages",
[
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63,
],
)
print(res5) # >>> OK
res6 = r.tdigest().rank("racer_ages", 50)
print(res6) # >>> [7]
res7 = r.tdigest().rank("racer_ages", 50, 40)
print(res7) # >>> [7, 4]
res8 = r.tdigest().quantile("racer_ages", 0.5)
print(res8) # >>> [44.2]
res9 = r.tdigest().byrank("racer_ages", 4)
print(res9) # >>> [42.63]
res10 = r.tdigest().min("racer_ages")
print(res10) # >>> 19.27
res11 = r.tdigest().max("racer_ages")
print(res11) # >>> 85.71
res12 = r.tdigest().reset("racer_ages")
print(res12) # >>> OK
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
const res1 = await client.tDigest.create('bikes:sales', 100);
console.log(res1); // >>> OK
const res2 = await client.tDigest.add('bikes:sales', [21]);
console.log(res2); // >>> OK
const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);
console.log(res3); // >>> OK
const res4 = await client.tDigest.create('racer_ages');
console.log(res4); // >>> OK
const res5 = await client.tDigest.add('racer_ages', [
45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63
]);
console.log(res5); // >>> OK
const res6 = await client.tDigest.rank('racer_ages', [50]);
console.log(res6); // >>> [7]
const res7 = await client.tDigest.rank('racer_ages', [50, 40]);
console.log(res7); // >>> [7, 4]
const res8 = await client.tDigest.quantile('racer_ages', [0.5]);
console.log(res8); // >>> [44.2]
const res9 = await client.tDigest.byRank('racer_ages', [4]);
console.log(res9); // >>> [42.63]
const res10 = await client.tDigest.min('racer_ages');
console.log(res10); // >>> 19.27
const res11 = await client.tDigest.max('racer_ages');
console.log(res11); // >>> 85.71
const res12 = await client.tDigest.reset('racer_ages');
console.log(res12); // >>> OK
package io.redis.examples;
public class TDigestExample {
public void run(){
UnifiedJedis unifiedJedis = new UnifiedJedis("redis://127.0.0.1:6379");
String res1 = unifiedJedis.tdigestCreate("bikes:sales", 100);
System.out.println(res1); // >>> True
String res2 = unifiedJedis.tdigestAdd("bikes:sales", 21);
System.out.println(res2); // >>> OK
String res3 = unifiedJedis.tdigestAdd("bikes:sales", 150, 95, 75, 34);
System.out.println(res3); // >>> OK
String res4 = unifiedJedis.tdigestCreate("racer_ages");
System.out.println(res4); // >>> True
String res5 = unifiedJedis.tdigestAdd("racer_ages", 45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63);
System.out.println(res5); // >>> OK
List<Long> res6 = unifiedJedis.tdigestRank("racer_ages", 50);
System.out.println(res6); // >>> [7]
List<Long> res7 = unifiedJedis.tdigestRank("racer_ages", 50, 40);
System.out.println(res7); // >>> [7, 4]
List<Double> res8 = unifiedJedis.tdigestQuantile("racer_ages", 0.5);
System.out.println(res8); // >>> [44.2]
List<Double> res9 = unifiedJedis.tdigestByRank("racer_ages", 4);
System.out.println(res9); // >>> [42.63]
double res10 = unifiedJedis.tdigestMin("racer_ages");
System.out.println(res10); // >>> 19.27
double res11 = unifiedJedis.tdigestMax("racer_ages");
System.out.println(res11); // >>> 85.71
String res12 = unifiedJedis.tdigestReset("racer_ages");
System.out.println(res12); // >>> OK
}
}
using NRedisStack.RedisStackCommands;
using NRedisStack.Tests;
using StackExchange.Redis;
public class Tdigest_tutorial
{
[SkipIfRedis(Is.OSSCluster)]
public void run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool res1 = db.TDIGEST().Create("bikes:sales", 100);
Console.WriteLine(res1); // >>> True
bool res2 = db.TDIGEST().Add("bikes:sales", 21);
Console.WriteLine(res2); // >>> True
bool res3 = db.TDIGEST().Add("bikes:sales", 150, 95, 75, 34);
Console.WriteLine(res3); // >>> true
// Tests for 'tdig_start' step.
bool res4 = db.TDIGEST().Create("racer_ages");
Console.WriteLine(res4); // >>> True
bool res5 = db.TDIGEST().Add("racer_ages",
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63
);
Console.WriteLine(res5); // >>> True
long[] res6 = db.TDIGEST().Rank("racer_ages", 50);
Console.WriteLine(string.Join(", ", res6)); // >>> 7
long[] res7 = db.TDIGEST().Rank("racer_ages", 50, 40);
Console.WriteLine(string.Join(", ", res7)); // >>> 7, 4
// Tests for 'tdig_cdf' step.
double[] res8 = db.TDIGEST().Quantile("racer_ages", 0.5); ;
Console.WriteLine(string.Join(", ", res8)); // >>> 44.2
double[] res9 = db.TDIGEST().ByRank("racer_ages", 4);
Console.WriteLine(string.Join(", ", res9)); // >>> 42.63
// Tests for 'tdig_quant' step.
double res10 = db.TDIGEST().Min("racer_ages");
Console.WriteLine(res10); // >>> 19.27
double res11 = db.TDIGEST().Max("racer_ages");
Console.WriteLine(res11); // >>> 85.71
// Tests for 'tdig_min' step.
bool res12 = db.TDIGEST().Reset("racer_ages");
Console.WriteLine(res12); // >>> True
// Tests for 'tdig_reset' step.
}
}
草图为空时,两者都返回 nan
。
这两个命令都返回准确的结果,等效于 TDIGEST.BYRANK racer_ages 0
和 TDIGEST.BYREVRANK racer_ages 0
。
使用 TDIGEST.INFO racer_ages
来检索有关草图的一些其他信息。
重置草图
> TDIGEST.RESET racer_ages
OK
"""
Code samples for t-digest pages:
https://redis.ac.cn/docs/latest/develop/data-types/probabilistic/t-digest/
"""
import redis
r = redis.Redis(decode_responses=True)
res1 = r.tdigest().create("bikes:sales", 100)
print(res1) # >>> True
res2 = r.tdigest().add("bikes:sales", [21])
print(res2) # >>> OK
res3 = r.tdigest().add("bikes:sales", [150, 95, 75, 34])
print(res3) # >>> OK
res4 = r.tdigest().create("racer_ages")
print(res4) # >>> True
res5 = r.tdigest().add(
"racer_ages",
[
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63,
],
)
print(res5) # >>> OK
res6 = r.tdigest().rank("racer_ages", 50)
print(res6) # >>> [7]
res7 = r.tdigest().rank("racer_ages", 50, 40)
print(res7) # >>> [7, 4]
res8 = r.tdigest().quantile("racer_ages", 0.5)
print(res8) # >>> [44.2]
res9 = r.tdigest().byrank("racer_ages", 4)
print(res9) # >>> [42.63]
res10 = r.tdigest().min("racer_ages")
print(res10) # >>> 19.27
res11 = r.tdigest().max("racer_ages")
print(res11) # >>> 85.71
res12 = r.tdigest().reset("racer_ages")
print(res12) # >>> OK
import assert from 'assert';
import { createClient } from 'redis';
const client = createClient();
await client.connect();
const res1 = await client.tDigest.create('bikes:sales', 100);
console.log(res1); // >>> OK
const res2 = await client.tDigest.add('bikes:sales', [21]);
console.log(res2); // >>> OK
const res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);
console.log(res3); // >>> OK
const res4 = await client.tDigest.create('racer_ages');
console.log(res4); // >>> OK
const res5 = await client.tDigest.add('racer_ages', [
45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63
]);
console.log(res5); // >>> OK
const res6 = await client.tDigest.rank('racer_ages', [50]);
console.log(res6); // >>> [7]
const res7 = await client.tDigest.rank('racer_ages', [50, 40]);
console.log(res7); // >>> [7, 4]
const res8 = await client.tDigest.quantile('racer_ages', [0.5]);
console.log(res8); // >>> [44.2]
const res9 = await client.tDigest.byRank('racer_ages', [4]);
console.log(res9); // >>> [42.63]
const res10 = await client.tDigest.min('racer_ages');
console.log(res10); // >>> 19.27
const res11 = await client.tDigest.max('racer_ages');
console.log(res11); // >>> 85.71
const res12 = await client.tDigest.reset('racer_ages');
console.log(res12); // >>> OK
package io.redis.examples;
public class TDigestExample {
public void run(){
UnifiedJedis unifiedJedis = new UnifiedJedis("redis://127.0.0.1:6379");
String res1 = unifiedJedis.tdigestCreate("bikes:sales", 100);
System.out.println(res1); // >>> True
String res2 = unifiedJedis.tdigestAdd("bikes:sales", 21);
System.out.println(res2); // >>> OK
String res3 = unifiedJedis.tdigestAdd("bikes:sales", 150, 95, 75, 34);
System.out.println(res3); // >>> OK
String res4 = unifiedJedis.tdigestCreate("racer_ages");
System.out.println(res4); // >>> True
String res5 = unifiedJedis.tdigestAdd("racer_ages", 45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63);
System.out.println(res5); // >>> OK
List<Long> res6 = unifiedJedis.tdigestRank("racer_ages", 50);
System.out.println(res6); // >>> [7]
List<Long> res7 = unifiedJedis.tdigestRank("racer_ages", 50, 40);
System.out.println(res7); // >>> [7, 4]
List<Double> res8 = unifiedJedis.tdigestQuantile("racer_ages", 0.5);
System.out.println(res8); // >>> [44.2]
List<Double> res9 = unifiedJedis.tdigestByRank("racer_ages", 4);
System.out.println(res9); // >>> [42.63]
double res10 = unifiedJedis.tdigestMin("racer_ages");
System.out.println(res10); // >>> 19.27
double res11 = unifiedJedis.tdigestMax("racer_ages");
System.out.println(res11); // >>> 85.71
String res12 = unifiedJedis.tdigestReset("racer_ages");
System.out.println(res12); // >>> OK
}
}
using NRedisStack.RedisStackCommands;
using NRedisStack.Tests;
using StackExchange.Redis;
public class Tdigest_tutorial
{
[SkipIfRedis(Is.OSSCluster)]
public void run()
{
var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();
bool res1 = db.TDIGEST().Create("bikes:sales", 100);
Console.WriteLine(res1); // >>> True
bool res2 = db.TDIGEST().Add("bikes:sales", 21);
Console.WriteLine(res2); // >>> True
bool res3 = db.TDIGEST().Add("bikes:sales", 150, 95, 75, 34);
Console.WriteLine(res3); // >>> true
// Tests for 'tdig_start' step.
bool res4 = db.TDIGEST().Create("racer_ages");
Console.WriteLine(res4); // >>> True
bool res5 = db.TDIGEST().Add("racer_ages",
45.88,
44.2,
58.03,
19.76,
39.84,
69.28,
50.97,
25.41,
19.27,
85.71,
42.63
);
Console.WriteLine(res5); // >>> True
long[] res6 = db.TDIGEST().Rank("racer_ages", 50);
Console.WriteLine(string.Join(", ", res6)); // >>> 7
long[] res7 = db.TDIGEST().Rank("racer_ages", 50, 40);
Console.WriteLine(string.Join(", ", res7)); // >>> 7, 4
// Tests for 'tdig_cdf' step.
double[] res8 = db.TDIGEST().Quantile("racer_ages", 0.5); ;
Console.WriteLine(string.Join(", ", res8)); // >>> 44.2
double[] res9 = db.TDIGEST().ByRank("racer_ages", 4);
Console.WriteLine(string.Join(", ", res9)); // >>> 42.63
// Tests for 'tdig_quant' step.
double res10 = db.TDIGEST().Min("racer_ages");
Console.WriteLine(res10); // >>> 19.27
double res11 = db.TDIGEST().Max("racer_ages");
Console.WriteLine(res11); // >>> 85.71
// Tests for 'tdig_min' step.
bool res12 = db.TDIGEST().Reset("racer_ages");
Console.WriteLine(res12); // >>> True
// Tests for 'tdig_reset' step.
}
}
学术来源
参考文献