Cap each value to a [lo, hi] range. Values below lo become lo, values above hi become hi, and values already in range are unchanged. By hand, apply a three-way branch in a loop. With pandas, Series.clip(lo, hi) performs the same capping in one call.

By hand

With pandas

Series.clip(lo, hi) caps every element to the given bounds in one pass. The snapshot shows values before and after so the replacements are immediately visible.

naive.py
values = [3, 15, 7, 42, 11, 2, 28, 9]
lo = 5
hi = 25
result = []
for v in values:
    if v < lo:
        result.append(lo)
    elif v > hi:
        result.append(hi)
    else:
        result.append(v)
print('RESULT:', result)
library.py
import pandas as pd
from dalib.display import set_display
set_display()

values = [3, 15, 7, 42, 11, 2, 28, 9]
lo = 5
hi = 25
df = pd.DataFrame({'x': values})
clipped = df['x'].clip(lo, hi)
result = clipped.tolist()
print('index:', clipped.index.tolist())
print('dtype:', clipped.dtype)
print('values before:', df['x'].tolist())
print('values after:', clipped.tolist())
print('RESULT:', result)
index: [0, 1, 2, 3, 4, 5, 6, 7]
dtype: int64
values before: [3, 15, 7, 42, 11, 2, 28, 9]
values after: [5, 15, 7, 25, 11, 5, 25, 9]
RESULT: [5, 15, 7, 25, 11, 5, 25, 9]

Implementation notes

  • clip preserves the dtype when all values and bounds are the same numeric type; integer input with integer bounds returns int64.
  • Clipping to a fixed range is sometimes called Winsorizing — replacing extremes with boundary values rather than discarding them.
  • Pass only one bound to clip on one side: s.clip(lower=lo) or s.clip(upper=hi).
  • Cross-reference: where-replace (numpy chapter) for conditional replacement using np.where.