Written and maintained by CASRAI Editorial Board
Last updated
pandas melt (pandas.melt() or its identical method DataFrame.melt()) reshapes a DataFrame from wide to long format: the columns you name as identifiers stay as they are, and every other column is unpivoted into two new columns, variable (the old column name) and value (the cell contents). This guide is for researchers and analysts who need to turn one-column-per-measurement data into one-row-per-observation data for grouping, plotting or modelling. It covers the signature, a worked research example with output run under pandas 3.0.5, the errors you are most likely to meet, and how melt compares with its neighbours and with the equivalent steps in Stata, SAS and Power Query.
What pandas melt is used for in research
Instruments, surveys and repeated-measures designs often produce wide tables: one row per participant, one column per time point or item. Most grouping, plotting and modelling tools expect long data instead, with one row per observation. The pandas user guide describes melt as reshaping so that one or more columns are identifier variables, while all other columns (the measured variables) are unpivoted to the row axis, leaving two non-identifier columns named variable and value by default. See also tidy data rules for structuring research data and data wrangling.
Install or access
melt ships with pandas; there is no separate package. The official installation page gives pip install pandas or, with conda, conda install -c conda-forge pandas. Then import pandas as pd. If you are new to building frames, see how to create a DataFrame in Python.
Syntax and parameters
From the pandas 3.1.1 API reference:
pandas.melt(frame, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None, ignore_index=True)
It returns the unpivoted DataFrame. DataFrame.melt takes the same arguments without frame.
| Parameter | What it does (per the pandas documentation) |
|---|---|
id_vars |
Column(s) to use as identifier variables; these are kept as columns. |
value_vars |
Column(s) to unpivot. If not specified, all columns that are not set as id_vars are used. |
var_name |
Name for the variable column. If None, uses frame.columns.name or ‘variable’. Must be a scalar if the columns are a MultiIndex. |
value_name |
Name for the value column, default ‘value’. It cannot be an existing column label. |
col_level |
If the columns are a MultiIndex, the level to melt. |
ignore_index |
Default True. If False, the original index is retained and index labels are repeated as necessary. |
Step-by-step: wide repeated-measures scores to long format
Illustrative composite: the data below are invented for demonstration and do not come from any real study. Each participant has a score at three time points in separate columns.
- Build or load the wide table.
import pandas as pdwide = pd.DataFrame({ "participant": ["P01", "P02", "P03"], "group": ["control", "treatment", "treatment"], "t1": [12, 14, 11], "t2": [13, 18, 15], "t3": [13, 21, 19]}) - Name the identifier columns in
id_varsand the measurement columns invalue_vars, and give the new columns meaningful names.long = wide.melt(id_vars=["participant", "group"], value_vars=["t1", "t2", "t3"], var_name="timepoint", value_name="score") - Use the long table, for example to summarise by group and time point.
long.groupby(["group", "timepoint"])["score"].mean()
Output and how to read it
Output of step 2 under pandas 3.0.5: three participants times three time points gives nine rows. Rows are ordered by melted column first (all t1 rows, then t2, then t3), and the original index is discarded and renumbered 0 to 8 because ignore_index defaults to True.
participant group timepoint score0 P01 control t1 121 P02 treatment t1 142 P03 treatment t1 113 P01 control t2 134 P02 treatment t2 185 P03 treatment t2 156 P01 control t3 137 P02 treatment t3 218 P03 treatment t3 19
Output of step 3 (group means):
group timepointcontrol t1 12.0 t2 13.0 t3 13.0treatment t1 12.5 t2 16.5 t3 20.0
Melt multiple columns, or all the rest
List several names in value_vars to unpivot only those. Omit value_vars and every column not in id_vars is unpivoted, which is the shortest form when all remaining columns are measurements.
Keep the original index
The user guide notes that melt ignores the index. With ignore_index=False the original index values are kept but duplicated: in the example above the index would run 0, 1, 2, 0, 1, 2, 0, 1, 2. A repeated index is a sign you may want participant as an explicit identifier column instead.
MultiIndex columns
Use col_level to melt one level. The API reference also shows passing tuples, as in pd.melt(df, id_vars=[("A", "D")], value_vars=[("B", "E")]), which produces columns named (A, D), variable_0, variable_1 and value. A custom var_name must be a scalar when the columns are a MultiIndex.
Opposite of melt: back to wide (unmelt)
The documentation does not describe pivot as melt’s inverse; as an editorial interpretation, for data with a unique identifier-and-variable combination, pivot restores the wide shape:
back = long.pivot(index=["participant", "group"], columns="timepoint", values="score").reset_index()
This returned the original three rows with columns participant, group, t1, t2, t3 in the test run. The user guide states that pivot() can only handle unique rows specified by index and columns, and to use pivot_table() if the data contains duplicates.
melt vs wide_to_long, stack, pivot and explode
| Function | Use it when | Source wording |
|---|---|---|
melt |
You name identifier columns and unpivot the rest into variable/value. | User guide: identifier variables stay, measured variables go to the row axis. |
wide_to_long |
Column names share a stub plus a suffix (A1970, B1980) and you want the suffix as its own level. | User guide: “similar to melt() with more customization for column matching.” |
stack / unstack |
You want to move a column level into the row index, or back, on an indexed frame. | User guide: pivot a level of column labels or of the row index; unstack can create missing values and fill_value overrides them. |
pivot |
Long to wide where each index/columns pair is unique. | User guide: only handles unique rows. |
pivot_table |
Long to wide where duplicates exist and must be aggregated. | Listed under melt’s See also and recommended for duplicate data. |
explode |
Cells hold lists that should become rows. | Listed under melt’s See also. |
Troubleshooting
Messages below were produced in a local run under pandas 3.0.5.
- ValueError: value_name (group) cannot match an element in the DataFrame columns. You passed a
value_namethat is already a column label. Choose a new name. - KeyError: The following id_vars or value_vars are not present in the DataFrame: [‘t9’] A name in
id_varsorvalue_varsdoes not match a column label exactly; check spelling and case. - ValueError: Index contains duplicate entries, cannot reshape raised by
pivotwhen going back to wide with repeated index/column pairs. Usepivot_tablewith anaggfuncsuch as “mean”, which in the test returned 1.5 for two values of 1 and 2, or fix the duplicates first. - Unexpected row order or index. Rows are stacked column by column, and the index is renumbered unless
ignore_index=False.
Free alternatives in other tools
| Tool | Equivalent task | CASRAI guide |
|---|---|---|
| Stata | Reshape wide to long | reshape command in Stata |
| SAS | Transposing variables to observations | PROC TRANSPOSE in SAS |
| Power Query (Excel, Power BI) | Unpivot columns | how to use Power Query |
Within Python, melt is the built-in route and needs no extra package. To combine frames before or after reshaping, see pandas concat.
FAQ
What does pandas melt do?
It converts a wide DataFrame to long format, keeping the id_vars columns and turning the other columns into variable and value columns.
Is df.melt() different from pd.melt()?
No. The API reference lists DataFrame.melt as the identical method.
How do I melt multiple columns?
Pass them as a list to value_vars, or omit value_vars to melt every column not in id_vars.
Is there a pandas unpivot function?
Unpivoting is what melt does; the user guide describes unpivoting measured variables to the row axis.
What is the opposite of pandas melt?
Use pivot for unique keys or pivot_table to aggregate duplicates; see the table above.
Sources
- pandas.melt, pandas 3.1.1 documentation: pandas.pydata.org/docs/reference/api/pandas.melt.html (read 2026-11-07)
- Reshaping and pivot tables, pandas user guide: pandas.pydata.org/docs/user_guide/reshaping.html (read 2026-11-07)
- Installation, pandas: pandas.pydata.org/docs/getting_started/install.html (read 2026-11-07)







