Calculating Summary Statistics Across Columns in Pandas – axis=1 Best Practices 2026

Data Manipulation Mar 18, 2026

Calculating Summary Statistics Across Columns in Pandas – axis=1 Best Practices 2026

When you need to calculate statistics across columns (horizontally, row by row), you must use axis=1. This is very different from the default axis=0 (which works down columns). In 2026, knowing when and how to use axis=1 is essential for tasks like calculating row totals, averages, or custom scores.

TL;DR — When to Use axis=1

  • Use axis=1 when you want to operate across columns (per row)
  • Use axis=0 (default) when you want to operate down columns (per column)

1. Basic Summary Statistics Across Columns

import pandas as pd

# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
    "order_date": pd.date_range("2026-01-01", periods=n, freq="3D"),
    "customer_id": rng.integers(101, 121, n),
    "customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
    "product_id": rng.integers(1, 9, n),
    "region": rng.choice(["North", "South", "East", "West"], n),
    "category": rng.choice(["Electronics", "Computers", "Books", "Home"], n),
    "payment_method": rng.choice(["card", "cash", "wallet"], n),
    "segment": rng.choice(["consumer", "business"], n),
    "quantity": rng.integers(1, 6, n),
    "amount": rng.uniform(50, 2500, n).round(2),
    "discount": rng.choice([0.0, 0.05, 0.1, 0.2], n),
}).assign(
    price=lambda d: (d["amount"] / d["quantity"]).round(2),
    profit=lambda d: (d["amount"] * rng.uniform(0.05, 0.3, n)).round(2),
).assign(
    ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
    status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)

df = pd.read_csv("sales_data.csv")

# Sum across columns (row totals)
df["total_per_row"] = df[["amount", "quantity", "discount"]].sum(axis=1)

# Mean across columns
df["average_per_row"] = df[["amount", "quantity", "profit"]].mean(axis=1)

# Maximum value in each row
df["max_value"] = df[["amount", "quantity", "profit"]].max(axis=1)

2. Real-World Examples

# Calculate total order value per row
# demo columns for this example
df["product_price"] = df["amount"]
df["shipping_cost"] = 15.0
df["tax"] = (df["amount"] * 0.08).round(2)

df["order_total"] = df[["product_price", "shipping_cost", "tax"]].sum(axis=1)

# Calculate average of multiple score columns
scores = pd.DataFrame({"exam1": [80, 65], "exam2": [90, 70], "exam3": [85, 60], "project": [95, 75]})   # demo scores
scores["overall_score"] = scores[["exam1", "exam2", "exam3", "project"]].mean(axis=1)

# Find which metric is highest in each row
scores["best_metric"] = scores[["exam1", "exam2", "exam3", "project"]].idxmax(axis=1)
print(scores[["overall_score", "best_metric"]])

3. Advanced: Custom Calculations Across Columns

# Small demo table for this block
df = pd.DataFrame({"score1": [80, 60], "score2": [70, 90], "score3": [90, 50],
                   "north_sales": [100, 40], "south_sales": [50, 40], "east_sales": [50, 20]})

# Weighted average across columns
weights = [0.5, 0.3, 0.2]
df["weighted_score"] = (
    df[["score1", "score2", "score3"]] * weights
).sum(axis=1)

# Percentage of total per row
cols = ["north_sales", "south_sales", "east_sales"]
df["north_percentage"] = df[cols].apply(lambda row: row["north_sales"] / row.sum() * 100, axis=1)

4. Best Practices in 2026

  • Use axis=1 explicitly when calculating row-wise statistics
  • Select only the relevant numeric columns before applying axis=1 operations
  • Be careful with missing values — they can affect row-wise calculations (consider .fillna(0) first if appropriate)
  • Use .sum(axis=1), .mean(axis=1), .max(axis=1), etc.
  • For very complex row-wise logic, consider using .apply(axis=1) with a custom function

Row-Wise Statistics Explained

Summary statistics across columns answer questions about each row: the total cost of an order, the average score of a student, or the highest reading of a sensor at one moment. In pandas you get them by passing axis=1 to methods such as sum, mean, max and std.

Select the relevant numeric columns first, so identifiers or text never slip into the calculation. Missing values are skipped by default; pass skipna=False when a missing component should make the whole row result missing.

Worked Example: Order Totals and Score Profiles

We compute an order total, a row average, the best subject per student, and the effect of skipna.

import pandas as pd
import numpy as np

orders = pd.DataFrame({
    "item": [20.0, 35.5, 12.0],
    "shipping": [5.0, 0.0, 3.5],
    "tax": [1.6, 2.8, np.nan],
})
orders["total"] = orders[["item", "shipping", "tax"]].sum(axis=1)
orders["total_strict"] = orders[["item", "shipping", "tax"]].sum(axis=1, skipna=False, min_count=3)
print(orders)

scores = pd.DataFrame({"math": [80, 92], "art": [88, 70], "music": [75, 85]}, index=["Ana", "Ben"])
print(scores.mean(axis=1).round(1))
print(scores.idxmax(axis=1))

The third order has no tax value, so its normal total skips it while the strict total becomes NaN. idxmax(axis=1) returns the column name of the best subject for each student, which is often more useful than the number itself.

Common Mistakes

  • Including ID or text columns in a row-wise calculation.
  • Forgetting that NaN values are skipped silently.
  • Using apply with a Python function where axis=1 methods are faster.
  • Confusing axis=1 with a per-column summary.

Quick Summary

  • Use axis=1 for one statistic per row.
  • Select numeric columns explicitly first.
  • Control missing values with skipna and min_count.
  • Use idxmax(axis=1) to find which column is largest.

Conclusion

Calculating summary statistics across columns (row by row) requires axis=1. In 2026, this is the standard way to compute totals, averages, weighted scores, or any custom row-level metric. Understanding the difference between axis=0 (default, column-wise) and axis=1 (row-wise) is fundamental to writing correct and efficient Pandas code.

Next steps:

  • Identify places in your code where you calculate totals or averages across several columns and make sure you are using axis=1

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices