Free GuideIntermediate
50 min read
#Reshaping#Pivot Tables#Melt#Stack Unstack#Cross-tabulation#Tidy Data

05. Data Transformation, Pivoting & Reshaping

Mastering tabular geometry: wide vs long formats, pivot tables, melt un-pivoting, hierarchical multi-index stacking/unstacking, cross-tabulations, and Tidy Data design principles.

Notebook 5: Data Transformation and Reshaping

Module: Data Analytics Level: Intermediate Prerequisites: Notebook 4 (Data Cleaning and Preparation) Estimated Time: 3–4 hours


Table of Contents#

  1. Why Shape Matters
  2. Wide vs. Long Format
  3. Pivot Tables
  4. melt: Wide to Long
  5. pivot vs. pivot_table
  6. stack and unstack
  7. Working With MultiIndex Results
  8. Cross-Tabulation
  9. Choosing the Right Reshape for the Task
  10. Hands-On: Reshaping a Dataset for Three Different Purposes
  11. Case Studies
  12. Common Misconceptions and Pitfalls
  13. Glossary
  14. Exercises
  15. Quiz
  16. Summary
  17. Further Reading

Learning Objectives#

By the end of this notebook, you will be able to:

  • Distinguish wide and long data formats and identify which one a given tool or analysis requires
  • Build pivot tables to summarize data across two or more dimensions
  • Convert wide data to long format using melt, and long data to wide format using pivot
  • Explain the difference between pivot and pivot_table, and when each is appropriate
  • Use stack and unstack to move data between columns and a MultiIndex
  • Navigate and flatten MultiIndex results produced by grouping and reshaping operations
  • Build cross-tabulations to summarize the relationship between two categorical variables
  • Choose the correct reshaping operation for a given downstream task (visualization, modeling, reporting)

1. Why Shape Matters#

The exact same information can be represented in a table in multiple valid "shapes," and different tools and analyses expect different shapes. A groupby aggregation (Notebook 3, Section 3) naturally produces one shape; a plotting library (Notebook 8) often expects another; a pivoted summary report for a spreadsheet-literate stakeholder expects yet another. This notebook is about converting fluently between these shapes without losing or duplicating information, a skill that becomes essential the moment an analysis needs to serve more than one downstream purpose.


2. Wide vs. Long Format#

2.1 Wide Format#

In wide format, each variable being measured gets its own column, and each row typically represents one observational unit (e.g., one region) across multiple conditions (e.g., quarters):

code
region Q1_revenue Q2_revenue Q3_revenue North 125000 118000 132000 South 98000 76000 85000 East 143000 151000 160000

Wide format is often intuitive for humans to read, especially in spreadsheet-style reports, since related values sit next to each other in a single row.

2.2 Long Format (Also Called Tidy Format)#

In long format, each row represents a single observation, with separate columns identifying which variable and which condition that observation belongs to:

code
region quarter revenue North Q1 125000 North Q2 118000 North Q3 132000 South Q1 98000 South Q2 76000 South Q3 85000 East Q1 143000 East Q2 151000 East Q3 160000

This structure is often called tidy data, following Hadley Wickham's widely cited formulation: every variable forms a column, every observation forms a row, and every type of observational unit forms a table.

2.3 Why Long Format Is Usually Preferred for Analysis#

Most Pandas operations covered so far in this course, groupby (Notebook 3), boolean filtering (Notebook 2), and most plotting libraries (Notebook 8), are designed to operate on long-format data, since a single column like quarter can be grouped, filtered, or mapped to a plot's color/facet dimension directly, whereas the same information spread across multiple wide columns (Q1_revenue, Q2_revenue, Q3_revenue) cannot be filtered or grouped on "quarter" as a single conceptual variable without first reshaping.

2.4 Why Wide Format Still Matters#

Wide format remains the natural and often required shape for: final reports intended for human stakeholders (a spreadsheet-style summary table), certain statistical and correlation operations that expect one column per variable (Notebook 9), and some visualization types (like heatmaps, Notebook 8) that expect a matrix-shaped input.

2.5 The Core Skill of This Notebook#

