Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Pivot tables

  • Pivoting of data is the process of aggregating data across two or more categorical variables.

  • Closely related are:

    • Group by, where the number of categorical variables is flexible.

    • Cross tabulation, which is basically pivoting, but with different defaults and parameters.

  • In Pandas, pivot tables are based on

    • values: what to aggregate

    • index and column: layout of result, and

    • aggfunc: how to aggregate.

Sales data from Power BI examples

# Read SalesData.xlsx
import pandas as pd
df = pd.read_excel('../../data/SalesData.xlsx', skiprows=4, header=0)
print(df.shape)
df.head()
(260096, 10)
Loading...
# Add a column giving the total price
df['TotalPrice'] = df['Quantity'] * df['UnitPrice']
df.head()
Loading...
# Use Pandas groupby() to group by Channel and sum the TotalPrice
df1 = df.groupby('Channel')['TotalPrice'].sum()
df1
Channel Distributor 6098515.78 Online 3113650.20 Retail 8697066.51 Name: TotalPrice, dtype: float64
# Use Pandas pivot_table() to group by Channel and Manager and sum the TotalPrice
df2 = df.pivot_table(index='Channel', columns='Manager', values='TotalPrice', aggfunc='sum')
df2
Loading...
# Exchange aggfunc='sum' for aggfunc='count' to confirm that the dataset is not very suitable for this type of analysis
df2 = df.pivot_table(index='Channel', columns='Manager', values='TotalPrice', aggfunc='count')
df2
Loading...

Olympic athletes and results

# Read the athlete_events.csv file
import pandas as pd
athletes = pd.read_csv('../../data/athlete_events.csv')
print(athletes.shape)
athletes.head()
(271116, 15)
Loading...
# Check average weight of athletes
athlete_weight = athletes['Weight'].mean()

# Print athlete_weight with 2 decimal places and kg suffix
print('Average weight of athletes: {:.2f} kg'.format(athlete_weight))
Average weight of athletes: 70.70 kg
# Check average weight of athletes for each year
athlete_weight_by_year = athletes.groupby('Year')['Weight'].mean()

# Plot athlete_weight_by_year using kind='bar'
import matplotlib.pyplot as plt
athlete_weight_by_year.plot(kind='bar', figsize=(12, 4))
# Add horizontal grid lines to the plot
plt.grid(axis='y') 
plt.show()
<Figure size 1200x400 with 1 Axes>
# Check average weight of athletes for each sport and drop NA values
athlete_weight_by_sport = athletes.groupby('Sport')['Weight'].mean().dropna()
athlete_weight_by_sport
Sport Alpine Skiing 72.068110 Archery 70.011135 Art Competitions 75.290909 Athletics 69.249287 Badminton 68.171439 Baseball 85.707792 Basketball 85.777053 Beach Volleyball 79.089219 Biathlon 66.631419 Bobsleigh 89.250678 Boxing 65.249890 Canoeing 76.492615 Cross Country Skiing 65.877670 Curling 72.131707 Cycling 70.067944 Diving 60.572741 Equestrianism 67.803975 Fencing 71.387538 Figure Skating 59.543651 Football 70.446834 Freestyle Skiing 67.026835 Golf 71.194444 Gymnastics 56.916553 Handball 81.497151 Hockey 69.169909 Ice Hockey 80.810364 Judo 78.759867 Lacrosse 76.714286 Luge 77.264151 Modern Pentathlon 70.279540 Motorboating 77.000000 Nordic Combined 66.909560 Rhythmic Gymnastics 48.760976 Rowing 80.035863 Rugby 77.533333 Rugby Sevens 78.939799 Sailing 75.975154 Shooting 74.027877 Short Track Speed Skating 64.310484 Skeleton 74.166667 Ski Jumping 65.079014 Snowboarding 69.549189 Softball 67.471655 Speed Skating 70.026352 Swimming 70.588492 Synchronized Swimming 55.863529 Table Tennis 64.956449 Taekwondo 68.007475 Tennis 70.802291 Trampolining 59.322148 Triathlon 61.817490 Tug-Of-War 95.615385 Volleyball 78.900214 Water Polo 84.566446 Weightlifting 78.726663 Wrestling 75.495570 Name: Weight, dtype: float64
# Make a pivot table of athlete weights by sport and year
athlete_weight_sport_year = athletes.pivot_table(index='Sport', columns='Year', values='Weight', aggfunc='mean')
athlete_weight_sport_year
Loading...
# Extract the rows for Season == Summer and Year >= 2000
summer = athletes.loc[athletes['Season'] == 'Summer',:]
summer2000 = summer.loc[summer['Year'] >= 2000,:]
summer2000.head()
Loading...
# Repeat the pivoting step on the summer2000 data
awsy = summer2000.pivot_table(index='Sport', columns='Year', values='Weight', aggfunc='mean')

# Remove rows that only contain NaN values
awsy = awsy.dropna(how='all')
awsy.round(1)
Loading...

Aggregate on multiple functions and/or values

# Repeat, but limit to summers of 2000-2016
awsy = summer2000.pivot_table(index='Sport', columns='Year', values=['Weight','Height'], aggfunc=['mean','max'])

# Remove rows that only contain NaN values
awsy = awsy.dropna(how='all')
awsy.round(1).head()
Loading...
# Repeat, but add margins
awsy = summer2000.pivot_table(index='Sport', columns='Year', values=['Weight','Height'], aggfunc='max', margins=True)

# Remove rows that only contain NaN values
awsy = awsy.dropna(how='all')
awsy.round(1).head()
Loading...

Stack and unstack

  • These operations switch between the groupby-format and the pivot_table-format.

# Groupby with two columns
athlete_weight_by_sport_groupby = athletes.groupby('Sport')[['Weight','Height']].mean()
athlete_weight_by_sport_groupby
Loading...
# Unstack the result
athlete_weight_by_sport_groupby.unstack()
Sport Weight Aeronautics NaN Alpine Skiing 72.068110 Alpinism NaN Archery 70.011135 Art Competitions 75.290909 ... Height Tug-Of-War 182.480000 Volleyball 186.994822 Water Polo 184.834648 Weightlifting 167.824801 Wrestling 172.358586 Length: 132, dtype: float64

Exercise

  • Extract winter olympics.

  • Make yearly medal statistics for all countries using pivoting to count medals.

    • Remove all countries that have no winter olympic medals.

  • Extract the top 10 countries that have the most winter olympic medals in sum.

  • Use the noc_regions.csv to exchange the NOC codes with region names.

  • Plot the resulting 10 curves as proportions of medals per year.

    • Place the legend outside the plot