Q&A library Python

How do I take part of a string in every row of a pandas column?

Python Beginner slicingstr-accessorstrings

Short answer

Use the str accessor with ordinary slicing to take a fixed range of characters from every value. It is vectorised and handles missing values, unlike a loop or an apply.

The short version

Use .str[:3] to take a fixed slice from every value in a column. The .str accessor supports normal Python slicing, applies it to the whole column at once, and returns NaN for missing values rather than raising.

Slicing a column

import pandas as pd

codes = pd.Series(["ABCDEF", "GHIJKL"])

codes.str[:3].tolist()    # ['ABC', 'GHI']
codes.str[-2:].tolist()   # ['EF', 'KL']
codes.str[2:4].tolist()   # ['CD', 'IJ']

The syntax is exactly Python's slicing, applied element by element. Negative indices count from the end, as usual.

Why not apply

codes.apply(lambda s: s[:3])   # works, but slower and fragile

This runs a Python function per row, and it raises TypeError on any missing value, because you cannot slice a NaN. The .str accessor is vectorised and returns NaN for missing entries, so it survives real data.

Getting a single character

.str[0] takes the first character, and .str.get(0) does the same with a clearer name.

codes.str[0].tolist()        # ['A', 'G']
codes.str.get(0).tolist()    # ['A', 'G']

When the position is not fixed

Fixed slicing assumes every value has the same layout, which real data often breaks. If the part you want is delimited rather than positional, split instead.

pd.Series(["pen-red"]).str.split("-").str[0]     # ['pen']
pd.Series(["pen-red"]).str.split("-", expand=True)  # two columns

And if the structure varies, use a regular expression with extract, which returns NaN for anything that does not match, telling you which rows break the pattern.

pd.Series(["PEN-001"]).str.extract(r"([A-Z]+)-(\d+)")

The fragility of fixed positions

Slicing by position is the least robust approach, because it silently produces wrong results the moment a value has a different length or layout. A code that gains a prefix, or an occasional short value, will slice into the wrong place and give plausible nonsense.

So use positional slicing only when the format is genuinely fixed and guaranteed (a country code, a fixed-width export). Otherwise prefer splitting on a delimiter or extracting with a pattern, both of which fail visibly rather than silently.

Checking your assumption

Before slicing by position, confirm every value really is the same shape.

codes.str.len().value_counts()

If more than one length appears, positional slicing is the wrong tool and will corrupt some of your rows.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project