0Pricing
Pandas & NumPy Academy · Lesson

melt: Wide to Long Format

Convert a wide DataFrame to long format with pd.melt, specifying id_vars and value_vars.

melt: Wide to Long Format is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Wide vs Long Data Formats

Data can be structured in two fundamental shapes. Wide format has one row per subject and spreads measurements across multiple columns — for example, separate columns for January, February, and March sales. Long format has one row per observation, with a column for the variable name and another for the value. Most Pandas analysis functions, machine learning libraries, and visualisation tools prefer long format, making melt() an essential transformation tool.

The pd.melt() Function

pd.melt(df) (also available as df.melt()) converts a wide DataFrame to long format. The key parameters are id_vars (columns to keep as identifiers, unchanged), value_vars (columns to unpivot into rows), var_name (name for the new variable column), and value_name (name for the new value column).

import pandas as pd

# Wide format: separate columns per month
df_wide = pd.DataFrame({
    'product': ['A', 'B'],
    'Jan':     [100, 150],
    'Feb':     [120, 130],
    'Mar':     [140, 160]
})

print(df_wide)
#   product  Jan  Feb  Mar
# 0       A  100  120  140
# 1       B  150  130  160

Basic melt() Call

Call melt() with id_vars set to the columns you want to keep as identifiers. Every other column is unpivoted: its name becomes a value in the new variable column and its cell values move to the value column. The number of output rows equals the number of input rows times the number of value columns being melted.

df_long = df_wide.melt(
    id_vars='product',
    var_name='month',
    value_name='sales'
)
print(df_long)
#   product month  sales
# 0       A   Jan    100
# 1       B   Jan    150
# 2       A   Feb    120
# 3       B   Feb    130
# 4       A   Mar    140
# 5       B   Mar    160

Selecting Which Columns to Melt

By default, every column not listed in id_vars is melted. Use value_vars to melt only a subset of columns. Columns not in id_vars or value_vars are dropped from the result. This is useful when your wide DataFrame contains a mix of time-series columns and other attributes you do not want to unpivot.

df_wide2 = pd.DataFrame({
    'product': ['A', 'B'],
    'category': ['Electronics', 'Clothing'],
    'Jan': [100, 150],
    'Feb': [120, 130],
    'Mar': [140, 160]
})

# Melt only Jan and Feb, keep product and category
df_long2 = df_wide2.melt(
    id_vars=['product', 'category'],
    value_vars=['Jan', 'Feb'],
    var_name='month',
    value_name='sales'
)
print(df_long2)

Resetting the Index After melt()

melt() preserves the original row indices by repeating them for each variable. The result often has a non-sequential index like [0, 1, 0, 1, 0, 1]. It is good practice to call reset_index(drop=True) immediately after melt() to get a clean sequential index from 0 to n-1 in the output.

df_long = df_wide.melt(id_vars='product', var_name='month', value_name='sales')
print('Before reset:', df_long.index.tolist())
# [0, 1, 0, 1, 0, 1]

df_long = df_long.reset_index(drop=True)
print('After reset:', df_long.index.tolist())
# [0, 1, 2, 3, 4, 5]

melt() Is the Inverse of pivot()

melt() is conceptually the reverse of pivot() / pivot_table(). You can go from long to wide with pivot_table() and from wide back to long with melt(). This round-trip is often used in pipelines: receive data in wide format, reshape to long for Seaborn plotting or ML features, then optionally pivot back for a report table.

# long -> wide
table = df_long.pivot_table(values='sales', index='product',
                            columns='month', aggfunc='sum')
print(table)
# product  Feb  Jan  Mar
# A        120  100  140
# B        130  150  160

# wide -> long again
long_again = table.reset_index().melt(id_vars='product', var_name='month', value_name='sales')
print(long_again.head())

Using melt() for Seaborn Plotting

Seaborn's categorical and relational plots work best with long-format data. A typical workflow: start with a wide DataFrame that has a column per group, melt to long format so that one column identifies the group and another holds the values, then pass both to Seaborn's hue and x/y parameters. This is one of the most common reasons to use melt().

