Function Application
Pandas - Applying Custom Functions
Section titled “Pandas - Applying Custom Functions”Pandas allows you to apply custom functions or functions from other libraries to its objects (Series, DataFrame). Understanding how to apply functions effectively is key to leveraging Pandas for complex data manipulation. There are three main methods for function application:
- DataFrame-wise:
pipe()- For chaining functions that expect a DataFrame or Series. - Row/Column-wise:
apply()- For applying a function along an axis (rows or columns). - Element-wise:
applymap()(DataFrame) /map()(Series) - For applying a Python function to each element individually.
Performance Note: Vectorized operations (using built-in Pandas/NumPy functions directly on Series/DataFrames) are almost always faster than applying custom Python functions using these methods. Use apply, applymap, or map when vectorized solutions are not available or are overly complex.
DataFrame-wise Application: .pipe()
Section titled “DataFrame-wise Application: .pipe()”The .pipe() method allows you to chain custom functions in a more readable way, especially functions that expect a DataFrame or Series as the first argument. It passes the DataFrame (or Series) it’s called on as the first argument to the provided function, along with any other specified arguments.
Consider a scenario where you have a function that processes a DataFrame, perhaps adding a constant value.
Example: adder function via pipe()
Section titled “Example: adder function via pipe()”Let’s define a simple function add_value that adds a specified number to all elements of a DataFrame.
import pandas as pdimport numpy as np
def add_value(input_df, value_to_add): """Adds a constant value to all elements of a DataFrame.""" return input_df + value_to_add
rng = np.random.default_rng(42)df = pd.DataFrame(rng.standard_normal((5, 3)), columns=['col1', 'col2', 'col3'])
print("Original DataFrame:")print(df)
# Apply the function using pipedf_added = df.pipe(add_value, value_to_add=2)
print("\nDataFrame after df.pipe(add_value, 2):")print(df_added)
# Without pipe (less readable for complex chains):# df_added_alt = add_value(df, 2)# print("\nDataFrame using add_value(df, 2):")# print(df_added_alt)Example output:
Original DataFrame: col1 col2 col30 0.496714 -0.138264 0.6476891 1.523030 -0.234153 -0.2341372 1.579213 0.767435 -0.4694743 0.542560 -0.463418 -0.4657304 -0.465729 1.104638 -0.122903
DataFrame after df.pipe(add_value, 2): col1 col2 col30 2.496714 1.861736 2.6476891 3.523030 1.765847 1.7658632 3.579213 2.767435 1.5305263 2.542560 1.536582 1.5342704 1.534271 3.104638 1.877097pipe is especially useful when you have multiple custom processing steps to apply sequentially.
Row or Column-wise Application: .apply()
Section titled “Row or Column-wise Application: .apply()”The .apply() method applies a function along an axis of a DataFrame. The function passed to apply receives a Series (representing a row or column) as input.
axis=0(default): Apply function to each column (function receives each column Series).axis=1: Apply function to each row (function receives each row Series).
Caution: Using apply(..., axis=1) can be slow, as it often involves iterating row by row internally. Always prefer vectorized operations if possible.
Example 1: Column-wise Mean (axis=0)
Section titled “Example 1: Column-wise Mean (axis=0)”import pandas as pdimport numpy as np
rng = np.random.default_rng(43)df = pd.DataFrame(rng.standard_normal((5, 3)), columns=['col1', 'col2', 'col3'])
print("DataFrame:")print(df)
# Calculate the mean of each column# The function np.mean receives each column Seriescolumn_means = df.apply(np.mean, axis=0)print("\nColumn Means (df.apply(np.mean, axis=0)):")print(column_means)
# This is equivalent to the more direct and usually preferred method:# column_means_direct = df.mean(axis=0)# print("\nColumn Means (df.mean(axis=0)):")# print(column_means_direct)Example output:
DataFrame: col1 col2 col30 -0.535952 -0.875490 -0.2690011 0.125819 0.747178 -1.2557432 -1.034384 -0.977989 -0.3759913 0.417561 -0.166471 0.6745154 -0.563337 -0.508814 -0.494112
Column Means (df.apply(np.mean, axis=0)):col1 -0.318059col2 -0.356317col3 -0.344066dtype: float64Example 2: Row-wise Mean (axis=1)
Section titled “Example 2: Row-wise Mean (axis=1)”# Using df from Example 1
# Calculate the mean of each row# The function np.mean receives each row Seriesrow_means = df.apply(np.mean, axis=1)print("\nRow Means (df.apply(np.mean, axis=1)):")print(row_means)
# Again, equivalent to the more direct method:# row_means_direct = df.mean(axis=1)# print("\nRow Means (df.mean(axis=1)):")# print(row_means_direct)Example output:
Row Means (df.apply(np.mean, axis=1)):0 -0.5601481 -0.1275822 -0.7961213 -0.0214654 -0.522088dtype: float64Example 3: Row-wise Range (Max - Min) using Lambda
Section titled “Example 3: Row-wise Range (Max - Min) using Lambda”You can use lambda functions for concise custom operations.
# Using df from Example 1
# Calculate the range (max - min) for each rowrow_range = df.apply(lambda s: s.max() - s.min(), axis=1)print("\nRow Range (df.apply(lambda s: s.max() - s.min(), axis=1)):")print(row_range)Example output:
Row Range (df.apply(lambda s: s.max() - s.min(), axis=1)):0 0.6064891 2.0029212 0.6583933 1.8543394 0.069225dtype: float64Element-wise Application: .applymap() (DataFrame) & .map() (Series)
Section titled “Element-wise Application: .applymap() (DataFrame) & .map() (Series)”When you need to apply a Python function that operates on a single value to every element of a DataFrame or Series, use applymap() or map() respectively.
.applymap(func): Appliesfuncto each element of a DataFrame..map(func): Appliesfuncto each element of a Series.
Performance: These methods iterate element by element at the Python level and can be significantly slower than vectorized operations or even apply. Use them when you need a pure Python function applied element-wise and vectorization isn’t feasible.
Example 1: Using .map() on a Series
Section titled “Example 1: Using .map() on a Series”Let’s format numbers in a Series as currency strings.
import pandas as pdimport numpy as np
rng = np.random.default_rng(44)s = pd.Series(rng.random(5) * 100)
print("Original Series:")print(s)
# Apply a formatting function using mapformatted_series = s.map(lambda x: f'${x:,.2f}') # Format as $xxx.xxprint("\nFormatted Series (s.map(lambda x: f'${x:,.2f}')):")print(formatted_series)Example output:
Original Series:0 67.8416751 24.0975002 94.9169093 23.5393934 26.238116dtype: float64
Formatted Series (s.map(lambda x: f'${x:,.2f}')):0 $67.841 $24.102 $94.923 $23.544 $26.24dtype: objectExample 2: Using .applymap() on a DataFrame
Section titled “Example 2: Using .applymap() on a DataFrame”Let’s apply the same currency formatting function to all elements of a DataFrame.
# Using df from Example 1
print("Original DataFrame:")print(df)
# Apply the formatting function using applymapformatted_df = df.applymap(lambda x: f'{x:.2f}') # Format to 2 decimal placesprint("\nFormatted DataFrame (df.applymap(lambda x: f'{x:.2f}')):")print(formatted_df)Example output:
Original DataFrame: col1 col2 col30 -0.535952 -0.875490 -0.2690011 0.125819 0.747178 -1.2557432 -1.034384 -0.977989 -0.3759913 0.417561 -0.166471 0.6745154 -0.563337 -0.508814 -0.494112
Formatted DataFrame (df.applymap(lambda x: f'{x:.2f}')): col1 col2 col30 -0.54 -0.88 -0.271 0.13 0.75 -1.262 -1.03 -0.98 -0.383 0.42 -0.17 0.674 -0.56 -0.51 -0.49