Replace all the labels on an axis at once, from a list you already have.
Have you ever scraped a table, looked at the column names, and realized there was nothing worth mapping from? Names like Unnamed: 1, Unnamed: 2, 0, 1, 2 — or a name so long and so full of footnote markers that retyping it exactly to use as a dictionary key is its own small ordeal. That is the situation set_axis exists for.
The distinction between it and its neighbor is simple, and it is the whole method. rename takes a mapping from old names to new ones, and changes the labels you name. set_axis takes a sequence and replaces all of them, positionally, first to first. You reach for it when you have a list of correct names — typed out, read from a config, computed from the old labels — and no mapping, often because the old labels are useless and there is nothing to map from.
Official documentation: DataFrame.set_axis
The arguments that earn their keep
df.set_axis(labels, # the one positional argument: a full-length sequence
axis=0) # keyword-only; 0 or 'index', or 'columns'
That is the entire method, and two things about it matter.
axis defaults to the rows, and almost everyone reaching for set_axis wants the columns. Forget it and you will hear about it: on a frame with 193 rows and 5 columns, a list of 5 column names produces ValueError: Length mismatch: Expected axis has 193 elements, new values have 5 elements. Which is the second thing. set_axis counts what you hand it and refuses anything that is not exactly the right length — the one real argument for using it over rename, since a miscount is caught rather than shrugged off.
It returns a new data frame and leaves the original alone, so it drops into a chain. There is no inplace; it went away in Pandas 2.0, and code copied from an older answer gets TypeError: DataFrame.set_axis() got an unexpected keyword argument 'inplace'. Do not pass copy= either — under copy-on-write it does nothing but raise a Pandas4Warning.
A worked example, on real data
Wikipedia's table of minimum annual leave by country is the data behind Bamboo Weekly #174. Reading it with read_html is one line, and the column names are what you get:
import pandas as pd
url = 'https://en.wikipedia.org/wiki/List_of_minimum_annual_leave_by_country'
raw = pd.read_html(url, storage_options={'User-Agent': 'Mozilla/5.0'})[1]
raw.columns.tolist()
['Country', 'Paid vacation days by year (five-day workweek) [1]', 'Paid public holidays (bank holidays) [2][3]', 'Total paid leave (five-day workweek)', 'Notes']
You could write a rename dictionary for that. You would be typing four long strings, exactly, footnote markers and all, purely so that Pandas can look them up and throw them away. What you actually have is five columns in a known order and five names you want them to have:
df = raw.set_axis(['country', 'vacation', 'holidays', 'total', 'notes'],
axis='columns')
df[['country', 'vacation', 'holidays', 'total']].head()
country vacation holidays total
0 Afghanistan 20 15 35
1 Albania 28 12 40
2 Algeria 30 11 41
3 Andorra 31 14 45
4 Angola 22 11 33
raw is untouched, and everything downstream is now ordinary.
The version of this you will meet in a spreadsheet is worse, because the names are not merely long, they are absent. Put a decorative title row above the data and read_excel hands back columns called Unnamed: 1 through Unnamed: 5. Try header= first when that happens, since both readers will take the row number that holds the real names. set_axis is for when no single row holds them — a two-row header with merged cells, where half the labels arrive as Unnamed: 3_level_1 and the names you want have to be assembled rather than found.
Three mistakes people make
Handing it a rename dictionary. This is the one that costs an afternoon. set_axis accepts any sequence, and iterating a dictionary gives you its keys — so a perfectly correct old-to-new mapping, passed to the wrong method, quietly sets the labels back to exactly what they already were:
raw.set_axis({'Country': 'country',
'Paid vacation days by year (five-day workweek) [1]': 'vacation',
'Paid public holidays (bank holidays) [2][3]': 'holidays',
'Total paid leave (five-day workweek)': 'total',
'Notes': 'notes'},
axis='columns').columns.tolist()
['Country', 'Paid vacation days by year (five-day workweek) [1]', 'Paid public holidays (bank holidays) [2][3]', 'Total paid leave (five-day workweek)', 'Notes']
No error, no warning, no change. The lengths matched, so the one check the method performs had nothing to say. If you have a dictionary, you want rename.
Using it to change one name out of many. set_axis has no partial mode. You supply every label or you supply none, which makes it the wrong tool for a single correction in a wide frame. That is rename — and it is worth knowing what you give up in the trade, because rename will accept a key that matches nothing and hand the frame back unchanged. raw.rename(columns={'Countries': 'country'}) on this table does precisely nothing, silently, because the column is called Country. Pass errors='raise' if you want to hear about it.
Assuming the labels have to be strings. They do not, and this is where the method quietly earns its place: df.set_axis(pd.to_datetime(df.columns), axis='columns') replaces string column names with a real DatetimeIndex, which is what makes .T.resample('3YE').mean().T possible afterward. Feed it whatever sequence you computed.
Where it shows up in Bamboo Weekly
Twelve Bamboo Weekly solutions call set_axis, in 36 places. Only one of them is free to read, so I have marked the rest:
Bamboo Weekly #75: Refugees is free, and is the computed-labels case at its clearest. The World Bank publishes one column per year and those names arrive as strings, so the solution does .pipe(lambda df_: df_.set_axis(df_.columns.astype(int), axis='columns')) — new labels derived from the old ones, in order, with no mapping in sight.
Bamboo Weekly #144: Museum heists (paid) is the two-row-header case, and the heaviest set_axis user on the site with eight calls. It reads an Excel file with header=[1,2], flattens the resulting tuples with '/'.join(col), and then makes two more passes to strip the Unnamed: fragments out of the joined names.
Bamboo Weekly #145: Economic indicators (paid) is the names-from-a-config case. Eight FRED series are described in a Python dictionary, and after pd.concat the columns come from data_sources.values() — a dict_values view, which is a sequence, which is all set_axis asks for.
Practice it
Work through a set_axis exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/set-axis/
Go deeper
rename is the other half of this page and the one to read next: it takes the mapping, handles one level of a MultiIndex, and covers rename_axis for naming the axis itself rather than its labels. reset_index is for labels that are not wrong so much as in the wrong place. And if the junk names came from a file or a web page, the fix may belong further upstream, in read_excel or read_html.
More Pandas videos on Python and Pandas with Reuven Lerner.
Bamboo Weekly is the practice. If you want the structured version — full courses with downloadable Jupyter notebooks, plus live Pandas office hours when you get stuck — that is LernerPython+Data. A paid Bamboo Weekly subscription is included with it.
Related methods
.rename()— when you only want to change some of the labels and know their current names.reset_index()— when the labels should move between the index and the columnspd.read_excel()— which is where Unnamed columns usually come from in the first place
See it on real data
Below are the 12 Bamboo Weekly exercises that use set_axis on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #174: Vacation
- Bamboo Weekly #167: Oil prices
- Bamboo Weekly #163: Daylight saving time
- Bamboo Weekly #153: Venezuela
- Bamboo Weekly #145: Economic indicators
- Bamboo Weekly #144: Museum Heists
- Bamboo Weekly #141: Argentina
- Bamboo Weekly #127: European comparisons
- Bamboo Weekly #123: Missiles
- Bamboo Weekly #98: Retail sales
- Bamboo Weekly #97: Drones
- Bamboo Weekly #75: Refugees
Part of the Pandas Methods Index. See also practice by skill.