How do I unpivot only some columns with melt in pandas?
Short answer
Pass id_vars for the columns to keep as they are and value_vars for the ones to unpivot. Naming both explicitly stops melt from unpivoting columns you meant to keep.
The short version
melt turns columns into rows. Pass id_vars for the columns that should stay as identifying columns, and value_vars for the columns to unpivot. Naming value_vars explicitly is important, because otherwise melt unpivots every column not listed in id_vars, including ones you wanted to keep.
A worked example
import pandas as pd
wide = pd.DataFrame({"city": ["Leeds"], "a": [1], "b": [2]})
long = pd.melt(wide, id_vars="city", value_vars=["a", "b"])
print(long.to_dict("records"))
[{'city': 'L', 'variable': 'a', 'value': 1},
{'city': 'L', 'variable': 'b', 'value': 2}]
The a and b columns became rows, with their names in a variable column and their contents in a value column, while city was repeated to identify each row.
Naming the new columns
The default names variable and value are rarely what you want in the output. Rename them at the point of melting.
pd.melt(
wide,
id_vars="city",
value_vars=["a", "b"],
var_name="metric",
value_name="amount",
)
This produces metric and amount columns, which say what they hold.
The trap of omitting value_vars
If you leave value_vars out, melt unpivots everything that is not an id column.
pd.melt(wide, id_vars="city") # unpivots a, b, AND any other column present
On a wide frame with extra columns you meant to keep (a date, a status), those get swept into the melt too, producing a confusing result. Listing value_vars explicitly makes the operation predictable, and it keeps working when someone adds a column to the source data later.
Several id columns
Pass a list to keep more than one identifying column, which is the usual case in real data.
pd.melt(df, id_vars=["city", "date"], value_vars=["sales", "returns"])
Every id column is repeated on each output row, preserving the context of each value.
Why unpivot at all
Long format, one row per observation, is what groupby, filtering and most plotting libraries expect. A spreadsheet layout with a column per month or per year fights you at every step, because the information you want to group by is trapped in the column headers. Melting moves it into a column where you can use it.
The reverse
pivot_table goes back the other way, spreading a column of categories across the columns. The pair, melt to analyse and pivot to present, is a common rhythm: reshape long for the work, reshape wide for the final table a person reads.