Use rename() to change column labels or row index labels with a mapping or callable. Choose the axis explicitly, detect missing source labels and check the proposed names for collisions before applying a schema change.
Specify columns explicitly to change field labels. Unlisted columns remain unchanged. The default returns a new DataFrame; it does not change stored values.
import pandas as pd
def student_data():
return pd.DataFrame({"NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"], "ID": [1, 2, 3, 4, 5, 6], "MATH": [30, 40, 50, 60, 70, 80], "ENGLISH": [20, 30, 40, 50, 60, 70]})
df = student_data()
result = df.rename(columns={"NAME": "name1", "ENGLISH": "eng"})
print(result)
assert result.columns.tolist() == ["name1", "ID", "MATH", "eng"]
assert df.columns.tolist() == ["NAME", "ID", "MATH", "ENGLISH"]Expected output
name1 ID MATH eng
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70index changes row labels rather than the values in an ID column. With inplace=True, the source object changes and the method returns None. Do not assign that return value back to df.
df = student_data()
returned = df.rename(index={0: "a", 1: "b"}, inplace=True)
print(df)
print("Return value:", returned)
assert returned is None
assert df.index.tolist() == ["a", "b", 2, 3, 4, 5]
assert df["ID"].tolist() == [1, 2, 3, 4, 5, 6]Expected output
NAME ID MATH ENGLISH
a Ravi 1 30 20
b Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70
Return value: NoneUse str.lower with axis="columns" to lowercase existing string labels. This changes labels only, not names stored in cells. For mixed-type labels, first decide whether converting labels to text is appropriate.
df = student_data()
result = df.rename(str.lower, axis="columns")
print(result)
assert result.columns.tolist() == ["name", "id", "math", "english"]
assert result["name"].tolist() == df["NAME"].tolist()Expected output
name id math english
0 Ravi 1 30 20
1 Raju 2 40 30
2 Alex 3 50 40
3 Ron 4 60 50
4 King 5 70 60
5 Jack 6 80 70The mapper form defaults to the row index, so specify axis when renaming columns. columns and index are clearer for many selective changes; do not mix conflicting forms.
df = student_data()
mapped = df.rename({"NAME": "name"}, axis=1)
explicit = df.rename(columns={"NAME": "name"})
assert mapped.equals(explicit)
print(explicit.head(2))
print("Default mapper leaves NAME unchanged:", df.rename({"NAME": "name"}).columns.tolist())Expected output
name ID MATH ENGLISH
0 Ravi 1 30 20
1 Raju 2 40 30
Default mapper leaves NAME unchanged: ['NAME', 'ID', 'MATH', 'ENGLISH']The default errors="ignore" ignores mapping keys absent from the selected axis. Use errors="raise" for a required schema migration so typos are visible. This does not detect collisions between target names.
df = student_data()
try:
df.rename(columns={"NAM": "name"}, errors="raise")
except KeyError as error:
print(type(error).__name__ + ": " + str(error))
result = df.rename(columns={"NAME": "name"}, errors="raise")
print(result.columns.tolist())
assert "name" in result.columnsExpected output
KeyError: "['NAM'] not found in axis"
['name', 'ID', 'MATH', 'ENGLISH']Renaming can create duplicate columns, including a collision with a column not listed in the mapping. Inspect the proposed labels before applying the change. Column validation explains why duplicate labels make selection ambiguous.
df = pd.DataFrame({"amount": [5], "total": [7]})
mapping = {"amount": "total"}
proposed = pd.Index([mapping.get(column, column) for column in df.columns])
print("Proposed labels:", proposed.tolist())
print("Unique:", proposed.is_unique)
assert not proposed.is_unique
# Resolve the naming conflict before calling rename().Expected output
Proposed labels: ['total', 'total']
Unique: Falselevel targets an existing label level. This changes values within that level, not its descriptive name. Use rename_axis() for axis names. Preserve the hierarchy unless a flat schema is explicitly required.
columns = pd.MultiIndex.from_tuples([("sales", "jan"), ("sales", "feb"), ("cost", "jan")], names=["metric", "month"])
df = pd.DataFrame([[12, 20, 5]], columns=columns)
result = df.rename(columns={"jan": "January", "feb": "February"}, level="month")
print(result)
assert result.columns.names == ["metric", "month"]
assert result.columns.get_level_values("month").tolist() == ["January", "February", "January"]Expected output
metric sales cost
month January February January
0 12 20 5This synthetic extract has spaces and inconsistent field labels. Normalise labels into a helper list, check collisions, then apply an explicit semantic mapping. A label called Amount is not automatically a valid numeric amount; type conversion is a separate step.
raw = pd.DataFrame({" Order ID ": pd.Series(["0012", "0045"], dtype="string"), " Product Name ": ["Pen", "Book"], "Amount ": ["5.50", "bad"]})
normalised_labels = pd.Index([str(column).strip().lower().replace(" ", "_") for column in raw.columns])
assert raw.columns.is_unique and normalised_labels.is_unique
normalisation_map = dict(zip(raw.columns, normalised_labels))
staged = raw.rename(columns=normalisation_map, errors="raise")
semantic_map = {"product_name": "product", "amount": "sales_amount"}
proposed = pd.Index([semantic_map.get(column, column) for column in staged.columns])
assert proposed.is_unique
cleaned = staged.rename(columns=semantic_map, errors="raise")
print(cleaned)
print("Naming map:", normalisation_map)
assert cleaned.columns.tolist() == ["order_id", "product", "sales_amount"]
assert cleaned["order_id"].iloc[0] == "0012"Expected output
order_id product sales_amount
0 0012 Pen 5.50
1 0045 Book bad
Naming map: {' Order ID ': 'order_id', ' Product Name ': 'product_name', 'Amount ': 'amount'}Verify required labels before calculations. Keep invalid raw values in a review table. See type conversion, dtype selection and missing-value audits.
required = {"order_id", "product", "sales_amount"}
assert required.issubset(cleaned.columns) and cleaned.columns.is_unique
amount = pd.to_numeric(cleaned["sales_amount"], errors="coerce")
valid = amount.notna() & amount.ge(0)
accepted = cleaned.loc[valid].copy()
accepted["sales_amount"] = amount.loc[valid]
review = cleaned.loc[~valid].copy()
print("Accepted:")
print(accepted)
print("Review:")
print(review)
assert accepted["order_id"].tolist() == ["0012"]
# Optional Colab export:
# accepted.to_csv("renamed_sales.csv", index=False)Expected output
Accepted:
order_id product sales_amount
0 0012 Pen 5.5
Review:
order_id product sales_amount
1 0045 Book badUse rename() for selected labels or callable transformations. add_prefix() and add_suffix() apply the same leading or trailing text across an axis. Renaming is not value replacement, dtype conversion or unit conversion. Inspect row labels after changes before using loc.
Why was a name not changed? Check the selected axis, exact spelling and surrounding spaces. Does errors="raise" guarantee unique target names? No; check target labels separately. Does rename() change row order? No. How do I change the index name? Use rename_axis(); rename(index=...) changes index labels. Can I use inplace=True? Yes; it returns None. Should I pass copy=True? The copy argument is deprecated and ignored in Pandas 3.0; these examples omit it. Does a cleaned label make its column valid? No; inspect values and types separately.
Create columns id, MATH and ENGLISH. Rename them to id, math_score and english_score, using errors="raise". Confirm the source column names and values remain unchanged.
An explicit mapping leaves id untouched. Verify both the target schema and the source.
practice = pd.DataFrame({"id": [1, 2], "MATH": [75, 85], "ENGLISH": [70, 80]})
answer = practice.rename(columns={"MATH": "math_score", "ENGLISH": "english_score"}, errors="raise")
print(answer)
assert answer.columns.tolist() == ["id", "math_score", "english_score"]
assert practice.columns.tolist() == ["id", "MATH", "ENGLISH"]
assert answer["math_score"].equals(practice["MATH"])Expected output
id math_score english_score
0 1 75 70
1 2 85 80Open in Google Colab View on GitHub
Run the examples in order, change mappings and imported labels, then inspect the output schema and naming collisions. Save your own copy to keep edits. All sample tables are included in the notebook.
Continue with the Data Cleaning hub, Pandas Input and Output, column inspection, add_prefix() and add_suffix().
Reference: Pandas DataFrame.rename documentation.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.