Reshape Data with melt and stack
Learn to reshape data using pandas melt and stack functions in this hands-on Python tutorial. Understand when to use each method, follow step-by-step examples, and troubleshoot common issues. Perfect for data analysis learners.
Focus: reshape data with melt and stack
Every data analyst has hit this wall: you've spent hours cleaning a dataset, only to realize it's in the wrong shape. Columns that should be values are spread out as headers, or you need to pivot a table for a visualization but the structure fights you at every turn. The pandas functions melt and stack are your escape hatches—they let you reshape messy wide data into the long, tidy format that most analysis and plotting tools expect. In this lesson, you'll learn how to use these transformations like a pro, so you can tackle any data shape with confidence.
The problem this lesson solves
Real-world data rarely arrives in the perfect tidy format that pandas loves. You might receive a spreadsheet where each column is a different month, or a survey where each response is a separate column. This wide format is great for human reading but awful for analysis. Need to compute average sales by month across all products? You'd have to write repetitive code for each column. Need to plot trends? Most visualization libraries expect one row per observation, with variables in columns.
The core problem: data in wide format prevents you from using pandas group-by operations, filtering, and plotting effectively. You know what you want—a long format where each row is an observation and each variable has its own column—but you don't know how to get there. That's exactly what melt and stack solve. They give you a direct, efficient path from wide to tidy, without manual loops or error-prone copying.
Pro tip: think of reshaping as the 'undo' button for pivot tables. If you've ever used
pivotorpivot_tableto aggregate data,meltandstackreverse that process, turning summarized columns back into granular rows.
Core concept / mental model
Imagine you have a table of sales data with products as rows and months as columns. That's like a spreadsheet where the variable 'month' is split across multiple headers. melt and stack are like turning that sheet 90 degrees—they gather all those month columns into a single column called 'month' and put the values in another column called 'sales'. The result is a table where each row says: "This product had this sales value in this month."
Here are the key definitions you'll encounter:
- Wide format: each row is one observation, but variables are spread across multiple columns (e.g., Jan_2023, Feb_2023).
- Long format: each row is one observation, and each variable (e.g., 'date', 'sales') has its own column. This is often called tidy data.
- identifier variable: the column(s) you keep intact (like 'product'), also called
id_vars. - variable name: the column that holds the former column names (like 'month'), controlled by
var_name. - value name: the column that holds the actual values (like 'sales'), controlled by
value_name.
The mental model: melt unpivots a DataFrame—it takes multiple columns and collapses them into two columns: one for the column names (variable) and one for the values. stack does something similar but works at the index level, turning columns into a MultiIndex, which is less intuitive but powerful for certain structured data.
Analogy: if your wide DataFrame is a wardrobe with many drawers (columns),
melttakes all contents and dumps them into two big bins—one labeled 'drawer name' and one labeled 'item'—while keeping the tags (like 'product') intact.
How it works step by step
The melt function is the workhorse for most reshaping tasks. Let's walk through its logic:
- Identify identifier columns: these are the columns you want to keep as-is (e.g., 'product', 'region'). They typically represent the entity of each row.
- Choose which columns to unpivot: either specify them explicitly with
value_vars, or letmeltautomatically use all remaining columns. - Decide names for the two new columns:
var_namefor the column that holds the former column names, andvalue_namefor the column that holds the values. - Call
melt: the function returns a new DataFrame in long format—each original row is repeated for each unpivoted column, with the value filled in.
For stack, the steps are:
- Set the index: columns you want to keep as identifiers become the index (or part of a MultiIndex).
- Call
stack(): pandas takes the remaining columns and moves them into the index, creating a MultiIndex with a level named after the stacked columns. - Reset the index (optional): to flatten the result back into a regular DataFrame with columns.
The key difference: melt gives you clean column names and works on the 'columns axis', while stack works on the index, which can be useful when you later need to unstack or pivot. For most data cleaning, melt is simpler and more intuitive.
Hands-on walkthrough
Let's get our hands dirty. We'll start with a classic wide dataset: monthly sales by product. First, install pandas if you haven't already:
pip install pandas
Now, create a sample DataFrame and melt it:
import pandas as pd
# Wide data: each month is a column
df = pd.DataFrame({
"product": ["Widget", "Gadget", "Sprocket"],
"Jan": [150, 230, 310],
"Feb": [180, 250, 290],
"Mar": [210, 270, 340]
})
print("Wide DataFrame:")
print(df)
# Melt into long format
melted = df.melt(id_vars=["product"],
var_name="month",
value_name="sales")
print("\nMelted (long) DataFrame:")
print(melted)
Expected output:
Wide DataFrame:
product Jan Feb Mar
0 Widget 150 180 210
1 Gadget 230 250 270
2 Sprocket 310 290 340
Melted (long) DataFrame:
product month sales
0 Widget Jan 150
1 Gadget Jan 230
2 Sprocket Jan 310
3 Widget Feb 180
4 Gadget Feb 250
5 Sprocket Feb 290
6 Widget Mar 210
7 Gadget Mar 270
8 Sprocket Mar 340
Now let's see stack in action on a similar structure. We'll keep the product column as index and stack the month columns:
# Use the same wide df
df_stacked = df.set_index("product").stack().reset_index()
df_stacked.columns = ["product", "month", "sales"]
print(df_stacked)
Expected output:
product month sales
0 Widget Jan 150
1 Widget Feb 180
2 Widget Mar 210
3 Gadget Jan 230
4 Gadget Feb 250
5 Gadget Mar 270
6 Sprocket Jan 310
7 Sprocket Feb 290
8 Sprocket Mar 340
The order differs (stack groups by index first), but the data is equivalent. You can also use melt to unpivot only specific columns, which is handy when you have some columns you want to keep long:
# Melt only Jan and Feb, ignoring Mar
melted_subset = df.melt(id_vars=["product"],
value_vars=["Jan", "Feb"],
var_name="month",
value_name="sales")
print(melted_subset)
Expected output:
product month sales
0 Widget Jan 150
1 Gadget Jan 230
2 Sprocket Jan 310
3 Widget Feb 180
4 Gadget Feb 250
5 Sprocket Feb 290
Pro tip: always use id_vars to preserve your identity columns. If you omit it, melt will treat all columns as values, which is rarely what you want.
Compare options / when to choose what
You have two main tools: melt and stack. Here's a quick comparison to help you decide:
| Feature | melt |
stack |
|---|---|---|
| Works on | Columns (unpivot) | Index (pivot columns into rows) |
| Result | Two new columns: variable and value |
MultiIndex with stacked column levels |
| Ease of use | Intuitive, requires id_vars |
Less intuitive, requires index management |
| Common use case | Tidy data for plotting, aggregation | Intermediate step before unstack, working with MultiIndex |
| After result | Ready for groupby or seaborn | Often followed by reset_index() |
When to choose melt:
- You want a simple, flat DataFrame in long format.
- You're preparing data for visualization libraries like seaborn or matplotlib.
- You need to aggregate across many columns (e.g., average by month).
When to choose stack:
- You're working with hierarchical data and need to keep the index structure.
- You plan to perform groupby on the stacked index.
- You need to reverse a
pivotorpivot_tableoperation.
There are also variations you might encounter:
pivotandpivot_table: the reverse operations, going from long to wide. Great for making summary tables.wide_to_long: a pandas function that handles complex multiple-column patterns (likescore_1,score_2) in one call.- Using
.T(transpose): simple row/column swap, but doesn't restructure variables—only flips axes.
In most data cleaning tasks, melt will be your go-to. Reserve stack for special cases involving MultiIndex or when you need to preserve hierarchical order.
Troubleshooting & edge cases
Issue 1: DataFrame becomes too long unexpectedly.
- Cause: you forgot to specify
id_vars, so all columns (including identifiers) got melted into thevaluecolumn. - Fix: always list your identifier columns in
id_vars. For example,df.melt(id_vars=['product'])keeps 'product' intact.
Issue 2: Column names become strings, and you lose data types.
- Cause:
meltconverts all column names to strings (they always are), but the values column may have mixed types if your columns had different dtypes. - Fix: after melting, use
pd.to_numericto coerce thevaluecolumn to numeric if needed. For example:
melted["sales"] = pd.to_numeric(melted["sales"], errors="coerce")
Issue 3: stack creates a MultiIndex that's hard to work with.
- Cause: stacking adds levels to the index, which can confuse beginners.
- Fix: call
reset_index()afterward to bring the index back to columns, and optionally rename the new column:
df_stacked = df.set_index("product").stack().reset_index()
df_stacked.columns = ["product", "month", "sales"]
Issue 4: Missing values in wide data (NaN) appear in the melted result.
- Cause:
meltpreserves NaNs because they might be meaningful. - Fix: use
.dropna()on the value column if you want to remove rows with missing values:
melted.dropna(subset=["sales"], inplace=True)
Issue 5: Duplicate column names.
- Cause: duplicate column names confuse
meltbecause it can't distinguish them. - Fix: rename columns to be unique before melting. For instance:
df.columns = [f"{col}_1" if i == 0 else col for i, col in enumerate(df.columns)]
Pro tip: always check your data types and unique column names before reshaping. A quick
df.columns.tolist()anddf.dtypescan save you hours of debugging.
What you learned & what's next
You've mastered the core idea behind melt and stack: transforming wide data into tidy, long format for analysis. You can now:
- Explain what
meltdoes and when to use it. - Complete a practical exercise to reshape data with
meltandstack. - Understand the difference between unpivoting with columns and stacking with index.
- Troubleshoot common issues like missing
id_varsor type coercion.
Your next step in the Data Analysis with Python track is likely about grouping and aggregation — once your data is in long format, you can use groupby to compute summaries, or pivot_table to create new wide views. These tools complete the reshape-pivot-aggregate cycle that powers most data analysis workflows.
Remember: every time you encounter a messy wide dataset, melt is your first move. It's a fundamental skill that will unlock the full power of pandas for your analyses.
Practice recap
Try it yourself: grab a wide dataset (like the built-in pandas.DataFrame for sales) and reshape it with melt. Then use groupby to compute the average sales per month. For a challenge, use stack on a dataset with a MultiIndex and compare the results. Share your output in the comments!
Common mistakes
- Forgetting to specify
id_vars— this melts your identifier columns into the value column, producing duplicates and confusion. - Mixing data types in the value column — if your wide columns have different dtypes, the melted column becomes object; coerce with
pd.to_numeric. - Using
stackon a regular DataFrame without setting an index — you'll get an error or unexpected results. - Not checking for duplicate column names before melting — pandas can't distinguish them and the output is wrong.
- Ignoring missing values — NaNs in wide data appear in the melted result; decide whether to drop or keep them.
Variations
- Use
pivotorpivot_tableto reverse a melt, turning long data back into a wide summary table. - For complex patterns like
score_1,score_2, usewide_to_longfor a one-call solution. - For simple row/column swaps, consider
.T(transpose) — but it won't create new variable columns.
Real-world use cases
- Convert monthly sales data from a spreadsheet into a long-format DataFrame for time-series plotting with seaborn or matplotlib.
- Prepare survey response data (with columns per question) into tidy rows for group-by analysis or statistical modeling.
- Reshape multi-column sensor readings (e.g., temperature and humidity per location) into long format for machine learning feature engineering.
Key takeaways
meltunpivots wide data into long format, turning multiple columns into 'variable' and 'value' columns.- Always specify
id_varsto keep identifier columns intact when usingmelt. stackmoves columns into a MultiIndex, useful for hierarchical data; pair it withreset_index()for a flat DataFrame.- Choose
meltfor most data cleaning and visualization prep; reservestackfor index-based operations. - Handle data types and missing values explicitly after reshaping to avoid subtle bugs.