Because different stages of a single analysis often require different shapes, being able to convert fluently between wide and long format, in both directions, is one of the most frequently used practical skills in real analytics work, arguably used even more often day-to-day than many of the statistical techniques covered later in this course.


3. Pivot Tables#

3.1 The Purpose of a Pivot Table#

A pivot table summarizes long-format data into a wide-format table, aggregating values where multiple observations map to the same cell. It directly generalizes the groupby aggregation from Notebook 3, Section 4.1, but arranges one grouping variable across columns instead of stacking every group vertically.

3.2 Basic Syntax#

Python
import pandas as pd df = pd.DataFrame({ "region": ["North", "North", "South", "South", "East", "East"], "quarter": ["Q1", "Q2", "Q1", "Q2", "Q1", "Q2"], "revenue": [125000, 118000, 98000, 76000, 143000, 151000] }) pivot = df.pivot_table( values="revenue", index="region", columns="quarter", aggfunc="mean" ) print(pivot)

Expected output:

code
quarter Q1 Q2 region East 143000 151000 North 125000 118000 South 98000 76000

3.3 Multiple Aggregations and Multiple Values#

Python
pivot_multi = df.pivot_table( values="revenue", index="region", columns="quarter", aggfunc=["mean", "sum", "count"] ) print(pivot_multi)

This produces a result with a MultiIndex on the columns (one level for the aggregation function, one level for the quarter), a structure covered in depth in Section 7.

3.4 Handling Missing Combinations#

If a region has no data for a given quarter, pivot_table inserts NaN by default, which can be controlled explicitly:

Python
pivot_filled = df.pivot_table( values="revenue", index="region", columns="quarter", aggfunc="mean", fill_value=0 )

3.5 Adding Row and Column Totals#

Python
pivot_with_totals = df.pivot_table( values="revenue", index="region", columns="quarter", aggfunc="sum", margins=True, margins_name="Total" ) print(pivot_with_totals)

margins=True adds a final row and column containing the overall aggregate across all groups, directly replicating the "grand total" row/column common in spreadsheet-style pivot tables.


4. melt: Wide to Long#

4.1 The Inverse Operation#

melt performs the reverse transformation of a pivot table: converting wide-format columns into two long-format columns, one holding the former column names (by default called variable) and one holding the corresponding values (by default called value).

Python
wide_df = pd.DataFrame({ "region": ["North", "South", "East"], "Q1_revenue": [125000, 98000, 143000], "Q2_revenue": [118000, 76000, 151000] }) long_df = wide_df.melt( id_vars="region", value_vars=["Q1_revenue", "Q2_revenue"], var_name="quarter", value_name="revenue" ) print(long_df)

Expected output:

code
region quarter revenue 0 North Q1_revenue 125000 1 South Q1_revenue 98000 2 East Q1_revenue 143000 3 North Q2_revenue 118000 4 South Q2_revenue 76000 5 East Q2_revenue 151000

id_vars specifies which columns should remain as identifying columns (not melted), while value_vars specifies which columns should be collapsed into the long variable/value pair. If value_vars is omitted, every column not listed in id_vars is melted automatically.

4.2 Why melt Is Necessary Before Certain Operations#

Referring back to Section 2.3, a dataset arriving in wide format (as is common from spreadsheet exports or certain reporting systems) often cannot be directly grouped or filtered by the variable it actually represents (e.g., "quarter") until it is melted into long format, making melt frequently the very first step in an analysis pipeline for spreadsheet-sourced data.


5. pivot vs. pivot_table#

5.1 The Key Difference#

pivot and pivot_table look similar but serve different purposes:

MethodAggregationRequirement
.pivot()None — requires exactly one value per index/column combinationRaises an error if duplicate index/column combinations exist
.pivot_table()Applies an aggregation function (default mean)Handles duplicate index/column combinations gracefully by aggregating them

5.2 When pivot Fails#

Python
df_with_dupes = pd.DataFrame({ "region": ["North", "North", "South"], "quarter": ["Q1", "Q1", "Q1"], # North appears twice for Q1 "revenue": [125000, 130000, 98000] }) # This raises an error: duplicate entries for (North, Q1) # result = df_with_dupes.pivot(index="region", columns="quarter", values="revenue")

Expected behavior:

ValueError: Index contains duplicate entries, cannot reshape

pivot_table, by contrast, would handle this gracefully by averaging (or summing, or applying whatever aggfunc is specified) the two North/Q1 values automatically, exactly as demonstrated in Section 3.2.

5.3 When to Use Which#

Use .pivot() when you are confident the data has exactly one value per index/column combination already (for example, data that has already been aggregated by an earlier groupby step) and want a strict, fast reshape with an explicit error if that assumption is violated. Use .pivot_table() whenever aggregation might be needed, or as the generally safer default when working with raw, unaggregated data, since it will not silently fail on duplicate combinations and instead handles them in a well-defined way.


6. stack and unstack#

6.1 The Relationship to MultiIndex#

stack and unstack move data between a DataFrame's columns and a hierarchical row index (MultiIndex), providing a more general, lower-level reshaping mechanism than pivot/melt, which are often implemented internally using these operations.

6.2 unstack: Moving an Index Level to Columns#

Python
grouped = df.groupby(["region", "quarter"])["revenue"].sum() print(grouped) print("\nAfter unstack:") print(grouped.unstack())

Expected output:

code
region quarter East Q1 143000 Q2 151000 North Q1 125000 Q2 118000 South Q1 98000 Q2 76000 Name: revenue, dtype: int64 After unstack: quarter Q1 Q2 region East 143000 151000 North 125000 118000 South 98000 76000

This is exactly the MultiIndex-to-wide-table conversion previewed in Notebook 3, Section 3.3, and produces an identical result to the pivot_table call in Section 3.2, since a groupby followed by unstack and a pivot_table are, for simple cases, two different routes to the same destination.

6.3 stack: Moving Columns to an Index Level#

Python
wide = grouped.unstack() restacked = wide.stack() print(restacked)

stack performs the exact inverse of unstack, moving the column labels back into an inner level of a row MultiIndex, collapsing the wide table back to the long, hierarchically-indexed Series shown in Section 6.2's first output.

6.4 unstack With Multiple Index Levels#

Python
grouped_multi = df.groupby(["region", "quarter"])["revenue"].agg(["sum", "mean"]) # unstack the outermost level (region) by default is level=-1 (innermost); # specify explicitly which level to unstack print(grouped_multi.unstack(level="quarter"))

Specifying level explicitly (by name or position) is important once more than two index levels are involved, since the default behavior (unstacking the innermost level) may not match the intended reshape.


7. Working With MultiIndex Results#

7.1 Why MultiIndex Results Are Common#

Section 3.3's multi-aggregation pivot table and Section 6.4's multi-level groupby both produced results with a MultiIndex, a hierarchical index with more than one level. While powerful, MultiIndex results can be awkward to work with directly, especially when passing data to tools (like some plotting functions) that expect a simple, flat structure.

7.2 Flattening a MultiIndex#

Python
pivot_multi = df.pivot_table( values="revenue", index="region", columns="quarter", aggfunc=["mean", "sum"] ) # Flatten the column MultiIndex into single, readable string labels pivot_multi.columns = ["_".join(col).strip() for col in pivot_multi.columns.values] print(pivot_multi)

Expected output:

code
mean_Q1 mean_Q2 sum_Q1 sum_Q2 region East 143000 151000 143000 151000 North 125000 118000 125000 118000 South 98000 76000 98000 76000

7.3 Resetting a MultiIndex to Regular Columns#

Python
grouped = df.groupby(["region", "quarter"])["revenue"].sum() flat = grouped.reset_index() print(flat)

.reset_index() converts every level of a MultiIndex (row-based, in this case) back into regular columns, restoring the long format shown in Section 6.2's first output as an ordinary DataFrame rather than a hierarchically-indexed Series, which is often the most convenient shape to hand off to a plotting function or to write out to a file.


8. Cross-Tabulation#

8.1 The Purpose of pd.crosstab#

pd.crosstab() is a specialized tool for summarizing the relationship between two (or more) categorical variables, most commonly producing a frequency count table, directly relevant to the correlation and relationship analysis covered in Notebook 9:

Python
df = pd.DataFrame({ "region": ["North", "North", "South", "South", "East", "East", "North"], "customer_tier": ["Gold", "Silver", "Gold", "Gold", "Silver", "Gold", "Gold"] }) crosstab = pd.crosstab(df["region"], df["customer_tier"]) print(crosstab)

Expected output:

code
customer_tier Gold Silver region East 1 1 North 3 1 South 2 0

8.2 Normalizing a Cross-Tabulation#

Python
crosstab_pct = pd.crosstab(df["region"], df["customer_tier"], normalize="index") print(crosstab_pct.round(2))

Expected output:

code
customer_tier Gold Silver region East 0.50 0.50 North 0.75 0.25 South 1.00 0.00

normalize="index" converts each row to proportions summing to 1, making it easy to compare the relative distribution of customer tiers within each region regardless of each region's total count, which raw frequency counts alone would not directly reveal.

8.3 crosstab vs. pivot_table#

pd.crosstab() is essentially a convenience wrapper specialized for the common case of counting frequency combinations between categorical variables, while pivot_table is the more general tool for aggregating a numeric value across categorical dimensions; pd.crosstab(a, b) is roughly equivalent to pd.pivot_table(data, index=a, columns=b, aggfunc="size"), but with a more convenient and readable syntax for that specific, very common use case.


9. Choosing the Right Reshape for the Task#

TaskReshaping Tool
Summarize a numeric value across two categorical dimensions, human-readable reportpivot_table
Convert spreadsheet-style wide columns into analyzable long formatmelt
Reshape strictly (no aggregation needed), fast, with duplicate-detection safetypivot
Move between a MultiIndex and columns, especially after a multi-level groupbystack / unstack
Count frequency of combinations between two categorical variablescrosstab
Prepare data for most plotting functions or further groupby/filteringEnsure long format (melt if currently wide)
Prepare a correlation matrix or certain statistical testsEnsure wide format (one column per variable, Notebook 9)

10. Hands-On: Reshaping a Dataset for Three Different Purposes#

This example starts from a single long-format dataset and reshapes it three different ways for three different downstream needs: a stakeholder report, a plotting-ready structure, and a correlation-ready structure.

Python
import pandas as pd # Starting long-format dataset (the "tidy" source of truth) sales_long = pd.DataFrame({ "store": ["A", "A", "A", "B", "B", "B", "C", "C", "C"], "month": ["Jan", "Feb", "Mar", "Jan", "Feb", "Mar", "Jan", "Feb", "Mar"], "revenue": [12000, 13500, 11800, 9800, 10200, 9500, 15000, 15600, 16200], "foot_traffic": [850, 900, 820, 700, 720, 690, 1100, 1150, 1180] }) # Purpose 1: a wide, human-readable stakeholder report (one row per store, # one column per month) using pivot_table report = sales_long.pivot_table(values="revenue", index="store", columns="month", aggfunc="sum") report = report[["Jan", "Feb", "Mar"]] # reorder columns chronologically print("Stakeholder report (wide):") print(report) # Purpose 2: confirm the data is already in the long format most plotting # functions expect (store, month, revenue as separate columns) - no # reshaping needed here, since sales_long is already tidy print("\nAlready plot-ready (long):") print(sales_long.head()) # Purpose 3: a wide, one-column-per-variable structure for correlation # analysis (Notebook 9), requiring revenue and foot_traffic as separate # columns, which sales_long already provides directly correlation_input = sales_long[["revenue", "foot_traffic"]] print("\nCorrelation-ready structure:") print(correlation_input.corr().round(2))

Expected output (abbreviated):

code
Stakeholder report (wide): month Jan Feb Mar store A 12000 13500 11800 B 9800 10200 9500 C 15000 15600 16200 Correlation-ready structure: revenue foot_traffic revenue 1.00 0.99 foot_traffic 0.99 1.00

Interpretation: This highlights a key practical lesson from Section 2.5: a single well-structured long-format dataset can typically be reshaped, or in some cases used directly, to serve reporting, plotting, and statistical needs, but each downstream purpose has a specific shape it expects, and part of the analyst's job is recognizing which shape a given task calls for.


11. Case Studies#

11.1 Case Study: A Dashboard Tool That Could Not Read a Wide Export#

A team receives a monthly sales export from an external finance system in wide format (one column per month, per Section 2.1), intending to load it directly into a business intelligence dashboard tool that expects long-format, tidy input (one row per store-month combination) in order to build a time-series chart with month as a filterable dimension.

Diagnosis: The wide format, while human-readable, does not expose "month" as a single filterable or groupable field; the dashboard tool has no way to treat Jan_revenue, Feb_revenue, and Mar_revenue as three values of one underlying "month" variable without an explicit reshape.

Lesson: Applying melt (Section 4) to convert the wide export into a long store, month, revenue structure immediately resolved the compatibility issue, directly illustrating the practical necessity described in Section 4.2: many tools, not just Pandas's own groupby, specifically require long/tidy input to expose variables as filterable or groupable dimensions.

11.2 Case Study: A Pivot Table That Silently Averaged When Summing Was Intended#

An analyst builds a pivot table summarizing total revenue by region and quarter using pivot_table without specifying an explicit aggfunc, and later discovers the reported figures are roughly one-tenth of the true total revenue, since several individual transactions map to each region/quarter combination.

Diagnosis: pivot_table's default aggregation function is mean (Section 3.2), not sum; without an explicit aggfunc="sum", the pivot table silently averaged the individual transaction amounts within each region/quarter cell rather than summing them, producing numbers that were plausible-looking (still normal-sized revenue figures) but represented a completely different, incorrect quantity.

Lesson: Always specify aggfunc explicitly rather than relying on the default, and, per the general validation discipline from Notebook 4, Section 8, sanity-check a pivot table's totals against an independently computed overall sum to catch exactly this class of silent aggregation mismatch.

11.3 Case Study: A Correlation Analysis Blocked by Wrong Shape#

An analyst wants to compute the correlation (Notebook 9) between "revenue" and "foot_traffic" across stores and months but has the data in a long format where "revenue" and "foot_traffic" are both collapsed into a single generic value column with a metric column distinguishing which is which (a shape produced by an earlier melt step used for a different, plotting-related purpose).

Diagnosis: Correlation, as covered in Notebook 9, requires each variable to occupy its own column so that pairs of values can be compared row-by-row; with both metrics stacked into one value column, there is no direct way to compute a meaningful correlation without first reshaping back to a structure with separate revenue and foot_traffic columns.

Lesson: Applying pivot (or unstack, after first setting an appropriate index) to move metric's distinct values back into separate columns restored the one-column-per-variable structure needed for correlation analysis, directly illustrating the wide-format requirement noted in Section 2.4 and Section 9's task-to-tool mapping table; a dataset melted for one purpose (plotting) is not automatically suited to a different downstream purpose (correlation) without reshaping back.


12. Common Misconceptions and Pitfalls#

MisconceptionReality
"Wide format is always more useful since it's easier for a human to read."Wide format is often best for final human-facing reports, but most programmatic operations (grouping, filtering, plotting, correlation across observations) require long/tidy format, per Section 2.3.
"pivot and pivot_table are interchangeable and it doesn't matter which one you use."pivot raises an error on duplicate index/column combinations, while pivot_table aggregates them; using the wrong one either fails unexpectedly or silently changes the meaning of the result, per Section 5.2.
"pivot_table's default behavior sums values within each group."The default aggfunc is mean, not sum, per Section 3.2 and the case study in Section 11.2; always specify aggfunc explicitly for clarity and correctness.
"Once data is melted for one purpose, it's ready for any subsequent analysis."A shape suited to one purpose (e.g., plotting) may not suit another (e.g., correlation, which needs a wide, one-column-per-variable structure), per Section 11.3; reshape again as needed for each new purpose.
"MultiIndex results from groupby or pivot_table can be used directly everywhere a normal DataFrame can."Many tools expect a flat structure; .reset_index() (Section 7.3) or explicit column flattening (Section 7.2) is often needed before passing MultiIndex results to plotting functions or exporting to a file.
"crosstab and pivot_table do fundamentally different things."crosstab is essentially a convenient special case of pivot_table focused on frequency counts between categorical variables, per Section 8.3.

13. Glossary#

TermDefinition
Wide formatA data layout where each variable or condition has its own column
Long format / tidy dataA data layout where each row is a single observation, with separate columns identifying the variable and its value
Pivot tableA wide-format summary table produced by aggregating long-format data across two or more dimensions
meltThe operation converting wide-format columns into long-format variable/value pairs
pivotA strict reshape from long to wide format that raises an error on duplicate index/column combinations
pivot_tableA reshape from long to wide format that aggregates duplicate index/column combinations using a specified function
stack / unstackOperations moving data between DataFrame columns and an inner level of a MultiIndex
MultiIndexA hierarchical index with more than one level, often produced by multi-key grouping or pivoting
Cross-tabulation (crosstab)A frequency count table summarizing the relationship between two or more categorical variables

14. Exercises#

Exercise 1 (Conceptual)#

A colleague has a dataset with columns product, jan_sales, feb_sales, mar_sales and wants to create a line chart showing sales trends over time, with one line per product, using a plotting library that expects long-format input. Explain what reshaping step is needed and why.

<details> <summary>Solution</summary>

The dataset needs to be melted (Section 4) from its current wide format into long format, producing columns like product, month, and sales. This is necessary because a plotting library that draws one line per product and maps a "month" dimension to the x-axis needs "month" to exist as a single column whose values (Jan, Feb, Mar) can be mapped directly to positions on an axis; with the data in wide format, "month" is implicitly represented as three separate column names rather than as values in a single column, which most plotting APIs cannot consume directly, exactly as described in the dashboard case study in Section 11.1.

</details>

Exercise 2 (Conceptual)#

Explain why pivot() would raise an error on a dataset where each (store, date) combination appears more than once, while pivot_table() would not, and what this implies about verifying data before choosing between them.

<details> <summary>Solution</summary>

pivot() is a strict reshape that assumes exactly one value exists for each index/column combination and has no mechanism to combine multiple values if more than one exists for the same combination, so it raises an explicit ValueError rather than guessing how to resolve the conflict (Section 5.2). pivot_table(), by contrast, is explicitly designed to aggregate values when duplicates exist, using whichever aggfunc is specified (default mean), so it handles the same duplicate scenario without error, but produces a summarized value rather than an error. This implies that before choosing pivot(), it is worth verifying (for example, by checking for duplicates on the intended index/column combination, similar to Notebook 4, Section 3) that the data genuinely has only one value per combination; if that is not guaranteed, pivot_table() with an explicit, deliberately chosen aggfunc is the safer choice, as recommended in Section 5.3.

</details>

Exercise 3 (Coding)#

Given a long-format DataFrame df with columns department, year, and budget, write code to produce a wide-format table with departments as rows, years as columns, and total budget as values, including a "Total" row and column summarizing overall totals.

<details> <summary>Solution</summary>
Python
import pandas as pd df = pd.DataFrame({ "department": ["Sales", "Sales", "Engineering", "Engineering"], "year": [2023, 2024, 2023, 2024], "budget": [500000, 550000, 900000, 950000] }) result = df.pivot_table( values="budget", index="department", columns="year", aggfunc="sum", margins=True, margins_name="Total" ) print(result)

This directly applies the margins=True technique from Section 3.5 to add both a "Total" row (summing across departments for each year) and a "Total" column (summing across years for each department), along with a grand total in the bottom-right cell, matching the common spreadsheet-style pivot table report format.

</details>

Exercise 4 (Applied Thinking)#

You have a long-format dataset tracking daily website metrics (date, metric_name, metric_value, where metric_name includes values like "page_views," "signups," and "bounce_rate"). Describe the reshaping step needed to compute the correlation between "page_views" and "signups" across days, and explain why the current long format is not directly usable for that calculation.

<details> <summary>Solution</summary>

The current long format stacks every metric into a single metric_value column distinguished only by the metric_name label, exactly the structure described in the case study in Section 11.3. To compute a correlation between "page_views" and "signups," each needs to occupy its own column so that, for each date, the corresponding page_views value and signups value can be compared directly as a pair; this requires a pivot (or pivot_table, to be safe against any unexpected duplicate date/metric_name combinations) with index="date", columns="metric_name", and values="metric_value", producing a wide table with separate page_views and signups columns (among others) that .corr() (Notebook 9) can then operate on directly.

</details>

15. Quiz#

Q1. In tidy (long) format, what does each row represent? a) A full observational unit with all its variables side by side b) A single observation, with separate columns identifying the variable and its value c) A summary statistic across multiple observations d) A single column of a wide table