import seaborn as sns
import matplotlib.pyplot as plt

# Long format is required for sns.lineplot with hue
df_long = df_wide.melt(id_vars='product', var_name='month', value_name='sales')

# sns.lineplot(data=df_long, x='month', y='sales', hue='product')
# plt.show()
print('Long data ready for Seaborn:')
print(df_long)

Naming the Output Columns

By default, melt() names the new variable column 'variable' and the value column 'value'. Always override these with meaningful names using var_name and value_name. Descriptive column names make downstream code easier to read and prevent confusion when a DataFrame has dozens of columns.

# Default: generic column names 'variable' and 'value'
default = df_wide.melt(id_vars='product')
print(default.columns.tolist())
# ['product', 'variable', 'value']

# Better: descriptive names
good = df_wide.melt(id_vars='product', var_name='month', value_name='revenue_usd')
print(good.columns.tolist())
# ['product', 'month', 'revenue_usd']

Melting Survey Data

A classic use case for melt() is survey responses where each column is a question and each row is a respondent. Melting converts this wide format into long format with columns for respondent ID, question name, and answer value. You can then easily compute the distribution of answers for each question using groupby().

survey = pd.DataFrame({
    'respondent': [1, 2, 3],
    'Q1_rating': [5, 3, 4],
    'Q2_rating': [4, 5, 3],
    'Q3_rating': [2, 4, 5]
})

long = survey.melt(id_vars='respondent', var_name='question', value_name='rating')
print(long)
print(long.groupby('question')['rating'].mean())

Sorting the Melted Result

After melting, the rows are ordered by the original column order (Jan, Feb, Mar for each row). You often want to sort by the identifier column first, then by the variable column. Use sort_values() on the melted DataFrame to achieve the order that makes most sense for your analysis or downstream processing.

df_long = df_wide.melt(id_vars='product', var_name='month', value_name='sales')

# Sort by product, then month
df_sorted = df_long.sort_values(['product', 'month']).reset_index(drop=True)
print(df_sorted)
#   product month  sales
# 0       A   Feb    120
# 1       A   Jan    100
# 2       A   Mar    140
# 3       B   Feb    130
# 4       B   Jan    150
# 5       B   Mar    160

melt() in a Data Pipeline

In a typical data pipeline, melt() appears right after loading or cleaning wide-format data, before any analysis or modelling step. Place it early in your pipeline to get to long format quickly, then all subsequent operations (groupby, seaborn, sklearn) work on the clean long-format data without special-casing.

def load_and_reshape(filepath):
    raw = pd.read_csv(filepath)
    # Melt all month columns into long format
    month_cols = [c for c in raw.columns if c.startswith('month_')]
    long = raw.melt(
        id_vars=[c for c in raw.columns if c not in month_cols],
        value_vars=month_cols,
        var_name='month',
        value_name='value'
    )
    return long.reset_index(drop=True)

Quick Check

Test your understanding of pd.melt wide-to-long transformation from this lesson.

Lesson Recap

In this lesson you learned: melt() converts wide to long format by unpivoting columns into rows; id_vars specifies which columns stay fixed and value_vars selects which columns to melt; var_name and value_name give descriptive names to the new columns; and long format is preferred for Seaborn, groupby, and ML pipelines. Next up we explore stack and unstack for reshaping MultiIndex DataFrames.

Frequently asked questions

Is the “melt: Wide to Long Format” lesson free?

Yes — the full text of “melt: Wide to Long Format” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.

What will I learn in “melt: Wide to Long Format”?

Convert a wide DataFrame to long format with pd.melt, specifying id_vars and value_vars. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Pandas & NumPy Academy?

No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “melt: Wide to Long Format” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Pandas & NumPy Academy lesson?

Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. pivot_table: Cross-Tabulation
  2. melt: Wide to Long Format
  3. stack and unstack with MultiIndex
  4. crosstab for Frequency Tables
← Back to Pandas & NumPy Academy