{
  "schema_version": 2,
  "id": "develop/clients/dotnet/nredisstack/prob",
  "title": "Probabilistic data types",
  "url": "https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/prob/",
  "summary": "Learn how to use approximate calculations with Redis.",
  "aliases": [
    "/develop/clients/dotnet/prob"
  ],
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-08T15:21:05+01:00",
  "page_type": "content",
  "content_hash": "e8b932ca6495eaedc12358557debc4f9c477728fad2a8e3c99b7c88517b3889d",
  "sections": [
    {
      "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()` method to access the Bloom filter commands.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\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()` method to access the Cuckoo filter\ncommands.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\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\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\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()` method to access the Count-min\nsketch commands.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\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()` method to access the\nt-digest commands.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\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()` method to access the\nTop-K commands.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "set-membership-ex0",
      "language": "csharp",
      "code": "bool[] res1 = db.BF().MAdd(\n            \"recorded_users\", \"andy\", \"cameron\", \"david\", \"michelle\"\n        );\n        Console.WriteLine(string.Join(\", \", res1));\n        // >>> true, true, true, true\n\n        bool res2 = db.BF().Exists(\"recorded_users\", \"cameron\");\n        Console.WriteLine(res2); // >>> true\n\n        bool res3 = db.BF().Exists(\"recorded_users\", \"kaitlyn\");\n        Console.WriteLine(res3); // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-membership-ex1",
      "language": "csharp",
      "code": "bool res4 = db.CF().Add(\"other_users\", \"paolo\");\n        Console.WriteLine(res4); // >>> true\n\n        bool res5 = db.CF().Add(\"other_users\", \"kaitlyn\");\n        Console.WriteLine(res5); // >>> true\n\n        bool res6 = db.CF().Add(\"other_users\", \"rachel\");\n        Console.WriteLine(res6); // >>> true\n\n        bool[] res7 = db.CF().MExists(\"other_users\", \"paolo\", \"rachel\", \"andy\");\n        Console.WriteLine(string.Join(\", \", res7));\n        // >>> true, true, false\n\n        bool res8 = db.CF().Del(\"other_users\", \"paolo\");\n        Console.WriteLine(res8); // >>> true\n\n        bool res9 = db.CF().Exists(\"other_users\", \"paolo\");\n        Console.WriteLine(res9); // >>> false",
      "section_id": "set-membership"
    },
    {
      "id": "set-cardinality-ex0",
      "language": "csharp",
      "code": "bool res10 = db.HyperLogLogAdd(\n            \"group:1\",\n            [\"andy\", \"cameron\", \"david\"]\n        );\n        Console.WriteLine(res10); // >>> true\n\n        long res11 = db.HyperLogLogLength(\"group:1\");\n        Console.WriteLine(res11); // >>> 3\n\n        bool res12 = db.HyperLogLogAdd(\n            \"group:2\",\n            [\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"]\n        );\n        Console.WriteLine(res12); // >>> true\n\n        long res13 = db.HyperLogLogLength(\"group:2\");\n        Console.WriteLine(res13); // >>> 4\n\n        db.HyperLogLogMerge(\n            \"both_groups\",\n            \"group:1\", \"group:2\"\n        );\n\n        long res14 = db.HyperLogLogLength(\"both_groups\");\n        Console.WriteLine(res14); // >>> 7",
      "section_id": "set-cardinality"
    },
    {
      "id": "frequency-ex0",
      "language": "csharp",
      "code": "// Specify that you want to keep the counts within 0.01\n        // (0.1%) of the true value with a 0.005 (0.05%) chance\n        // of going outside this limit.\n        bool res15 = db.CMS().InitByProb(\"items_sold\", 0.01, 0.005);\n        Console.WriteLine(res15); // >>> true\n\n        long[] res16 = db.CMS().IncrBy(\n            \"items_sold\",\n            [\n                new(\"bread\", 300),\n                new(\"tea\", 200),\n                new(\"coffee\", 200),\n                new(\"beer\", 100)\n            ]\n        );\n        Console.WriteLine(string.Join(\", \", res16));\n        // >>> 300, 200, 200, 100\n\n        long[] res17 = db.CMS().IncrBy(\n            \"items_sold\",\n            [\n                new(\"bread\", 100),\n                new(\"coffee\", 150)\n            ]\n        );\n        Console.WriteLine(string.Join(\", \", res17));\n        // >>> 400, 350\n\n        long[] res18 = db.CMS().Query(\n            \"items_sold\",\n            \"bread\", \"tea\", \"coffee\", \"beer\"\n        );\n        Console.WriteLine(string.Join(\", \", res18));\n        // >>> 400, 200, 350, 100",
      "section_id": "frequency"
    },
    {
      "id": "quantiles-ex0",
      "language": "csharp",
      "code": "bool res19 = db.TDIGEST().Create(\"male_heights\");\n        Console.WriteLine(res19); // >>> true\n\n        bool res20 = db.TDIGEST().Add(\n            \"male_heights\",\n            175.5, 181, 160.8, 152, 177, 196, 164\n        );\n        Console.WriteLine(res20); // >>> true\n\n        double res21 = db.TDIGEST().Min(\"male_heights\");\n        Console.WriteLine(res21); // >>> 152.0\n\n        double res22 = db.TDIGEST().Max(\"male_heights\");\n        Console.WriteLine(res22); // >>> 196.0\n\n        double[] res23 = db.TDIGEST().Quantile(\"male_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res23)); // >>> 181.0\n\n        // Note that the CDF value for 181.0 is not exactly\n        // 0.75. Both values are estimates.\n        double[] res24 = db.TDIGEST().CDF(\"male_heights\", 181.0);\n        Console.WriteLine(string.Join(\", \", res24)); // >>> 0.7857142857142857\n\n        bool res25 = db.TDIGEST().Create(\"female_heights\");\n        Console.WriteLine(res25); // >>> true\n\n        bool res26 = db.TDIGEST().Add(\n            \"female_heights\",\n            155.5, 161, 168.5, 170, 157.5, 163, 171\n        );\n        Console.WriteLine(res26); // >>> true\n\n        double[] res27 = db.TDIGEST().Quantile(\"female_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res27)); // >>> 170.0\n\n        // Specify 0 for `compression` and false for `override`.\n        bool res28 = db.TDIGEST().Merge(\n            \"all_heights\", 0, false, \"male_heights\", \"female_heights\"\n        );\n        Console.WriteLine(res28); // >>> true\n\n        double[] res29 = db.TDIGEST().Quantile(\"all_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res29)); // >>> 175.5",
      "section_id": "quantiles"
    },
    {
      "id": "ranking-ex0",
      "language": "csharp",
      "code": "bool res30 = db.TOPK().Reserve(\"top_3_songs\", 3, 7, 8, 0.9);\n        Console.WriteLine(res30); // >>> true\n\n        RedisResult[] res31 = db.TOPK().IncrBy(\n            \"top_3_songs\",\n            new Tuple<RedisValue, long>[] {\n                new(\"Starfish Trooper\", 3000),\n                new(\"Only one more time\", 1850),\n                new(\"Rock me, Handel\", 1325),\n                new(\"How will anyone know?\", 3890),\n                new(\"Average lover\", 4098),\n                new(\"Road to everywhere\", 770)\n            }\n        );\n        Console.WriteLine(\n            string.Join(\n                \", \",\n                string.Join(\n                    \", \",\n                    res31.Select(\n                        r => $\"{(r.IsNull ? \"Null\" : r)}\"\n                    )\n                )\n            )\n        );\n        // >>> Null, Null, Null, Rock me, Handel, Only one more time, Null\n\n        RedisResult[] res32 = db.TOPK().List(\"top_3_songs\");\n        Console.WriteLine(\n            string.Join(\n                \", \",\n                string.Join(\n                    \", \",\n                    res32.Select(\n                        r => $\"{(r.IsNull ? \"Null\" : r)}\"\n                    )\n                )\n            )\n        );\n        // >>> Average lover, How will anyone know?, Starfish Trooper\n\n        bool[] res33 = db.TOPK().Query(\n            \"top_3_songs\",\n            \"Starfish Trooper\", \"Road to everywhere\"\n        );\n        Console.WriteLine(string.Join(\", \", res33));\n        // >>> true, false",
      "section_id": "ranking"
    }
  ]
}