Q2. What is the default aggregation function used by pivot_table() if none is specified? a) sum b) count c) mean d) max

Q3. What happens when .pivot() (not pivot_table) is used on data with duplicate index/column combinations? a) It automatically averages the duplicates b) It automatically sums the duplicates c) It raises an error d) It silently keeps only the first occurrence

Q4. Which operation converts wide-format columns into a long-format variable/value pair? a) pivot b) melt c) crosstab d) stack only, never melt

Q5. True or False: pd.crosstab() and pd.pivot_table() are entirely unrelated tools with no conceptual overlap.

<details> <summary>Answers</summary>
  1. b) A single observation, with separate columns identifying the variable and its value
  2. c) mean
  3. c) It raises an error
  4. b) melt
  5. False — crosstab is essentially a convenient special case of pivot_table specialized for frequency counts between categorical variables, per Section 8.3
</details>

16. Summary#

  • Wide format places each variable in its own column, while long (tidy) format represents each observation as a single row with explicit variable and value columns; most programmatic operations require long format, while human-facing reports often favor wide format.
  • pivot_table summarizes long data into wide, aggregated form, with a default mean aggregation that must be overridden explicitly (commonly to sum) to avoid the silent misinterpretation shown in this notebook's case studies.
  • melt performs the inverse operation, converting wide columns into long variable/value pairs, and is frequently the first step needed when working with spreadsheet-style exports.
  • pivot is a strict, non-aggregating reshape that raises an error on duplicate index/column combinations, while pivot_table handles duplicates by aggregating them.
  • stack and unstack provide a more general mechanism for moving data between columns and a MultiIndex, and are what pivot/unstack-based groupby results are built from internally.
  • crosstab provides a convenient, specialized interface for frequency counts between categorical variables, closely related to pivot_table.
  • Choosing the correct shape for a given downstream task, reporting, plotting, or statistical analysis, is a practical skill exercised constantly throughout real analytics work, and a dataset reshaped for one purpose often needs to be reshaped again for another.

