{
  "schema_version": 2,
  "id": "develop/clients/nodejs/prob",
  "title": "Probabilistic data types",
  "url": "https://redis.io/docs/latest/develop/clients/nodejs/prob/",
  "summary": "Learn how to use approximate calculations with Redis.",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-08T15:03:39+01:00",
  "page_type": "content",
  "content_hash": "9e7c9e7b17fe376200b6aca2b0adeb97dd42b726b4b48442c87de8540f3a26fc",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Redis supports several\n[probabilistic data types](https://redis.io/docs/latest/develop/data-types/probabilistic)\nthat let you calculate values approximately rather than exactly.\nThe types fall into two basic categories:\n\n-   [Set operations](#set-operations): These types let you calculate (approximately)\n    the number of items in a set of distinct values, and whether or not a given value is\n    a member of a set.\n-   [Statistics](#statistics): These types give you an approximation of\n    statistics such as the quantiles, ranks, and frequencies of numeric data points in\n    a list.\n\nTo see why these approximate calculations would be useful, consider the task of\ncounting the number of distinct IP addresses that access a website in one day.\n\nAssuming that you already have code that supplies you with each IP\naddress as a string, you could record the addresses in Redis using\na [set](https://redis.io/docs/latest/develop/data-types/sets):\n\n[code example]\n\nThe set can only contain each key once, so if the same address\nappears again during the day, the new instance will not change\nthe set. At the end of the day, you could get the exact number of\ndistinct addresses using the `sCard()` function:\n\n[code example]\n\nThis approach is simple, effective, and precise but if your website\nis very busy, the `ip_tracker` set could become very large and consume\na lot of memory.\n\nYou would probably round the count of distinct IP addresses to the\nnearest thousand or more to deliver the usage statistics, so\ngetting it exactly right is not important. It would be useful\nif you could trade off some accuracy in exchange for lower memory\nconsumption. The probabilistic data types provide exactly this kind of\ntrade-off. Specifically, you can count the approximate number of items in a\nset using the [HyperLogLog](#set-cardinality) data type, as described below.\n\nIn general, the probabilistic data types let you perform approximations with a\nbounded degree of error that have much lower memory consumption or execution\ntime than the equivalent precise calculations."
    },
    {
      "id": "set-operations",
      "title": "Set operations",
      "role": "content",
      "text": "Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."
    },
    {
      "id": "set-membership",
      "title": "Set membership",
      "role": "content",
      "text": "[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\nNote that you must use the `bf` property to access the Bloom filter commands.\n\n[code example]\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow. Note that you must use the `cf` property to access the Cuckoo filter\ncommands.\n\n[code example]\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."
    },
    {
      "id": "set-cardinality",
      "title": "Set cardinality",
      "role": "content",
      "text": "A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\n[code example]\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."
    },
    {
      "id": "statistics",
      "title": "Statistics",
      "role": "content",
      "text": "Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."
    },
    {
      "id": "frequency",
      "title": "Frequency",
      "role": "content",
      "text": "A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\nNote that you must use the `cms` property to access the Count-min\nsketch commands.\n\n[code example]\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."
    },
    {
      "id": "quantiles",
      "title": "Quantiles",
      "role": "content",
      "text": "A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the \n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set. Note that you must use the `tDigest` property to access the\nt-digest commands.\n\n[code example]\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."
    },
    {
      "id": "ranking",
      "title": "Ranking",
      "role": "content",
      "text": "A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topK.reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist. Note that you must use the `topK` property to access the\nTop-K commands.\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "overview-ex0",
      "language": "js",
      "code": "await client.sAdd(\"ip_tracker\", new_ip_address);",
      "section_id": "overview"
    },
    {
      "id": "overview-ex1",
      "language": "js",
      "code": "const num_distinct_ips = await client.sCard(\"ip_tracker\");",
      "section_id": "overview"
    },
    {
      "id": "set-membership-ex0",
      "language": "js",
      "code": "const res1 = await client.bf.mAdd(\n    \"recorded_users\",\n    [\"andy\", \"cameron\", \"david\", \"michelle\"]\n);\nconsole.log(res1);  // >>> [true, true, true, true]\n\nconst res2 = await client.bf.exists(\"recorded_users\", \"cameron\");\nconsole.log(res2);  // >>> true\n\nconst res3 = await client.bf.exists(\"recorded_users\", \"kaitlyn\");\nconsole.log(res3);  // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-membership-ex1",
      "language": "js",
      "code": "const res4 = await client.cf.add(\"other_users\", \"paolo\");\nconsole.log(res4);  // >>> true\n\nconst res5 = await client.cf.add(\"other_users\", \"kaitlyn\");\nconsole.log(res5);  // >>> true\n\nconst res6 = await client.cf.add(\"other_users\", \"rachel\");\nconsole.log(res6);  // >>> true\n\nconst res7 = await client.cf.exists(\"other_users\", \"paolo\");\nconst res7a = await client.cf.exists(\"other_users\", \"kaitlyn\");\nconst res7b = await client.cf.exists(\"other_users\", \"rachel\");\nconst res7c = await client.cf.exists(\"other_users\", \"andy\");\nconsole.log([res7, res7a, res7b, res7c]);  // >>> [true, true, true, false]\n\nconst res8 = await client.cf.del(\"other_users\", \"paolo\");\nconsole.log(res8);  // >>> true\n\nconst res9 = await client.cf.exists(\"other_users\", \"paolo\");\nconsole.log(res9);  // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-cardinality-ex0",
      "language": "js",
      "code": "const res10 = await client.pfAdd(\"group:1\", [\"andy\", \"cameron\", \"david\"]);\nconsole.log(res10);  // >>> true\n\nconst res11 = await client.pfCount(\"group:1\");\nconsole.log(res11);  // >>> 3\n\nconst res12 = await client.pfAdd(\"group:2\", [\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"]);\nconsole.log(res12);  // >>> true\n\nconst res13 = await client.pfCount(\"group:2\");\nconsole.log(res13);  // >>> 4\n\nconst res14 = await client.pfMerge(\"both_groups\", [\"group:1\", \"group:2\"]);\nconsole.log(res14);  // >>> OK\n\nconst res15 = await client.pfCount(\"both_groups\");\nconsole.log(res15);  // >>> 7",
      "section_id": "set-cardinality"
    },
    {
      "id": "frequency-ex0",
      "language": "js",
      "code": "// Specify that you want to keep the counts within 0.01\n// (1%) of the true value with a 0.005 (0.5%) chance\n// of going outside this limit.\nconst res16 = await client.cms.initByProb(\"items_sold\", 0.01, 0.005);\nconsole.log(res16);  // >>> OK\n\n// The parameters for `incrBy()` are passed as an array of objects\n// each containing an `item` and `incrementBy` property.\nconst res17 = await client.cms.incrBy(\n    \"items_sold\",\n    [\n        { item: \"bread\", incrementBy: 300},\n        { item: \"tea\", incrementBy: 200},\n        { item: \"coffee\", incrementBy: 200},\n        { item: \"beer\", incrementBy: 100}\n    ]\n);\nconsole.log(res17);  // >>> [300, 200, 200, 100]\n\nconst res18 = await client.cms.incrBy(\n    \"items_sold\",    \n    [\n        { item: \"bread\", incrementBy: 100},\n        { item: \"coffee\", incrementBy: 150}\n    ]\n);\nconsole.log(res18);  // >>> [400, 350]\n\nconst res19 = await client.cms.query(\n    \"items_sold\",\n    [\"bread\", \"tea\", \"coffee\", \"beer\"]\n);\nconsole.log(res19);  // >>> [400, 200, 350, 100]",
      "section_id": "frequency"
    },
    {
      "id": "quantiles-ex0",
      "language": "js",
      "code": "const res20 = await client.tDigest.create(\"male_heights\");\nconsole.log(res20);  // >>> OK\n\nconst res21 = await client.tDigest.add(\n    \"male_heights\",\n    [175.5, 181, 160.8, 152, 177, 196, 164]\n);\nconsole.log(res21);  // >>> OK\n\nconst res22 = await client.tDigest.min(\"male_heights\");\nconsole.log(res22);  // >>> 152\n\nconst res23 = await client.tDigest.max(\"male_heights\");\nconsole.log(res23);  // >>> 196\n\nconst res24 = await client.tDigest.quantile(\"male_heights\", [0.75]);\nconsole.log(res24);  // >>> [181]\n\n// Note that the CDF value for 181 is not exactly\n// 0.75. Both values are estimates.\nconst res25 = await client.tDigest.cdf(\"male_heights\", [181]);\nconsole.log(res25);  // >>> [0.7857142857142857]\n\nconst res26 = await client.tDigest.create(\"female_heights\");\nconsole.log(res26);  // >>> OK\n\nconst res27 = await client.tDigest.add(\n    \"female_heights\",\n    [155.5, 161, 168.5, 170, 157.5, 163, 171]\n);\nconsole.log(res27);  // >>> OK\n\nconst res28 = await client.tDigest.quantile(\"female_heights\", [0.75]);\nconsole.log(res28);  // >>> [170]\n\nconst res29 = await client.tDigest.merge(\n    \"all_heights\", [\"male_heights\", \"female_heights\"]\n);\nconsole.log(res29);  // >>> OK\n\nconst res30 = await client.tDigest.quantile(\"all_heights\", [0.75]);\nconsole.log(res30);  // >>> [175.5]",
      "section_id": "quantiles"
    },
    {
      "id": "ranking-ex0",
      "language": "js",
      "code": "// The `reserve()` method creates the Top-K object with\n// the given key. The parameters are the number of items\n// in the ranking and values for `width`, `depth`, and\n// `decay`, described in the Top-K reference page.\nconst res31 = await client.topK.reserve(\n    \"top_3_songs\", 3,\n    { width: 7, depth: 8, decay: 0.9 }\n);\nconsole.log(res31);  // >>> OK\n\n// The parameters for `incrBy()` are passed as an array of objects\n// each containing an `item` and `incrementBy` property.\nconst res32 = await client.topK.incrBy(\n    \"top_3_songs\",\n    [\n        { item: \"Starfish Trooper\", incrementBy: 3000},\n        { item: \"Only one more time\", incrementBy: 1850},\n        { item: \"Rock me, Handel\", incrementBy: 1325},\n        { item: \"How will anyone know?\", incrementBy: 3890},\n        { item: \"Average lover\", incrementBy: 4098},\n        { item: \"Road to everywhere\", incrementBy: 770}\n    ]\n);\nconsole.log(res32);\n// >>> [null, null, null, 'Rock me, Handel', 'Only one more time', null]\n\nconst res33 = await client.topK.list(\"top_3_songs\");\nconsole.log(res33);\n// >>> ['Average lover', 'How will anyone know?', 'Starfish Trooper']\n\nconst res34 = await client.topK.query(\n    \"top_3_songs\", [\"Starfish Trooper\", \"Road to everywhere\"]\n);\nconsole.log(res34);  // >>> [true, false]",
      "section_id": "ranking"
    }
  ]
}
