Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

Apply in pandas: DataFrame.apply and Series.apply

How pandas apply works for rows, columns and Series, with tested examples, a result_type table and documented pitfalls.

Written and maintained by CASRAI Editorial Board

Last updated

Reader task: you have a pandas DataFrame or Series and need to run your own function over its rows, columns or values, and know which arguments change the output shape. apply in pandas calls a function you supply on each column or row of a DataFrame (DataFrame.apply) or on each element of a Series (Series.apply) and combines the results into a Series or DataFrame.

Command forms below were checked against the pandas 3.1.1 documentation (read 2026-11-07), and the worked example was run on pandas 3.0.5.

What apply is used for in research

Analysts use apply when a derived variable needs logic that no built-in column operation expresses: a score computed from several fields in one record, a recode with custom rules, or a per-column summary function. The documentation describes DataFrame.apply as applying “a function along an axis of the DataFrame”. The objects passed to your function are Series: with axis=0 the function receives each column, with axis=1 each row. For clean-up steps that come before this, see data wrangling and Python for exploratory data analysis.

Install or access (official source only)

The pandas installation page lists these official routes: pip install pandas, or conda install -c conda-forge pandas. To create an isolated conda environment: conda create -c conda-forge -n name_of_my_env python pandas, then conda activate name_of_my_env. The page recommends working in a virtual environment and notes that pandas distributed through the Anaconda channel is not officially managed by the pandas development team. Source: pandas.pydata.org/docs/getting_started/install.html.

Step-by-step: apply a function in pandas

The data below are a small illustrative table (three made-up participants with pre and post scores), not real study data.

  1. Build the data.

    import pandas as pd
    import numpy as np
    df = pd.DataFrame({"participant": ["P1", "P2", "P3"], "pre": [12, 15, 9], "post": [18, 14, 13]})
  2. Apply a function to each row (axis=1). The function receives one row as a Series, so you index it by column name.

    df.apply(lambda r: r["post"] - r["pre"], axis=1)

    Output values: 6, -1, 4.

  3. Apply a function to each column (axis=0, the default).

    df[["pre", "post"]].apply(lambda c: c.max() - c.min())

    Output: pre 6, post 5 (one value per column).

  4. Return several values per row. Return a Series from the function and the result becomes a DataFrame whose columns are the Series index.

    def summarise(r):
        return pd.Series({"change": r["post"] - r["pre"], "improved": r["post"] > r["pre"]})
    df.apply(summarise, axis=1)

    Output: columns change (6, -1, 4) and improved (True, False, True).

  5. Pass extra arguments. Positional arguments go in args=(...); keyword arguments are passed straight through.

    def pct(r, base):
        return round(100 * (r["post"] - r["pre"]) / base, 1)
    df.apply(pct, axis=1, base=10)

    Output values: 60.0, -10.0, 40.0.

  6. Apply to one column (Series.apply). This is the usual form for if-else recodes with a lambda.

    df["pre"].apply(lambda x: "high" if x >= 12 else "low")

    Output: high, high, low. Per the documentation, Series.apply passes each element to a callable func, as Series.map does.

Apply with a lambda over multiple columns

Use axis=1 and read the columns you need from the row, as in step 2. Select a subset of columns first (df[["pre", "post"]].apply(...)) when you only want those passed in.

Signature and arguments

DataFrame.apply(func, axis=0, raw=False, result_type=None, args=(), by_row='compat', engine=None, engine_kwargs=None, **kwargs). Series.apply(func, args=(), *, by_row='compat', **kwargs).

  • axis: 0 or ‘index’ applies the function to each column; 1 or ‘columns’ to each row. In the function, the Series .name is the column label (axis=0) or row label (axis=1).
  • raw: False passes Series; True passes ndarrays. The docs say that if you are only applying a NumPy reduction function, True “will achieve much better performance”.
  • args and **kwargs: extra positional and keyword arguments for func.
  • by_row (added in pandas 2.1.0): only matters when func is list-like or dict-like of functions. For Series.apply, “compat” passes each element to a callable; False passes the whole Series.
  • engine (added in 2.2.0): ‘python’, ‘numba’ or a JIT decorator. The docs say JIT engines are only recommended for functions that take a significant amount of time.

Note: convert_dtype does not appear in the current Series.apply signature; older tutorials that use it were written for earlier versions.

Output and how to read it

The return value is a Series or a DataFrame. result_type (DataFrame.apply, for list-like returns) decides the shape. The outputs below are the ones in the official examples, for df = pd.DataFrame([[4, 9]] * 3, columns=["A", "B"]):

result_type Documented meaning Official example result
None (default) List-likes are returned as a Series; a returned Series is expanded to columns df.apply(lambda x: [1, 2], axis=1) gives a Series of [1, 2] objects (dtype object)
'expand' List-like results become columns Same call with result_type="expand" gives a DataFrame with columns 0 and 1, values 1 and 2
'reduce' Returns a Series if possible rather than expanding list-likes The opposite of expand
'broadcast' Results keep the original shape, index and columns Columns A and B with values 1 and 2

When the function returns a Series with a labelled index, the result columns take those labels (the documentation example gives columns foo and bar). result_type has no effect when func is a NumPy ufunc such as np.sqrt; wrap it in a lambda to use it.

Troubleshooting

Unexpected results or errors when the function changes its input

The pandas user guide (“Mutating with User Defined Function (UDF) methods”, which covers apply, aggregate, transform and filter) warns that mutating the object being iterated gives unexpected behavior. The documented fix is to copy first inside the function (s = s.copy() before an operation such as s.pop("a")). The DataFrame.apply notes also say functions that mutate the passed object are not supported.

Lists inside cells

The same guide notes that lists stored in cells produce object dtype and no vectorised code paths.

Wrong axis

If a row-wise formula fails with a key error or returns one value per column, check axis: the default 0 passes columns, not rows.

Exceptions inside the function

Put a try/except inside your own function and return a missing value on failure so one bad record does not stop the whole call. Converting strings to numbers is a separate job from apply and belongs to pd.to_numeric; see the pandas documentation for its errors argument.

Parallel apply

The speed-related options documented in DataFrame.apply itself are raw=True and the engine argument. This page makes no benchmark claims.

Free alternatives within pandas and NumPy

Option What the documentation says
Pass a NumPy ufunc directly, e.g. df.apply(np.sqrt) Official example: applies the function to every cell (4 becomes 2.0, 9 becomes 3.0)
Series.map Listed in the Series.apply “See also” as the element-wise counterpart
Series.agg and Series.transform Listed in the Series.apply “See also” as related methods
raw=True with a NumPy reduction Documented as giving much better performance for that case

Related CASRAI how-tos: how to drop columns in pandas and dropna in pandas.

FAQ

What does apply do in pandas?

It runs a function you supply along an axis of a DataFrame (each column or each row) or over each element of a Series, and returns the combined result as a Series or DataFrame.

What is the difference between axis=0 and axis=1 in apply?

axis=0 (default) applies the function to each column; axis=1 applies it to each row.

How do I use apply with lambda and if-else?

Write the conditional expression inside the lambda, for example df["pre"].apply(lambda x: "high" if x >= 12 else "low").

How do I return multiple columns from apply?

Return a pd.Series with a labelled index from the function and use axis=1, or set result_type="expand" for list-like returns.

Is there a top-level pandas.apply?

No; apply is a method on DataFrame and Series, so call df.apply(...) or s.apply(...).

Sources (official documentation, read 2026-11-07)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Apply in pandas: DataFrame.apply and Series.apply

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →