Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

NumPy std: np.std Syntax, ddof (Population vs Sample) and Pitfalls

np.std divides by N by default. Learn axis, ddof=1 for sample SD, where=, nanstd, float32 precision and why pandas .std() gives a different number.

Written and maintained by CASRAI Editorial Board

Last updated

numpy.std computes the standard deviation of an array. Called as np.std(a), it divides by N, so it returns the population standard deviation of the flattened array. For a sample standard deviation, pass ddof=1. That single default is the most common source of mismatched numbers between NumPy, pandas and a calculator, and it is the main thing this guide settles.

What np.std is used for in research

Standard deviation summarises how far values spread around their mean. In research code, np.std is typically used to report the spread of a measured variable, to standardise features (subtract the mean, divide by the standard deviation), and to turn replicate measurements into an error estimate. The function operates on any array-like input and can reduce along one axis or across the whole array.

Related reading on CASRAI: coefficient of variation (standard deviation relative to the mean), mean absolute deviation as an alternative spread measure, and Python for exploratory data analysis for the wider workflow.

Install or access

The NumPy project’s install page gives pip install numpy for pip and conda install numpy (inside an activated environment) for conda. To confirm the install, run:

import numpy as np
print(np.__version__)

Source: numpy.org/install, checked 2026-10-29.

Step-by-step: np.std with axis, ddof, dtype and where

The signature in the current NumPy manual (v2.5) is:

numpy.std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=<no value>, *, where=<no value>, mean=<no value>, correction=<no value>)
  1. Whole array. np.std(a). With axis=None (the default) the standard deviation is taken over the flattened array. For a = np.array([[1, 2], [3, 4]]) the result is 1.1180339887498949.
  2. Along an axis. np.std(a, axis=0) gives one value per column, array([1., 1.]); np.std(a, axis=1) gives one value per row, array([0.5, 0.5]).
  3. Sample instead of population. np.std(a, ddof=1). The manual defines the divisor as N – ddof and states that ddof is zero by default.
  4. Control precision. np.std(a, dtype=np.float64). Integer input is computed in float64 by default; float input keeps the array’s own type.
  5. Use only some elements. Pass a boolean mask with where= (added in NumPy 1.20.0). With a = np.array([[14, 8, 11, 10], [7, 9, 10, 11], [10, 15, 5, 10]]), np.std(a) is 2.614064523559687 and np.std(a, where=[[True], [True], [False]]) is 2.0, because the third row is excluded.

Population vs sample: what ddof does

The manual describes ddof=0 as sometimes called the “population standard deviation” and ddof=1 as the “sample standard deviation”, using Bessel’s correction. It adds that the resulting estimate of the standard deviation is still biased, but less than it would have been without the correction.

A worked example you can reproduce: for x = np.array([2, 4, 4, 4, 5, 5, 7, 9]) the mean is 5 and the squared deviations sum to 32. np.std(x) divides by 8 and returns 2.0; np.std(x, ddof=1) divides by 7 and returns 2.138089935299395.

Which one to report

If the array is the entire population you care about, ddof=0 is the matching choice. If the array is a sample drawn to estimate the spread of a larger population, which is the usual situation in experimental work, use ddof=1 and say so in the methods text. Whichever you use, state it: a bare “SD” without the divisor is ambiguous.

The correction argument (NumPy 2.0 and later)

NumPy 2.0.0 added correction, the Array API name for ddof, and mean, which lets you pass a precomputed mean. Only one of ddof and correction may be given. np.std(x, correction=1) returns the same value as np.std(x, ddof=1).

Output and how to read it

The return value is a float (or an array of floats when you reduce along an axis), in the same units as your data. It is never negative. For complex input, NumPy takes the absolute value before squaring, so the result is real and nonnegative. Judge the number against the mean: a standard deviation of 2.0 means very different things around a mean of 5 and around a mean of 500, which is why the coefficient of variation is often reported alongside it.

Troubleshooting

NumPy and pandas give different answers

pandas defines DataFrame.std(*, axis=0, skipna=True, ddof=1, numeric_only=False, **kwargs) and normalises by N – 1 by default, so it returns a sample standard deviation where np.std returns a population one. The pandas documentation says that to match numpy.std you should use ddof=0 and skipna=False. Its own example, a DataFrame with age = [21, 25, 62, 43] and height = [1.61, 1.87, 1.49, 2.01], shows the gap:

Call age height
df.std() (ddof=1) 18.786076 0.237417
df.std(ddof=0) 16.269219 0.205609

The fix is to choose one convention and set ddof explicitly in both libraries.

The result is nan because of missing values

For an array with NaN values, use np.nanstd, which ignores NaNs; its divisor is N – ddof where N is the count of non-NaN values. For a = np.array([[1, np.nan], [3, 4]]), np.nanstd(a) returns 1.247219128924647, np.nanstd(a, axis=0) returns array([1., 0.]) and np.nanstd(a, axis=1) returns array([0., 0.5]). An all-NaN slice, or zero degrees of freedom, returns NaN with a RuntimeWarning.

float32 precision

The NumPy manual warns that the computed standard deviation can be inaccurate, especially for float32, and recommends dtype=np.float64. Its example fills a float32 array with half 1.0 and half 0.1: np.std(a) returns np.float32(0.45000005), while np.std(a, dtype=np.float64) returns 0.44999999925494177.

Division by zero or a warning with ddof=1

With ddof=1 a single-element slice has N – ddof equal to 0, so there is no valid estimate. Check that every slice you reduce over has more than ddof elements.

Free alternatives

Tool Function Default divisor
NumPy numpy.std N (ddof=0)
NumPy, ignoring NaN numpy.nanstd N – ddof, N = non-NaN count; ddof=0 by default
pandas DataFrame.std N – 1 (ddof=1)
Python standard library statistics.stdev (sample), statistics.pstdev (population) Sample and population respectively, by function name

For spreadsheet users, the CASRAI guide on Excel statistics functions covers the equivalent functions and how they relate to ddof.

Other NumPy functions you may combine with it: NumPy sqrt and NumPy log.

FAQ

Is np.std the population or sample standard deviation?

Population by default, because ddof is zero and the divisor is N. Pass ddof=1 for the sample standard deviation.

How do I get the standard deviation of each column?

Use np.std(a, axis=0). Use axis=1 for each row.

Why does pandas .std() disagree with np.std?

pandas defaults to ddof=1 and skipna=True; NumPy defaults to ddof=0. Use df.std(ddof=0, skipna=False) to match NumPy.

How do I ignore NaN values?

Use np.nanstd, which excludes NaNs from the calculation.

Sources

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about NumPy std: np.std Syntax, ddof (Population vs Sample) and Pitfalls

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →