A percentile rank answers: what percentage of the data falls at or below a given value? Definition used here: rank = (count of values ≤ target) / n × 100 — the "weak" definition. Loop over values, count those ≤ target, divide by n and scale to 100. With scipy, percentileofscore(x, target, kind='weak') uses the same ≤ rule. Values: [2,4,...,20] n=10, target=12 → 6 values ≤ 12 → rank=60.0.

By hand

Definition: rank = (count of values ≤ target) / n × 100 (kind='weak'). Loop over values; increment count when v <= target. With target=12 and n=10, six values qualify (2,4,6,8,10,12) → rank = 6/10 × 100 = 60.0.

naive.py
Replay: real traced execution (multi-file project)
values = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
target = 12
n = len(values)
count = 0
for v in values:
    if v <= target:
        count = count + 1
rank = count / n * 100
print('RESULT:', round(rank, 2))
  1. values ← [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

    1values = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]2target = 12
    values this step[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]values
  2. target ← 12

    1values = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]2target = 123n = len(values)
    values this step12target
  3. n ← 10

    2target = 123n = len(values)4count = 0
    values this step10n
  4. count ← 0

    3n = len(values)4count = 05for v in values:
    values this step0count
  5. v ← 2, count ← 1

    pass 1 of 6
    4count = 05for v in values:6    if v <= target:7        count = count + 18rank = count / n * 100
    values this step2v0 1count
    All 6 passes — pass 1 is the card above
    passvcount
    120 1
    22 41 2
    34 62 3
    46 83 4
    58 104 5
    610 125 6
  6. v ← 14

    pass 1 of 4
    4count = 05for v in values:6    if v <= target:7        count = count + 1
    values this step12 14v
    All 4 passes — pass 1 is the card above
    passv
    112 14
    214 16
    316 18
    418 20
  7. for v in values:

    4count = 05for v in values:6    if v <= target:
  8. rank ← 60.0

    7        count = count + 18rank = count / n * 1009print('RESULT:', round(rank, 2))
    values this step60.0rank
  9. stdout ← RESULT: 60.0

    8rank = count / n * 1009print('RESULT:', round(rank, 2))
    values this stepRESULT: 60.0stdout

With the library

scipy.stats.percentileofscore(x, target, kind='weak') counts values ≤ target and scales to 100 — identical to the naive formula. kind='strict' counts strictly < target; kind='mean' averages the two. Always specify kind explicitly to avoid ambiguity.

library.py
from scipy.stats import percentileofscore
from dalib.display import set_display
set_display()

values = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
target = 12
rank = percentileofscore(values, target, kind='weak')
print('target:', target)
print('n:', len(values))
print('RESULT:', round(rank, 2))
target: 12
n: 10
RESULT: 60.0

Implementation notes

  • kind='weak' (≤) is the most common definition and matches the intuition "12 is at the 60th percentile." kind='strict' (<) gives 50.0 here (five values below 12); kind='mean' gives 55.0.
  • Percentile rank is the inverse of np.percentile: given a rank, percentile gives the value; given a value, percentile rank gives the rank.
  • Cross-reference: five-number-summary (this chapter) for the fixed landmarks (0, 25, 50, 75, 100th percentiles) of a distribution.