keepcalculator

06 Numbers and Powers

Pandas Wide to Long: Reshape Data with melt()

Card No. 06·6467 · Updated · Standard formula, shown below

Yes, pandas can turn repeated columns into tidy rows using the melt() function (or pd.wide_to_long() for more complex patterns). You provide the columns you want to keep as identifiers, and pandas stacks the remaining columns into two new columns: one for the former column names and one for their values. This is the core of converting a wide DataFrame into a long (or tidy) format.

Why Reshape Wide to Long?

Wide data stores each observation as a row and each variable as a column. This is intuitive for viewing but often problematic for analysis. Many statistical methods, plotting libraries (like seaborn), and pandas operations expect tidy data: each row is one observation, each column is one variable. When you have repeated measurements (e.g., sales by month, scores by test, readings by sensor), those repeated columns are actually levels of a single variable. Melting collapses them into rows, making the data suitable for groupby, time-series analysis, or faceted plots.

For example, a DataFrame with columns id, jan, feb, mar can be melted into rows like id=1, month=jan, value=10. This long format is easier to filter, aggregate, and visualize.

How melt() Works

The basic syntax is pd.melt(frame, id_vars, value_vars, var_name, value_name). You specify:

  • id_vars: columns to keep as identifiers (e.g., subject ID, date).
  • value_vars: columns to unpivot. If omitted, all columns not in id_vars are used.
  • var_name: name for the new column holding the original column names.
  • value_name: name for the new column holding the values.

Here’s a concrete example:

import pandas as pd

df = pd.DataFrame({
    'id': [1, 2],
    'jan': [10, 20],
    'feb': [15, 25]
})

long = pd.melt(df, id_vars=['id'], var_name='month', value_name='sales')
print(long)

Output:

   id month  sales
0   1   jan     10
1   2   jan     20
2   1   feb     15
3   2   feb     25

Each original row becomes multiple rows—one per melted column. The order is by column, then row index.

Using pd.wide_to_long() for Prefix Patterns

When your columns follow a pattern like score_1, score_2, score_3, melt() alone gives you a column with the full names. If you want to split the number into a separate column, use pd.wide_to_long(). It takes a stubname (the prefix), a separator (like _), and the suffix as a number or string.

df = pd.DataFrame({
    'id': [1, 2],
    'score_1': [90, 85],
    'score_2': [88, 92],
    'score_3': [91, 89]
})

long = pd.wide_to_long(df, stubnames='score', i='id', j='attempt', sep='_')
print(long.reset_index())

This yields columns id, attempt, and score. The j parameter names the new column for the suffix. This is ideal for repeated measurements with clearly numbered columns.

Choosing Between melt() and wide_to_long()

Use melt() when you simply want to stack columns without splitting their names. Use wide_to_long() when the column names contain a meaningful suffix (e.g., time point, trial number) that you want as a separate column. Also consider stack() for MultiIndex columns—it’s more advanced but gives similar results.

If your data has multiple groups of repeated columns (e.g., score_1 and time_1), you can call wide_to_long() with multiple stubnames, and it will create separate columns for each stub. This is more efficient than melting twice and merging.

Common Mistakes and Pitfalls

  • Forgetting to reset index: After wide_to_long(), the result often has a MultiIndex. Use reset_index() to get a flat DataFrame.
  • Mixing data types: Melting columns with different dtypes will force the value column to a common type (usually object). Ensure your value columns are homogeneous or convert after melting.
  • Ignoring id_vars: If you omit id_vars, all columns are melted, which is rarely what you want. Always specify the identifier columns.
  • Column name collisions: If your new var_name or value_name already exists, pandas will raise an error. Choose unique names.
  • Separator in wide_to_long: The sep parameter must match your column naming. If your columns are score1 (no underscore), set sep=''.

Performance and Alternatives

For small to medium DataFrames, melt() is fast and straightforward. For very large data, consider using pivot_longer() from the pyjanitor package, which offers a more feature-rich API. Also, stack() can be faster in some cases but requires a MultiIndex. In practice, melt() is sufficient for most tasks.

Remember that reshaping is not the same as aggregating. Melting increases the number of rows; it does not change the data’s content. If you need to summarize (e.g., mean per group), do that after melting.

Frequently Asked Questions

How do I melt only specific columns?

Pass a list to value_vars. For example, pd.melt(df, id_vars=['id'], value_vars=['jan', 'feb']) melts only those two columns. The other columns (like mar) are dropped from the result.

What if my column names are not strings?

Pandas converts column names to strings for the var_name column. If you need the original types, you can convert them back after melting using astype() or a custom function.

Can I melt multiple groups of columns at once?

Yes, use pd.wide_to_long() with multiple stubnames. For example, pd.wide_to_long(df, stubnames=['score', 'time'], i='id', j='attempt', sep='_') will produce separate columns for score and time for each attempt.

How do I do the reverse (long to wide)?

Use df.pivot(index='id', columns='month', values='sales') or df.pivot_table() for duplicates. This is the inverse of melting.

Why does my value column become object dtype?

Because the melted columns have different dtypes (e.g., int and float), pandas must use a common type that can hold all values, which is often object. Convert the column to numeric after melting if needed.

Does melt() change the original DataFrame?

No, melt() returns a new DataFrame. The original remains unchanged. If you want to modify in place, assign the result back.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →