Skip to content

Commit 9901c79

Browse files
authored
Add support for TDIGEST.QUANTILE extensions (#2317)
* Add support for TDIGEST.QUANTILE extensions * linters * linters & utils * Update test_bloom.py
1 parent 4ed8aba commit 9901c79

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

89
def intlist(obj):
@@ -355,22 +356,31 @@ async def test_tdigest_min_and_max(modclient: redis.Redis):
355356

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

375385

376386
@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)