Skip to content

Commit f9ea68f

Browse files
committed
Add support for TDIGEST.QUANTILE extensions (#2317)
* Add support for TDIGEST.QUANTILE extensions * linters * linters & utils * Update test_bloom.py
1 parent 272e463 commit f9ea68f

File tree

5 files changed

+44
-19
lines changed

5 files changed

+44
-19
lines changed

redis/commands/bf/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ..helpers import parse_to_list
44
from .commands import * # noqa
55
from .info import BFInfo, CFInfo, CMSInfo, TDigestInfo, TopKInfo
6+
from .utils import parse_tdigest_quantile
67

78

89
class AbstractBloom(object):
@@ -166,7 +167,7 @@ def __init__(self, client, **kwargs):
166167
# TDIGEST_ADD: spaceHolder,
167168
# TDIGEST_MERGE: spaceHolder,
168169
TDIGEST_CDF: float,
169-
TDIGEST_QUANTILE: float,
170+
TDIGEST_QUANTILE: parse_tdigest_quantile,
170171
TDIGEST_MIN: float,
171172
TDIGEST_MAX: float,
172173
TDIGEST_INFO: TDigestInfo,

redis/commands/bf/commands.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,13 +393,14 @@ def max(self, key):
393393
""" # noqa
394394
return self.execute_command(TDIGEST_MAX, key)
395395

396-
def quantile(self, key, quantile):
396+
def quantile(self, key, quantile, *quantiles):
397397
"""
398-
Return double value estimate of the cutoff such that a specified fraction of the data
399-
added to this TDigest would be less than or equal to the cutoff.
398+
Returns estimates of one or more cutoffs such that a specified fraction of the
399+
observations added to this t-digest would be less than or equal to each of the
400+
specified cutoffs. (Multiple quantiles can be returned with one call)
400401
For more information see `TDIGEST.QUANTILE <https://redis.io/commands/tdigest.quantile>`_.
401402
""" # noqa
402-
return self.execute_command(TDIGEST_QUANTILE, key, quantile)
403+
return self.execute_command(TDIGEST_QUANTILE, key, quantile, *quantiles)
403404

404405
def cdf(self, key, value):
405406
"""

redis/commands/bf/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def parse_tdigest_quantile(response):
2+
"""Parse TDIGEST.QUANTILE response."""
3+
return [float(x) for x in response]

tests/test_asyncio/test_bloom.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import redis.asyncio as redis
44
from redis.exceptions import ModuleError, RedisError
55
from redis.utils import HIREDIS_AVAILABLE
6+
from tests.conftest import skip_ifmodversion_lt
67

78
pytestmark = pytest.mark.asyncio
89

@@ -357,22 +358,31 @@ async def test_tdigest_min_and_max(modclient: redis.Redis):
357358

358359
@pytest.mark.redismod
359360
@pytest.mark.experimental
361+
@skip_ifmodversion_lt("2.4.0", "bf")
360362
async def test_tdigest_quantile(modclient: redis.Redis):
361363
assert await modclient.tdigest().create("tDigest", 500)
362364
# insert data-points into sketch
363365
assert await modclient.tdigest().add(
364366
"tDigest", list([x * 0.01 for x in range(1, 10000)]), [1.0] * 10000
365367
)
366368
# assert min min/max have same result as quantile 0 and 1
367-
assert await modclient.tdigest().max(
368-
"tDigest"
369-
) == await modclient.tdigest().quantile("tDigest", 1.0)
370-
assert await modclient.tdigest().min(
371-
"tDigest"
372-
) == await modclient.tdigest().quantile("tDigest", 0.0)
373-
374-
assert 1.0 == round(await modclient.tdigest().quantile("tDigest", 0.01), 2)
375-
assert 99.0 == round(await modclient.tdigest().quantile("tDigest", 0.99), 2)
369+
assert (
370+
await modclient.tdigest().max("tDigest")
371+
== (await modclient.tdigest().quantile("tDigest", 1.0))[1]
372+
)
373+
assert (
374+
await modclient.tdigest().min("tDigest")
375+
== (await modclient.tdigest().quantile("tDigest", 0.0))[1]
376+
)
377+
378+
assert 1.0 == round((await modclient.tdigest().quantile("tDigest", 0.01))[1], 2)
379+
assert 99.0 == round((await modclient.tdigest().quantile("tDigest", 0.99))[1], 2)
380+
381+
# test multiple quantiles
382+
assert await modclient.tdigest().create("t-digest", 100)
383+
assert await modclient.tdigest().add("t-digest", [1, 2, 3, 4, 5], [1.0] * 5)
384+
res = await modclient.tdigest().quantile("t-digest", 0.5, 0.8)
385+
assert [0.5, 3.0, 0.8, 5.0] == res
376386

377387

378388
@pytest.mark.redismod

tests/test_bloom.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from redis.exceptions import ModuleError, RedisError
55
from redis.utils import HIREDIS_AVAILABLE
66

7+
from .conftest import skip_ifmodversion_lt
8+
79

810
def intlist(obj):
911
return [int(v) for v in obj]
@@ -354,18 +356,26 @@ def test_tdigest_min_and_max(client):
354356

355357
@pytest.mark.redismod
356358
@pytest.mark.experimental
359+
@skip_ifmodversion_lt("2.4.0", "bf")
357360
def test_tdigest_quantile(client):
358361
assert client.tdigest().create("tDigest", 500)
359362
# insert data-points into sketch
360363
assert client.tdigest().add(
361364
"tDigest", list([x * 0.01 for x in range(1, 10000)]), [1.0] * 10000
362365
)
363366
# assert min min/max have same result as quantile 0 and 1
364-
assert client.tdigest().max("tDigest") == client.tdigest().quantile("tDigest", 1.0)
365-
assert client.tdigest().min("tDigest") == client.tdigest().quantile("tDigest", 0.0)
366-
367-
assert 1.0 == round(client.tdigest().quantile("tDigest", 0.01), 2)
368-
assert 99.0 == round(client.tdigest().quantile("tDigest", 0.99), 2)
367+
res = client.tdigest().quantile("tDigest", 1.0)
368+
assert client.tdigest().max("tDigest") == res[1]
369+
res = client.tdigest().quantile("tDigest", 0.0)
370+
assert client.tdigest().min("tDigest") == res[1]
371+
372+
assert 1.0 == round(client.tdigest().quantile("tDigest", 0.01)[1], 2)
373+
assert 99.0 == round(client.tdigest().quantile("tDigest", 0.99)[1], 2)
374+
375+
# test multiple quantiles
376+
assert client.tdigest().create("t-digest", 100)
377+
assert client.tdigest().add("t-digest", [1, 2, 3, 4, 5], [1.0] * 5)
378+
assert [0.5, 3.0, 0.8, 5.0] == client.tdigest().quantile("t-digest", 0.5, 0.8)
369379

370380

371381
@pytest.mark.redismod

0 commit comments

Comments
 (0)