17. Further Reading#


Next Up#

Notebook 6: Descriptive Statistics and Probability Foundations — mean, median, mode, variance, standard deviation, common probability distributions, and the basic probability concepts needed for the exploratory and inferential analysis in the notebooks that follow, applied to the now clean and appropriately shaped data produced by Notebooks 4 and 5.


Course: Data Analytics | Notebook 5 of 13

Knowledge Checkpoint

Data Transformation & Reshaping Checkpoint

Q1.What is the key difference between `df.pivot()` and `df.pivot_table()` in Pandas?
A`df.pivot_table()` supports aggregation functions (like `aggfunc='mean'`) and handles duplicate index-column combinations; `df.pivot()` expects unique pairs and raises ValueError on duplicates.
B`df.pivot()` works on 3D arrays; `df.pivot_table()` works on 2D tables.
C`df.pivot_table()` converts all columns to integers.
DThere is no difference.
Q2.What does `pd.melt()` accomplish?
AIt unpivots a DataFrame from wide format to long format by turning column headers into a variable column and cell values into a value column.
BIt removes NaN values.
CIt merges two DataFrames on a common key.
DIt converts text data to lowercase.
Q3.In Hadley Wickham's "Tidy Data" principles, what three rules define a tidy dataset?
A1. Each variable forms a column; 2. Each observation forms a row; 3. Each type of observational unit forms a table.
B1. All numbers are normalized; 2. No NaN values exist; 3. Column names are alphabetical.
C1. All tables have fewer than 10 columns; 2. Data is saved as CSV; 3. Indexes are sequential.
D1. Data is sorted by date; 2. Text is tokenized; 3. Duplicates are removed.
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.