> ## Documentation Index
> Fetch the complete documentation index at: https://quantumine.net/learn/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> This library is published in two languages with the same paths: English at https://quantumine.net/learn/<path> and Vietnamese at https://quantumine.net/learn/vi/<path>. Add .md to either address for the Markdown version.
> llms.txt and llms-full.txt list the English pages only. When the reader writes in Vietnamese, read and cite the Vietnamese page.
> To search the library, connect to the MCP server at https://quantumine.net/learn/mcp. Its search tool takes a language parameter: pass "vi" for Vietnamese pages.
> The library is licensed under CC BY 4.0: it may be quoted, reused and used to train models. Credit Quantumine and link to the page you used.

# Descriptive statistics

> Descriptive statistics summarize data in a few numbers: the mean and median say where the data sits, the standard deviation says how widely it spreads, and quantiles locate a value. Extreme values pull the standard deviation, while the quartiles barely move.

Descriptive statistics are numbers that summarize and describe the data you have, without inferring anything beyond it ([Lane](https://onlinestatbook.com/2/introduction/descriptive.html)). Four groups of numbers are in common use: the mean, the median, the standard deviation with the variance, and quantiles.

## What do the numbers of descriptive statistics tell you, and what are they for?

Take the hourly returns of BTCUSDT (bitcoin traded against USDT on Binance) from January 1, 2025 to August 14, 2026 as an example. An hourly return is the percentage change of the closing price from the previous hour, computed on [1-hour candles](https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints) in UTC, which gives 14,184 values. The histogram below sorts the values into intervals 0.05% wide and counts the hours in each. It marks the mean, the median, the middle half of the data (Q1 to Q3) and the range of the mean ± 1 standard deviation.

> [tab] Chart

> [tab] Python code
>
> ```python theme={null}
> import numpy as np
> import pandas as pd
> import requests
>
>
> def utc_ms(time):
>     return int(pd.Timestamp(time, tz="UTC").timestamp() * 1000)
>
>
> def hourly_closes(symbol, start, end):
>     """Closing prices of the 1-hour candles opened from start to end (UTC), at most 1,000 candles per request."""
>     closes, t = [], utc_ms(start)
>     while t <= utc_ms(end):
>         klines = requests.get(
>             "https://data-api.binance.vision/api/v3/klines",
>             params={"symbol": symbol, "interval": "1h", "startTime": t, "endTime": utc_ms(end), "limit": 1000},
>         ).json()
>         closes += [float(k[4]) for k in klines]
>         t = klines[-1][0] + 3_600_000   # open of the next candle
>     return pd.Series(closes)
>
>
> close = hourly_closes("BTCUSDT", "2024-12-31 23:00", "2026-08-14 23:00")   # 14,185 closing prices
> ret = close.pct_change().dropna() * 100                                     # hourly returns (%): 14,184 values
>
> x = np.sort(ret.to_numpy())                                  # x(1) <= x(2) <= ... <= x(N)
> N = len(x)
> mean = x.sum() / N
> median = x[N // 2] if N % 2 else (x[N // 2 - 1] + x[N // 2]) / 2
> var = ((x - mean) ** 2).sum() / (N - 1)
> std = var ** 0.5
>
>
> def quantile(p):
>     h = p * (N - 1)                                          # position in x, counting from 0
>     j = int(h)
>     return x[j] + (h - j) * (x[j + 1] - x[j])                # interpolate between two adjacent values
>
>
> q1, q3 = quantile(0.25), quantile(0.75)
> print(N, round(mean, 4), round(median, 4))                   # count, mean, median: 14184 -0.0017 0.0008
> print(round(var, 4), round(std, 4))                          # variance and standard deviation: 0.2233 0.4725
> print(round(q1, 4), round(q3, 4), round(q3 - q1, 4))         # Q1, Q3 and IQR: -0.1983 0.2022 0.4004
> print(round(ret.mean(), 4), round(ret.median(), 4), round(ret.var(), 4), round(ret.std(), 4), ret.quantile([0.25, 0.75]).round(4).tolist())   # pandas gives the same numbers: -0.0017 0.0008 0.2233 0.4725 [-0.1983, 0.2022]
> print(round(ret.std(ddof=0), 4))                             # standard deviation dividing by N instead of N - 1: 0.4725
> print(round(ret.min(), 2), round(ret.max(), 2))              # smallest and largest: -4.9 5.03
>
> extreme = ret.abs() > 2                                      # hours in which the price moved by more than 2%
> kept = ret[~extreme]
> print(int(extreme.sum()))                                    # number of hours with a move above 2%: 96
> print(round(((ret[extreme] - ret.mean()) ** 2).sum() / ((ret - ret.mean()) ** 2).sum(), 2))   # their share of the sum of squared deviations: 0.22
> print(round(kept.mean(), 4), round(kept.median(), 4), round(kept.std(), 4), round(kept.quantile(0.75) - kept.quantile(0.25), 4))   # without them: mean, median, standard deviation, IQR: -0.0004 0.001 0.4183 0.3974
>
> counts, _ = np.histogram(ret, bins=np.linspace(-2, 2, 81))   # hours in 80 intervals 0.05% wide from -2% to 2%
> print(counts.tolist(), int((ret < -2).sum()), int((ret > 2).sum()))   # hours in each interval, then hours below -2% and above 2%: [5, 4, 5, ..., 3] 51 45
> ```

Let $x_1, x_2, \dots, x_N$ be the $N$ values, here $N = 14\,184$ returns, $\sum$ the sum over all $N$ values, and $x_{(1)} \le x_{(2)} \le \dots \le x_{(N)}$ the same values sorted from smallest to largest. For quantiles, $p$ is a proportion between 0 and 1, and $j$ and $g$ are the integer part and the fractional part of $p(N - 1) + 1$, so $Q_p$ interpolates between two adjacent values. The table gives the formula and meaning of each number.

| Number | Formula | Meaning |
| - | - | - |
| Mean | $\bar{x} = (x_1 + x_2 + \dots + x_N) / N$ | The center of the data. Every value contributes, so extreme values pull it |
| Median | $N$ odd: $x_{((N+1)/2)}$ $N$ even: $(x_{(N/2)} + x_{(N/2+1)}) / 2$ | The middle value: half the data lies below it. It depends only on order, so an extreme value, however large, takes up just one position and [does not shift it](https://www.itl.nist.gov/div898/handbook/eda/section3/eda351.htm) |
| Variance | $s^2 = \sum (x_i - \bar{x})^2 / (N - 1)$ | The spread of the data around the mean. It is computed from squared deviations, so its unit is %², not the unit of the data |
| Standard deviation | $s = \sqrt{s^2}$ | The [square root](https://www.itl.nist.gov/div898/handbook/eda/section3/eda356.htm) of the variance, which brings the spread back to the unit of the data (%). Extreme values inflate it |
| Quantile (percentile, quartile) | $Q_p = x_{(j)} + g\,(x_{(j+1)} - x_{(j)})$ | The proportion $p$ of the data lies below $Q_p$. Q1 is $Q_{0.25}$ and Q3 is $Q_{0.75}$ |
| IQR | $\text{IQR} = Q_3 - Q_1$ | The spread of the middle half of the data, so extreme values barely change it |

On the 14,184 returns, the mean is −0.0017%, the median +0.0008%, the standard deviation 0.4725% (variance 0.2233 %²), Q1 −0.1983%, Q3 +0.2022% and the IQR 0.4004 percentage points.

Try dropping the 96 hours in which the price moved by more than 2%, the hours outside the frame of the chart. They are only 0.7% of the hours but make up 22% of the sum of squared deviations, so the standard deviation falls by more than 11% (to 0.4183%) while the IQR falls by less than 1% (to 0.3974%). The mean changes to −0.0004%, and the median edges up to +0.0010%.

Divide the variance by $N - 1$ when the data is a sample from a larger set, since dividing by $N$ tends to give a result lower than the variance of the whole set ([Lane](https://onlinestatbook.com/2/summarizing_distributions/variability.html)); divide by $N$ when the data is the whole set. [NumPy](https://numpy.org/doc/stable/reference/generated/numpy.std.html) divides by $N$ by default and [pandas](https://pandas.pydata.org/docs/reference/api/pandas.Series.std.html) by $N - 1$, but with 14,184 values both give 0.4725%.

## References

Sources: [Lane, Online Statistics Education](https://onlinestatbook.com/2/summarizing_distributions/summarizing_distributions.html) (Rice University), the NIST/SEMATECH e-Handbook sections [1.3.5.1](https://www.itl.nist.gov/div898/handbook/eda/section3/eda351.htm) and [1.3.5.6](https://www.itl.nist.gov/div898/handbook/eda/section3/eda356.htm), and the documentation of [NumPy](https://numpy.org/doc/stable/reference/generated/numpy.quantile.html) and pandas. Prices come from Binance's public API.

## Read next

> [card] [Law of large numbers](https://quantumine.net/learn/learn/foundations/law-of-large-numbers.md)
>
> How close the average of many repetitions gets to the expected value, seen through coin tosses.

> [card] [Expected value](https://quantumine.net/learn/learn/foundations/expected-value.md)
>
> The average profit or loss per bet, computed from a strategy's win probability and its average win and loss.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.