Percent of Total
Python · GroupBy & SQL-Style Analytics
Theory
Share of total is a value divided by a total, and the only question is which total. Share of the grand total is a simple vectorised division: df['amount'] / df['amount'].sum() * 100. Share within a group uses transform so every row is divided by its own group's total: df['amount'] / df.groupby('city')['amount'].transform('sum') * 100. This mirrors SQL's amount / SUM(amount) OVER (PARTITION BY city).
For a summary table, aggregate first and divide the result by its own sum: totals = df.groupby('city')['amount'].sum(); totals / totals.sum(). For counts, value_counts(normalize=True) gives proportions directly, and pd.crosstab(a, b, normalize='index') gives row percentages of a cross-tab. Round only for display (.round(1)) and check that each set of shares adds up to 100. Sorting by share and applying cumsum() gives a cumulative share, used for 80/20 (Pareto) analysis.
Why we use it
Percentages make different-sized groups comparable and answer questions like 'which city brings most of our revenue' or 'what share of each city's sales comes from each customer'.
When to use it
Divide by sum() for share of grand total, by groupby().transform('sum') for share within group, and use value_counts(normalize=True) for category proportions. Keep the unrounded numbers for further maths.
How it works
- Choose the denominator
- Share of the grand total? Yes: Divide by df['col'].sum(). No: Share within each group.
- Group total for every row: groupby(key)[col].transform('sum')
- Divide each value by its group total, multiply by 100
- Round for display and check the totals are 100
- Percentage column
Syntax
df['col'] / df['col'].sum() * 100
df['col'] / df.groupby('key')['col'].transform('sum') * 100
totals = df.groupby('key')['col'].sum(); totals / totals.sum()
s.value_counts(normalize=True)Example
df['pct_of_city'] = (df['amount'] / df.groupby('city')['amount'].transform('sum') * 100).round(1)Key points
- Grand total: divide by s.sum(); group total: divide by groupby().transform('sum')
- Summary shares: aggregate, then totals / totals.sum()
- value_counts(normalize=True) gives proportions of counts
- Round only for display; check that shares add up to 100
- SQL equivalent: amount * 100.0 / SUM(amount) OVER (PARTITION BY city)