GroupBy
On this page
Data analysis often requires comparing and summarizing values across categories — such as total sales per company, average sentiment by author, or word frequency per decade. Pandas simplifies these tasks through its powerful GroupBy functionality, which lets analysts split data into groups, apply calculations to each, and combine the results into concise summaries.
This chapter introduces the groupby() method and shows how to organize, aggregate, and describe grouped data to uncover meaningful patterns and relationships.
Creating a Sample Dataset
To understand grouping, we use invented sales rows with familiar company labels and fictional employees. These are not company accounts, audited performance data or current corporate identities.
import pandas as pd
# Sample dataset
data = {
'Company': ['GOOG', 'GOOG', 'MSFT', 'MSFT', 'FB', 'FB'],
'Person': ['Sam', 'Charlie', 'Amy', 'Vanessa', 'Carl', 'Sarah'],
'Sales': [200, 120, 340, 124, 243, 350]
}
df = pd.DataFrame(data)
print(df) Company Person Sales
0 GOOG Sam 200
1 GOOG Charlie 120
2 MSFT Amy 340
3 MSFT Vanessa 124
4 FB Carl 243
5 FB Sarah 350
This table contains three columns:
- Company – identifies the organization,
- Person – the employee’s name,
- Sales – the amount of sales they achieved.
The goal is to summarize these results — for example, to find each company’s total or average sales.
The GroupBy Operation
The groupby() method in Pandas groups data based on the values of one or more columns. Once grouped, various aggregation methods such as mean(), sum(), count(), or std() (standard deviation) can be applied to each group.
Let’s begin by grouping the dataset by company:
by_company = df.groupby('Company')
print(by_company)<pandas.core.groupby.generic.DataFrameGroupBy object at 0x1036dfe00>
This tells us that the data has been grouped — but we haven’t yet asked for a summary. To see the result of the grouping, we can apply an aggregation function such as mean() to compute the average sales per company.
Calculating the Mean
The mean shows the average value of each numeric column within every group.
by_company.mean(numeric_only=True)| Sales | |
|---|---|
| Company | |
| FB | 296.5 |
| GOOG | 160.0 |
| MSFT | 232.0 |
Here, Pandas calculates the mean sales for each company by taking the average of their employees’ results. We can see that:
- Facebook (FB) achieved the highest average sales,
- Google (GOOG) recorded the lowest,
- Microsoft (MSFT) stands between them.
Other Common Aggregations
Besides the mean, Pandas provides many built-in methods for summarizing grouped data.
Minimum and Maximum
print(by_company.min())
print(by_company.max()) Person Sales
Company
FB Carl 243
GOOG Charlie 120
MSFT Amy 124
Person Sales
Company
FB Sarah 350
GOOG Sam 200
MSFT Vanessa 340
These methods reduce each column independently. The alphabetical minimum Person and minimum Sales need not come from the same row. For MSFT, min() pairs Amy with 124 even though Vanessa supplied the sales value 124. Use a row selector to find the employee associated with an extreme; see the Pandas GroupBy minimum reference.
Standard Deviation
Standard deviation measures how much the values vary from the mean. It describes the spread of these recorded values in their units. A lower value does not by itself establish a stable organization, a temporal trend or more reliable future performance. Pandas uses ddof=1 by default; the NumPy default is ddof=0.
by_company.std(numeric_only=True)| Sales | |
|---|---|
| Company | |
| FB | 75.660426 |
| GOOG | 56.568542 |
| MSFT | 152.735065 |
In this invented two-row-per-group table, the within-group standard deviation is largest for MSFT and smallest for GOOG. Two rows per group provide little evidence about wider employee populations or future periods.
Counting Entries
count() counts non-missing entries separately for each selected column; it is not necessarily the number of rows. size() counts rows. See the Pandas count reference. Here there are no missing entries:
by_company.count()| Person | Sales | |
|---|---|---|
| Company | ||
| FB | 2 | 2 |
| GOOG | 2 | 2 |
| MSFT | 2 | 2 |
Each company has two employees in this dataset. Counting is useful for determining sample sizes or checking data completeness per group.
Descriptive Statistics
The describe() method provides a comprehensive statistical summary for each group — including count, mean, standard deviation, min, quartiles, and max.
by_company.describe()| Sales | ||||||||
|---|---|---|---|---|---|---|---|---|
| count | mean | std | min | 25% | 50% | 75% | max | |
| Company | ||||||||
| FB | 2.0 | 296.5 | 75.660426 | 243.0 | 269.75 | 296.5 | 323.25 | 350.0 |
| GOOG | 2.0 | 160.0 | 56.568542 | 120.0 | 140.00 | 160.0 | 180.00 | 200.0 |
| MSFT | 2.0 | 232.0 | 152.735065 | 124.0 | 178.00 | 232.0 | 286.00 | 340.0 |
This table gives an overview of the central tendency and spread of values for each company. It is an excellent tool for quickly summarizing numerical datasets.
Transposing the Summary
For better readability, the summary can be transposed using .transpose(), which swaps rows and columns.
by_company.describe().transpose()| Company | FB | GOOG | MSFT | |
|---|---|---|---|---|
| Sales | count | 2.000000 | 2.000000 | 2.000000 |
| mean | 296.500000 | 160.000000 | 232.000000 | |
| std | 75.660426 | 56.568542 | 152.735065 | |
| min | 243.000000 | 120.000000 | 124.000000 | |
| 25% | 269.750000 | 140.000000 | 178.000000 | |
| 50% | 296.500000 | 160.000000 | 232.000000 | |
| 75% | 323.250000 | 180.000000 | 286.000000 | |
| max | 350.000000 | 200.000000 | 340.000000 |
Now, each column represents one company, and each row shows a statistical measure. This layout is helpful when comparing groups side by side.
Accessing a Specific Group
It is also possible to access the summary statistics of a specific group, such as Google (GOOG), directly:
by_company.describe().transpose()['GOOG']Sales count 2.000000
mean 160.000000
std 56.568542
min 120.000000
25% 140.000000
50% 160.000000
75% 180.000000
max 200.000000
Name: GOOG, dtype: float64
This isolates Google’s detailed statistics for focused analysis.
Summary
This section showed how groupby() organizes data into meaningful groups and summarizes them using averages, deviations, and descriptive statistics. Grouping transforms raw tables into structured insight — the basis for deeper analysis. The next lesson applies filtering, sorting and pivot summaries. The exploratory-analysis lesson then checks denominators, weighting and aggregation patterns.
Keep the person attached to the selected sale
Predict which employee belongs to the minimum MSFT sale. Then retrieve original rows instead of reducing unrelated columns independently.
minimum_rows = df.loc[df.groupby("Company")["Sales"].idxmin()]
print(minimum_rows[["Company", "Person", "Sales"]].to_string(index=False))
msft = minimum_rows.set_index("Company").loc["MSFT"]
assert msft["Person"] == "Vanessa" and msft["Sales"] == 124Company Person Sales
FB Carl 243
GOOG Charlie 120
MSFT Vanessa 124
The selected MSFT row belongs to Vanessa. idxmin() returns the first matching index when minima tie; decide whether the question requires all tied rows. This selection assumes the original index labels uniquely identify rows. Change one sale to create a tie and explain what information a one-row-per-company answer would omit.