Here’s how to calculate standard deviation in Python: for a plain list, use statistics.stdev() for a sample or statistics.pstdev() for a whole population. With arrays, use numpy.std(), but watch its default:
import statistics
statistics.stdev([1250.50, 980.25, 1430.00]) # sample standard deviation
import numpy as np
np.std(data, ddof=1) # sample; plain np.std is population
I calculated everything below on Python 3.12.5, NumPy 2.5.3 and pandas 3.0.6 using a week of made-up store sales in USD.
You’ll get the built-in statistics module, the formula by hand, the NumPy and pandas difference that trips everyone up, standard deviation by group, and using it to flag outliers.
Standard deviation with the statistics module
You don’t need to install anything for lists; the standard library has it covered. stdev() treats the data as a sample and divides by n – 1; pstdev() treats it as the entire population and divides by n:
import statistics
sales = [1250.50, 980.25, 1430.00, 1105.75, 760.00, 1320.40, 1188.90] # one week, USD
print(f"mean: ${statistics.mean(sales):,.2f}")
print(f"sample stdev: ${statistics.stdev(sales):,.2f}") # divides by n - 1
print(f"population pstdev: ${statistics.pstdev(sales):,.2f}") # divides by n
print(f"variance (sample): {statistics.variance(sales):,.2f}")
try:
statistics.stdev([99.0])
except statistics.StatisticsError as err:
print("StatisticsError:", err)
Output:
mean: $1,147.97
sample stdev: $224.31
population pstdev: $207.67
variance (sample): 50,315.66
StatisticsError: stdev requires at least two data points

Use the sample version when your data’s a subset, which it usually is. For the average itself, see the mean function in Python.
Calculating standard deviation by hand
The formula is short: find the mean, square each value’s distance from it, average those squares, and take the square root. The only choice is dividing by n or n – 1:
sales = [1250.50, 980.25, 1430.00, 1105.75, 760.00, 1320.40, 1188.90] # one week, USD
import math
n = len(sales)
mean = sum(sales) / n
squared = [(x - mean) ** 2 for x in sales]
sample_sd = math.sqrt(sum(squared) / (n - 1))
population_sd = math.sqrt(sum(squared) / n)
print(f"mean ${mean:,.2f} | sample ${sample_sd:,.2f} | population ${population_sd:,.2f}")
Output:
mean $1,147.97 | sample $224.31 | population $207.67
Seeing the steps makes the n versus n – 1 choice concrete, and it’s what the libraries do for you. Dividing by n – 1 (Bessel’s correction) slightly widens the estimate to account for using a sample mean.
Why do NumPy and pandas give different standard deviations?
This catches a lot of people. np.std() defaults to the population formula (ddof=0), while pandas .std() defaults to the sample formula (ddof=1):
import numpy as np
import pandas as pd
import statistics
sales = [1250.50, 980.25, 1430.00, 1105.75, 760.00, 1320.40, 1188.90] # one week, USD
arr = np.array(sales)
print(f"np.std(arr): {np.std(arr):.2f} <- population (ddof=0) by default")
print(f"np.std(arr, ddof=1): {np.std(arr, ddof=1):.2f}")
print(f"pd.Series.std(): {pd.Series(sales).std():.2f} <- sample (ddof=1) by default")
print(f"statistics.stdev(): {statistics.stdev(sales):.2f}")
Output:
np.std(arr): 207.67 <- population (ddof=0) by default
np.std(arr, ddof=1): 224.31
pd.Series.std(): 224.31 <- sample (ddof=1) by default
statistics.stdev(): 224.31

ddof=1 to NumPy and all three sample results match.ddof stands for delta degrees of freedom: the divisor is n – ddof. I always pass it explicitly so nobody has to remember the defaults.
Standard deviation by group in pandas
With a DataFrame, groupby().agg() gives the mean and standard deviation per group in one step. The coefficient of variation (std divided by mean) makes groups of different sizes comparable:
import pandas as pd
df = pd.DataFrame({
"store": ["Austin"] * 5 + ["Denver"] * 5,
"sales": [1250.50, 1310.00, 1195.25, 1280.75, 1240.00,
620.00, 1850.50, 940.25, 1500.00, 780.00],
})
summary = df.groupby("store")["sales"].agg(["mean", "std"]).round(2)
summary["cv"] = (summary["std"] / summary["mean"]).round(3) # spread relative to the mean
print(summary)
Output:
mean std cv
store
Austin 1255.30 43.31 0.035
Denver 1138.15 518.24 0.455

For more on selecting the data first, see loc vs iloc in pandas.
Using standard deviation to find outliers
A z-score says how many standard deviations a value sits from the mean. Values beyond about 2 or 3 are worth your closer look:
import numpy as np
daily = np.array([1250.50, 980.25, 1430.00, 1105.75, 760.00, 1320.40, 1188.90,
1215.00, 3980.00, 1099.99, 1275.25, 1160.60])
mean, sd = daily.mean(), daily.std(ddof=1)
z = (daily - mean) / sd
print(f"mean ${mean:,.2f}, sd ${sd:,.2f}")
for amount, score in zip(daily, z):
flag = " <- outlier" if abs(score) > 2 else ""
print(f"${amount:>9,.2f} z = {score:+.2f}{flag}")
Output:
mean $1,397.22, sd $831.21
$ 1,250.50 z = -0.18
$ 980.25 z = -0.50
$ 1,430.00 z = +0.04
$ 1,105.75 z = -0.35
$ 760.00 z = -0.77
$ 1,320.40 z = -0.09
$ 1,188.90 z = -0.25
$ 1,215.00 z = -0.22
$ 3,980.00 z = +3.11 <- outlier
$ 1,099.99 z = -0.36
$ 1,275.25 z = -0.15
$ 1,160.60 z = -0.28

One big outlier inflates the standard deviation itself, so for messy data you’ll want the median and interquartile range as a cross-check.
Which standard deviation function to use
| Function | Default | Best for |
|---|---|---|
statistics.stdev() | Sample (n – 1) | Lists, no extra installs |
statistics.pstdev() | Population (n) | When you have every value |
np.std(a, ddof=1) | Population unless ddof=1 | Large arrays, speed |
Series.std() | Sample (ddof=1) | DataFrames and groups |
Continue with these stats and data guides:
Frequently asked questions
How do you calculate standard deviation in Python?
Use statistics.stdev(data) for a sample or statistics.pstdev(data) for a population. With NumPy, use np.std(data, ddof=1) for a sample.
What’s the difference between stdev and pstdev?
stdev() divides by n – 1 (sample) and pstdev() divides by n (population).
Why is np.std different from pandas std?
NumPy defaults to ddof=0 (population) and pandas defaults to ddof=1 (sample). Set ddof explicitly to match.
How do I calculate standard deviation without any library?
Compute the mean, sum the squared differences from it, divide by n – 1, and take the square root with math.sqrt().
Why does stdev raise StatisticsError?
A sample standard deviation needs at least two values.
Where is the statistics module documented?
See statistics in the Python docs.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn