Compute the sample standard deviation — the square root of the sample variance. Standard deviation is in the same units as the data, making it more interpretable than variance. By hand, compute the sample variance (dividing by n−1) then take the square root. With the library, statistics.stdev does both steps with the same n−1 denominator.

By hand

The same two loops as sample-variance: accumulate total for the mean, then sq_diff for the sum of squared deviations. Divide by n - 1 and call math.sqrt. With sq_diff=54.0 and n-1=6, variance=9.0, and math.sqrt(9.0) = 3.0 exactly.

naive.py
Replay: real traced execution (multi-file project)
import math
values = [4, 8, 6, 13, 10, 6, 9]
n = len(values)
total = 0.0
for v in values:
    total = total + v
mean = total / n
sq_diff = 0.0
for v in values:
    sq_diff = sq_diff + (v - mean) ** 2
std = math.sqrt(sq_diff / (n - 1))
print('RESULT:', round(std, 10))
  1. import math

    1import math2values = [4, 8, 6, 13, 10, 6, 9]
  2. values ← [4, 8, 6, 13, 10, 6, 9]

    1import math2values = [4, 8, 6, 13, 10, 6, 9]3n = len(values)
    values this step[4, 8, 6, 13, 10, 6, 9]values
  3. n ← 7

    2values = [4, 8, 6, 13, 10, 6, 9]3n = len(values)4total = 0.0
    values this step7n
  4. total ← 0.0

    3n = len(values)4total = 0.05for v in values:
    values this step0.0total
  5. v ← 4, total ← 4.0

    pass 1 of 7
    4total = 0.05for v in values:6    total = total + v7mean = total / n
    values this step4v0.0 4.0total
    All 7 passes — pass 1 is the card above
    passvtotal
    140.0 4.0
    24 84.0 12.0
    38 612.0 18.0
    46 1318.0 31.0
    513 1031.0 41.0
    610 641.0 47.0
    76 947.0 56.0
  6. for v in values:

    4total = 0.05for v in values:6    total = total + v
  7. mean ← 8.0

    6    total = total + v7mean = total / n8sq_diff = 0.0
    values this step8.0mean
  8. sq_diff ← 0.0

    7mean = total / n8sq_diff = 0.09for v in values:
    values this step0.0sq_diff
  9. v ← 4, sq_diff ← 16.0

    pass 1 of 7
    8sq_diff = 0.09for v in values:10    sq_diff = sq_diff + (v - mean) ** 211std = math.sqrt(sq_diff / (n - 1))
    values this step9 4v0.0 16.0sq_diff
    All 7 passes — pass 1 is the card above
    passvsq_diff
    19 40.0 16.0
    24 8
    38 616.0 20.0
    46 1320.0 45.0
    513 1045.0 49.0
    610 649.0 53.0
    76 953.0 54.0
  10. for v in values:

    8sq_diff = 0.09for v in values:10    sq_diff = sq_diff + (v - mean) ** 2
  11. std ← 3.0

    10    sq_diff = sq_diff + (v - mean) ** 211std = math.sqrt(sq_diff / (n - 1))12print('RESULT:', round(std, 10))
    values this step3.0std
  12. stdout ← RESULT: 3.0

    11std = math.sqrt(sq_diff / (n - 1))12print('RESULT:', round(std, 10))
    values this stepRESULT: 3.0stdout

With the library

statistics.stdev uses n−1 and matches math.sqrt(statistics.variance(values)) exactly. np.std defaults to ddof=0 (population std) — pass ddof=1 for the sample std. The snapshot shows both numpy variants alongside the stdlib result to make the ddof trap visible.

library.py
import statistics
import numpy as np
from dalib.display import set_display
set_display()

values = [4, 8, 6, 13, 10, 6, 9]
std_stdlib = float(statistics.stdev(values))
std_np_pop = float(np.std(values))
std_np_samp = float(np.std(values, ddof=1))
print('statistics.stdev:', std_stdlib)
print('np.std (ddof=0): ', round(std_np_pop, 4))
print('np.std (ddof=1): ', std_np_samp)
print('RESULT:', round(std_stdlib, 10))
statistics.stdev: 3.0
np.std (ddof=0):  2.7775
np.std (ddof=1):  3.0
RESULT: 3.0

Implementation notes

  • np.std default is ddof=0 (population std, divides by n under the square root). Pass ddof=1 whenever you want the sample std from numpy.
  • statistics.stdev always uses n−1; there is no ddof parameter.
  • Standard deviation is in the same units as the original values (here, whatever unit the values represent). Variance is in squared units, which is why std is more commonly reported.
  • Cross-reference: sample-variance (this chapter) for the squared version and a detailed explanation of Bessel's correction.