{
  "category": "Statistical functions",
  "display_name": "TRIMMEAN",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.TRIMMEAN",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 131 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 131 recorded answers",
    "workbook_corpus": {
      "cells": 131,
      "matched": 131
    }
  },
  "implementations": [
    {
      "commit": "9e1884369a678158dd82d3d74188daf986995e87",
      "flavour": "excel-compatibility-candidate",
      "language": "Rust",
      "name": "CalcFunc",
      "own_code": [
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/statistics.rs#L397-L401",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/moment_stats_family.rs#L130-L166"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/statistics::list"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/statistics.rs#L397-L401",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 131 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.TRIMMEAN.json",
  "name": "TRIMMEAN",
  "notes": [],
  "schema": "efh.function-reference/v1",
  "section_order": [
    "what-it-does",
    "try-it",
    "quirks",
    "how-to-implement",
    "version-differences",
    "alternatives"
  ],
  "sections": {
    "alternatives": "\n### Relationships\n\n\n- **`AVERAGE`** is the `percent = 0` case and the estimator `TRIMMEAN` is a robustification of.\n- **`MEDIAN`** is the limit as the trimming approaches everything, for odd `n`. `TRIMMEAN` with a\n  large `percent` and `MEDIAN` should agree exactly on odd-length data whenever the trim leaves a\n  single value.\n- **`AVERAGEIF` / `AVERAGEIFS`** trim by *predicate* rather than by rank; they are the other way\n  to exclude outliers and they are not robust in the breakdown-point sense, because a predicate\n  chosen from the data is itself corruptible.\n- **`QUARTILE.EXC` / `PERCENTILE.INC` / `SMALL` / `LARGE`** are the other order-statistic\n  surfaces, and the interpolation conventions they disagree about are the same family of\n  convention disagreements that make `TRIMMEAN` non-portable across statistics packages.\n- **`STEYX`, `SKEW`, `SKEW.P`, `KURT`** share this surface's implementing module in the reference\n  engine — a module of moment statistics with one order statistic sitting inside it.\n- **Confused with**: a Winsorized mean (see above), and with \"remove outliers then average\", which\n  is a data-dependent rule with no fixed breakdown point.\n\n\n",
    "how-to-implement": "\n### Numerical notes\n\n\n`TRIMMEAN` is the rare statistical surface whose difficulty is **combinatorial rather than\nfloating-point**. The value is a mean of a subset, so once the subset is chosen the arithmetic is\nordinary; almost everything that can go wrong goes wrong in choosing it.\n\n**Selection, not sorting.** A full sort is `O(n log n)` and is the obvious implementation, but the\nfunction only needs the `k`-th smallest and `k`-th largest cut points; a two-sided quickselect\n(Hoare's algorithm, or `nth_element`) does the job in expected linear time, and introselect\nguarantees it. For the array-lifted case — many `percent` values against one `array` — sorting\nonce and reusing the order is obviously right, and an implementation that re-selects per element\ndoes needless work.\n\n**The count arithmetic is integer arithmetic and should stay that way.** `floor(n · percent / 2)`\ncomputed in floating point can land on the wrong integer when `n · percent` is within rounding of\nan even number: `n = 20`, `percent = 0.2` gives `n·percent = 4.000000000000001` or\n`3.9999999999999996` depending on how the product is formed, and the second rounds down to a\ndifferent trim count. This is a real, reachable discontinuity — one ulp in the product changes how\nmany observations are discarded, and therefore changes the answer by a visible amount rather than\nby a rounding error. Any implementation of this function needs a defensible rule for forming the\ncount, and the Handbook does not know what Excel's is.\n\n**Summation of the retained values.** The ordinary accumulation question applies: naive\nleft-to-right summation of a long retained set accumulates error proportional to `n`; pairwise or\ncompensated (Kahan/Neumaier) summation reduces it. One pleasant side effect of trimming is that\nthe retained values span a narrower range than the original data, so the sum is generally *better*\nconditioned than `AVERAGE`'s on the same input.\n\n**Sorting does not lose accuracy**, but it does fix an order, and summing in sorted order is a\ndifferent floating-point sum from summing in sheet order. So `TRIMMEAN(array, 0)` and\n`AVERAGE(array)` are mathematically identical and need not be identical in the last bit — which\nmakes their comparison a probe rather than a tautology.\n\nThe accuracy and convention behaviour of Excel's statistical procedures is a documented topic in\nthe published literature; Morten Welinder's work on Gnumeric's statistical functions is the most\nconcrete account, and the assessments by Knüsel and by McCullough & Wilson are the standing\nevaluations. The Handbook names them as the right reading and does not assert from them what any\ncurrent build does.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n\nReturns `Number`.\n\n- **`percent = 0`** trims nothing and equals `AVERAGE(array)`. This should be an exact identity\n  between two Excel surfaces, including in the last bits if both sum the same way — and it is\n  therefore a good probe of whether `TRIMMEAN` reuses `AVERAGE`'s accumulation.\n- **`percent` small enough that `n · percent < 2`** also trims nothing, because the floor of a\n  quantity below 2, halved, is zero. So on a ten-point data set every `percent` below `0.2`\n  behaves identically to `percent = 0`. That is a large flat region that surprises people who\n  expect `percent = 0.1` to do something.\n- **Ties at the trimming boundary.** Trimming is by position in the sorted order, not by value,\n  so when several observations tie at the cut point it does not matter which copies are removed —\n  the retained multiset, and therefore the mean, is the same. This is a real robustness of the\n  definition and it is worth knowing that no tie-breaking rule is needed.\n- **`n = 1`** with any admissible `percent` retains the single value.\n- **Sort order and non-finite values.** The definition depends on an ordering of the data. What\n  happens with values that do not order conventionally, and how blanks and text interact with the\n  sort, is undocumented.\n- **`array` empty** is undocumented; the engine's battery reports a value error for an empty\n  range.\n\n\n\n### Errors\n\n\nAs documented on Microsoft's `TRIMMEAN` page:\n\n| Error | Documented condition |\n|---|---|\n| `#NUM!` | `percent < 0` or `percent > 1`. |\n\nThat is the whole documented table. Undocumented, and therefore open: an empty `array`; the\n`percent = 1` even-`n` case that leaves no data; non-numeric contents of `array`; and a\nnonnumeric `percent`.\n\nErrors in either argument propagate under the universal coercion rule. The Handbook has not\nverified any of this against Excel.\n\n\n",
    "sources": "- Microsoft, \"TRIMMEAN function\" —\n  <https://support.microsoft.com/en-us/office/trimmean-function-d90c9878-a119-4746-88fa-63d988f511d3>\n  (syntax; the description of `percent` as the fractional number of data points to exclude with\n  the 20-point / `percent = 0.2` worked example; the rule that the number of excluded data points\n  is rounded down to the nearest multiple of 2; and `#NUM!` for `percent < 0` or `percent > 1`).\n  Retrieved for this page. The page documents nothing about text, logicals, blanks, an empty\n  array, or the `percent = 1` boundary.\n- J. W. Tukey on trimmed means; P. J. Huber, *Robust Statistics*; F. R. Hampel, E. M. Ronchetti,\n  P. J. Rousseeuw and W. A. Stahel, *Robust Statistics: The Approach Based on Influence\n  Functions* — the breakdown-point and influence-function reading of this estimator.\n- C. A. R. Hoare's selection algorithm and the introselect refinement — the linear-time route to\n  the two cut points.\n- N. J. Higham, *Accuracy and Stability of Numerical Algorithms*, chapter 4 (summation) — pairwise\n  and compensated accumulation of the retained values.\n- M. Welinder's documentation of Gnumeric's statistical functions, and the Excel accuracy\n  assessments of R. Knüsel and of B. D. McCullough and B. Wilson — named as the standing\n  literature, not as evidence about any current build.\n- Handbook [Coercion and lifting](../model/02-coercion-and-lifting.md).\n\n\n",
    "version-differences": "No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.",
    "what-it-does": "\n`TRIMMEAN(array, percent)` returns the **trimmed mean** — the arithmetic mean of the data after\ndiscarding an equal number of the largest and smallest values.\n\nThe trimming rule is the part that needs stating precisely, because it is not \"discard\n`percent` of the data\". Microsoft documents it in two sentences: `percent` is \"the fractional\nnumber of data points to exclude from the calculation\", and \"`TRIMMEAN` rounds the number of\nexcluded data points down to the nearest multiple of 2\". So with `n` numeric values:\n\n    total excluded  =  2 · floor( n · percent / 2 )\n    excluded per end,  k  =  floor( n · percent / 2 )\n    result = mean of the n − 2k values remaining after sorting\n\nThe documented example fixes the reading: with `percent = 0.2` and 20 points, four points are\ntrimmed — two from the top and two from the bottom.\n\nThe rounding-down-to-an-even-count rule exists to keep the trim **symmetric**. An odd excluded\ncount would have to remove one more value from one end than the other, which would bias the\nestimator; Excel would rather trim slightly less than trim asymmetrically. This is the right\nchoice, and it means the effective trimming fraction is generally *below* the requested\n`percent` — a fact worth knowing before comparing `TRIMMEAN` results against a statistics\npackage that rounds differently. There is no universal convention here: implementations\nvariously round the per-end count down, round it to nearest, or interpolate between order\nstatistics, and they disagree on the same data by construction rather than by accident.\n\nAs an estimator, `TRIMMEAN` interpolates between two familiar ones:\n\n| `percent` | Result |\n|---|---|\n| 0 | `AVERAGE(array)` — no trimming |\n| small | A robust mean: bounded influence from the tails |\n| large, `n` odd | approaches `MEDIAN(array)` |\n\nIts **breakdown point** is the fraction trimmed from each end, `k/n ≈ percent/2`: that is the\nproportion of arbitrarily corrupted observations the estimator tolerates before its value can be\ndriven anywhere. The mean has breakdown point 0; the median has ½. `TRIMMEAN` lets the caller\nchoose a point on that line, which is the whole reason robust location estimators exist (Tukey;\nHuber, *Robust Statistics*; Hampel, Ronchetti, Rousseeuw & Stahel).\n\nNote also what it is **not**: it is not a Winsorized mean, which replaces the extreme values with\nthe nearest retained value rather than discarding them, and which therefore has a different\nvariance and a different influence function. Excel offers no Winsorized mean.\n\n\n\n### Arguments\n\n\n| Argument | Meaning | Required |\n|---|---|---|\n| `array` | \"The array or range of values to trim and average.\" | Yes |\n| `percent` | \"The fractional number of data points to exclude from the calculation.\" | Yes |\n\n"
  },
  "signature": {
    "display": "TRIMMEAN(array, percent)",
    "parameters": [
      {
        "description": null,
        "name": "array",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "percent",
        "optional": false,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 2,
        "min": 2
      },
      "category": "Statistical functions",
      "classification": {
        "arg_preparation_profile": "ValuesOnlyPreAdapter",
        "coercion_lift_profile": "AggregateDirectAndRangeDualPolicy",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "None",
        "fec_dependency_profile": "None",
        "host_interaction_class": "None",
        "kernel_signature_class": "Custom",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": "Mean of the data after cutting a share of the values from both ends",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Returns the mean of the interior of a data set",
        "microsoft_url": "https://support.microsoft.com/en-us/office/trimmean-function-d90c9878-a119-4746-88fa-63d988f511d3"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "TRIMMEAN"
        },
        {
          "locale": "bg-BG",
          "name": "TRIMMEAN"
        },
        {
          "locale": "cs-CZ",
          "name": "TRIMMEAN"
        },
        {
          "locale": "da-DK",
          "name": "TRIMMIDDELVÆRDI"
        },
        {
          "locale": "de-DE",
          "name": "TRIMMEAN"
        },
        {
          "locale": "el-GR",
          "name": "TRIMMEAN"
        },
        {
          "locale": "en-AU",
          "name": "TRIMMEAN"
        },
        {
          "locale": "en-GB",
          "name": "TRIMMEAN"
        },
        {
          "locale": "en-US",
          "name": "TRIMMEAN"
        },
        {
          "locale": "es-ES",
          "name": "MEDIA.ACOTADA"
        },
        {
          "locale": "et-EE",
          "name": "TRIMMEAN"
        },
        {
          "locale": "fi-FI",
          "name": "KESKIARVO.TASATTU"
        },
        {
          "locale": "fr-FR",
          "name": "MOYENNE.REDUITE*"
        },
        {
          "locale": "he-IL",
          "name": "TRIMMEAN"
        },
        {
          "locale": "hr-HR",
          "name": "TRIMMEAN"
        },
        {
          "locale": "hu-HU",
          "name": "RÉSZÁTLAG"
        },
        {
          "locale": "id-ID",
          "name": "TRIMMEAN"
        },
        {
          "locale": "it-IT",
          "name": "TRIMMEAN"
        },
        {
          "locale": "ja-JP",
          "name": "TRIMMEAN"
        },
        {
          "locale": "ko-KR",
          "name": "TRIMMEAN"
        },
        {
          "locale": "lt-LT",
          "name": "TRIMMEAN"
        },
        {
          "locale": "lv-LV",
          "name": "TRIMMEAN"
        },
        {
          "locale": "nb-NO",
          "name": "TRIMMET.GJENNOMSNITT"
        },
        {
          "locale": "nl-NL",
          "name": "GETRIMD.GEM"
        },
        {
          "locale": "pl-PL",
          "name": "ŚREDNIA.WEWN"
        },
        {
          "locale": "pt-BR",
          "name": "TRIMMEAN"
        },
        {
          "locale": "pt-PT",
          "name": "MÉDIA.INTERNA"
        },
        {
          "locale": "ro-RO",
          "name": "TRIMMEAN"
        },
        {
          "locale": "ru-RU",
          "name": "УРЕЗСРЕДНЕЕ"
        },
        {
          "locale": "sk-SK",
          "name": "TRIMMEAN"
        },
        {
          "locale": "sl-SI",
          "name": "TRIMMEAN"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "TRIMMEAN"
        },
        {
          "locale": "sv-SE",
          "name": "TRIMMEDEL"
        },
        {
          "locale": "th-TH",
          "name": "TRIMMEAN"
        },
        {
          "locale": "tr-TR",
          "name": "KIRPORTALAMA işlevi"
        },
        {
          "locale": "uk-UA",
          "name": "TRIMMEAN"
        },
        {
          "locale": "vi-VN",
          "name": "TRIMMEAN"
        },
        {
          "locale": "zh-CN",
          "name": "TRIMMEAN"
        },
        {
          "locale": "zh-HK",
          "name": "TRIMMEAN"
        },
        {
          "locale": "zh-TW",
          "name": "TRIMMEAN"
        }
      ],
      "signature": {
        "display": "TRIMMEAN(array, percent)",
        "parameters": [
          {
            "description": null,
            "name": "array",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "percent",
            "optional": false,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "TRIMMEAN",
      "version_marker": null,
      "xlcall": {
        "code": "331",
        "symbol": "xlfTrimmean"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/trimmean-function-d90c9878-a119-4746-88fa-63d988f511d3"
  },
  "try_it": {
    "formula": "=TRIMMEAN({1;2;3;4;5;6;7;8;9;10},0.2)"
  },
  "url": "https://handbook.dnacalc.com/functions/TRIMMEAN.html",
  "variants": [],
  "versions": []
}