Skip to content

Chapter 6 of 7

GroupBy, Combining, and Reshaping

The Split-Apply-Combine paradigm is encoded in df.groupby('key'). After grouping, aggregations like df.groupby('key').mean(numeric_only=True) or .sum() collapse each group into a single row; grouping by multiple columns, df.groupby(['k1', 'k2']).sum(), produces a MultiIndex on the result. To iterate over the groups, write for name, group in df.groupby('key'): .... When several aggregations are needed per group, df.groupby('key').agg({'a': 'mean', 'b': ['min', 'max']}) returns all of them in one call, and named aggregation df.groupby('key').agg(mean_a=('a', 'mean'), max_b=('b', 'max')) assigns clean column names to each result.

Two advanced groupby methods preserve the shape of the original frame. df.groupby('key')['v'].transform('mean') broadcasts the per-group mean back to every row, aligning the result with the input index — useful for adding group-normalized columns. df.groupby('key').filter(lambda g: g['v'].mean() > 0) instead drops entire groups that fail a boolean condition. Combining frames is done either by stacking or joining. pd.concat([df1, df2]) stacks rows vertically, with ignore_index=True renumbering rows from zero; passing axis=1 instead glues frames side-by-side as new columns. For relational joins, pd.merge(df1, df2, on='key') works like a SQL join; the how parameter takes 'left', 'right', 'inner' (the default), or 'outer'. When the join keys have different names, use left_on='id1', right_on='id2'. df1.join(df2) is a convenience that defaults to merging on the index, while merge is the more general form.

Reshaping rotates the layout of a frame. df.pivot(index='id', columns='var', values='val') goes from long to wide but requires unique index/column pairs; when duplicates exist, df.pivot_table(index='id', columns='var', values='val', aggfunc='sum') aggregates them (defaulting to mean). The reverse, df.melt(id_vars=['id'], var_name='var', value_name='val'), turns wide columns into long rows. stack() compresses a level of the column index into the row index, yielding a Series with a MultiIndex, and unstack() does the opposite. Cross-tabulations are produced with pd.crosstab(df['a'], df['b']), with optional values= and aggfunc= to summarize another column. Finally, continuous values can be binned into discrete buckets with pd.cut(df['v'], bins=[0, 10, 20, 100]), which returns a categorical Series, or with pd.qcut(df['v'], q=4) for equal-frequency quartiles.

All chapters
  1. 1Foundations and I/O
  2. 2Inspecting, Selecting, and Filtering
  3. 3Modifying and Sorting
  4. 4Aggregation and Apply
  5. 5Missing Data and Duplicates
  6. 6GroupBy, Combining, and Reshaping
  7. 7Time Series, Strings, and Categorical Types

Drill it

Reading is not remembering. These come from the Pandas Dataframe Operations deck:

Q

What is a Pandas DataFrame?

A two-dimensional, size-mutable, tabular data structure with labeled axes (rows and columns), similar to a spreadsheet or SQL table.

Q

What library provides DataFrame in Python?

Pandas (import pandas as pd).

Q

How do you create a DataFrame from a dictionary of lists?

pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})

Q

How do you create a DataFrame from a list of dictionaries?

pd.DataFrame([{'a': 1}, {'a': 2, 'b': 3}]); missing keys become NaN.