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=1when 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=1explicitly when calculating row-wise statistics - Select only the relevant numeric columns before applying
axis=1operations - 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