Module 4: Multi-Step Analysis30 min

Reshaping Data

Progress Tracking

Log in to save this lesson and continue from where you left off.

Log in

Long vs Wide Format

Data comes in two shapes:

Python
# LONG format (each observation is a row)
  name     subject  score
  Alice    Math     90
  Alice    Science  85
  Bob      Math     78
  Bob      Science  92

# WIDE format (each subject is a column)
  name     Math  Science
  Alice    90    85
  Bob      78    92

Long format is better for analysis (filtering, grouping). Wide format is better for reports and display. You’ll constantly convert between them.

Creating Pivot Tables

Python
pd.pivot_table(
    orders,
    index="cust_id",
    values="total_order_cost",
    aggfunc=["sum", "count"]
)

Melting Wide to Long

pd.melt() is the reverse of pivoting. It takes columns and unpivots them into rows:

Python
# Wide format: one column per metric
# employee has salary, bonus as separate columns
pd.melt(
    employee,
    id_vars=["first_name", "department"],
    value_vars=["salary", "bonus"],
    var_name="metric",
    value_name="amount"
)

The key parameters:

  • id_vars — columns to keep as-is (identifier columns)
  • value_vars — columns to unpivot into rows
  • var_name — name for the new column holding the old column names
  • value_name — name for the new column holding the values

When to Pivot vs Melt

  • Data has one row per observation, you want a summary table → pivot_table()
  • Data has metrics spread across columns, you want one row per metric → melt()
  • Need to aggregate while reshaping → pivot_table() (has aggfunc)
  • Just rearranging, no aggregation → melt() or pivot() (without _table)
Table: employee
idfirst_namelast_nameagesexemployee_titledepartmentsalarytargetbonusemailcityaddressmanager_id
5MaxGeorge26MSalesSales1300200150Max@company.comCalifornia2638 Richards Avenue1
13KattyBond56FManagerManagement1500000300Katty@company.comArizona1
11RicherdGear57MManagerManagement2500000300Richerd@company.comAlabama1
10JenniferDion34FSalesSales1000200150Jennifer@company.comAlabama13
19GeorgeJoe50MManagerManagement1000000300George@company.comFlorida1003 Wyatt Street1
1
Pivot Department Salary Stats

Create a pivot table showing average and max salary per department. Use pivot table() with aggfunc.

Tables: employee

Titanic Survivors and Non-Survivors

Table: titanic
passengeridsurvivedpclassnamesexagesibspparchticketfarecabinembarked
103Braund, Mr. Owen Harrismale2210A/5 211717.25S
211Cumings, Mrs. John Bradley (Florence Briggs Thayer)female3810PC 1759971.28C85C
313Heikkinen, Miss. Lainafemale2600STON/O2. 31012827.92S
411Futrelle, Mrs. Jacques Heath (Lily May Peel)female351011380353.1C123S
503Allen, Mr. William Henrymale35003734508.05S
2
Titanic Survivors and Non-Survivors
View solution

Make a report showing the number of survivors and non-survivors by passenger class. Classes are categorized based on the `pclass` value as: • First class: `pclass = 1` • Second class: `pclass = 2` • Third class: `pclass = 3` Output the number of survivors and non-survivors by each class.

Tables: titanic

Inspections by Risk Category

An inspection can have several violations in different risk categories. First collapse the data to one row per inspection, keeping only its highest-severity category — rank the categories by severity, sort, and drop duplicates per inspection. Then build the pivot with pd.pivot_table(), add a total column, and sort by it descending.

Table: sf_restaurant_health_violations
business_idbusiness_namebusiness_addressbusiness_citybusiness_statebusiness_postal_codebusiness_latitudebusiness_longitudebusiness_locationbusiness_phone_numberinspection_idinspection_dateinspection_scoreinspection_typeviolation_idviolation_descriptionrisk_category
5800John Chin Elementary School350 Broadway StSan FranciscoCA9413337.8-122.4{'longitude': '-122.403154', 'needs_recoding': False, 'latitude': '37.798358', 'human_address': '{"address":"","city":"","state":"","zip":""}'}5800_201710172017-10-1798Routine - Unscheduled5800_20171017_103149Wiping cloths not clean or properly stored or inadequate sanitizerLow Risk
64236Sutter Pub and Restaurant700 Sutter StSan FranciscoCA9410237.79-122.41{'longitude': '-122.41188', 'needs_recoding': False, 'latitude': '37.78881', 'human_address': '{"address":"","city":"","state":"","zip":""}'}64236_201707252017-07-2588Routine - Unscheduled64236_20170725_103133Foods not protected from contaminationModerate Risk
1991SRI THAI CUISINE4621 LINCOLN WaySan FranciscoCA9412237.76-122.51{'longitude': '-122.507779', 'needs_recoding': False, 'latitude': '37.764073', 'human_address': '{"address":"","city":"","state":"","zip":""}'}1991_201711292017-11-2986Routine - Unscheduled1991_20171129_103139Improper food storageLow Risk
3816Washington Bakery & Restaurant733 Washington StSan FranciscoCA9410837.8-122.41{'longitude': '-122.405845', 'needs_recoding': False, 'latitude': '37.795174', 'human_address': '{"address":"","city":"","state":"","zip":""}'}3816_201607282016-07-2867Routine - Unscheduled3816_20160728_103108Contaminated or adulterated foodHigh Risk
39119Brothers Restaurant4128 GEARY BlvdSan FranciscoCA9411837.78-122.46{'longitude': '-122.463762', 'needs_recoding': False, 'latitude': '37.781148', 'human_address': '{"address":"","city":"","state":"","zip":""}'}39119_201607182016-07-1879Routine - Unscheduled39119_20160718_103133Foods not protected from contaminationModerate Risk
3
Find the number of inspections for each risk category by inspection type
View solution

A city health department wants to see how inspection outcomes break down by risk level for each type of inspection it runs. Each inspection can have multiple violation records, and each record carries its own risk category (or none). For each inspection, take the highest-severity risk category among its records — ranked High Risk > Moderate Risk > Low Risk > no risk category — and count that inspection once, under its inspection type, in the corresponding risk category. If none of an inspection's records have a risk category, count that inspection under its own no-risk-category bucket. Output the inspection type, the count of inspections in each risk category (no risk category, low risk, moderate risk, high risk) as separate columns, and the total number of inspections for that inspection type, sorted by the total number of inspections per inspection type in descending order.

Tables: sf_restaurant_health_violations

Key Takeaways

  • pd.pivot_table() converts long → wide with aggregation.
  • pd.melt() converts wide → long (unpivoting).
  • pd.crosstab() is a shortcut for frequency pivot tables.
  • Long format for analysis; wide format for reports.
  • index, columns, values, aggfunc are the four pivot_table parameters to know.

What’s Next

You’ve completed Module 4. You can now break complex analyses into clean steps, apply custom logic, and reshape data for any format. Module 5 covers dates, strings, and conditional logic — the functions that handle messy real-world data.