Use add_suffix() to append text to labels. It changes DataFrame column labels by default and Series index labels. Preserve shared keys, distinguish naming from value conversion and check collisions before combining datasets.
For a DataFrame, add_suffix() appends text to column labels by default. _Col changes NAME to NAME_Col, not to name1. The method returns a new object and does not change cell values.
import pandas as pd
df = 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]})
result = df.add_suffix("_Col")
print(result)
assert result.columns.tolist() == ["NAME_Col", "ID_Col", "MATH_Col", "ENGLISH_Col"]
assert df.columns.tolist() == ["NAME", "ID", "MATH", "ENGLISH"]Expected output
NAME_Col ID_Col MATH_Col ENGLISH_Col
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 70For a Series, add_suffix() changes row labels. The values and Series name remain unchanged. Labels that were integers become strings after a suffix is added.
s = pd.Series([1, 5, 7, 9, 10], name="score")
result = s.add_suffix("_Ser")
print(result)
assert result.index.tolist() == ["0_Ser", "1_Ser", "2_Ser", "3_Ser", "4_Ser"]
assert result.tolist() == s.tolist() and result.name == "score"Expected output
0_Ser 1
1_Ser 5
2_Ser 7
3_Ser 9
4_Ser 10
Name: score, dtype: int64Use axis="columns" or axis="index" explicitly for a DataFrame. The axis parameter is available from Pandas 2.0. Changing index labels can affect loc selection and alignment.
table = pd.DataFrame({"amount": [12, 20]}, index=[101, 102])
print(table.add_suffix("_sales", axis="columns"))
print(table.add_suffix("_order", axis="index"))
assert table.add_suffix("_order", axis="index").index.tolist() == ["101_order", "102_order"]Expected output
amount_sales
101 12
102 20
amount
101_order 12
102_order 20To append text to cell values, operate on the column itself. A label ending in _kg does not convert a stored measurement to kilograms: unit conversion requires a separate calculation. See combining text values.
table = pd.DataFrame({"weight": [2.5, 3.0]})
labelled = table.add_suffix("_kg")
print(labelled)
assert labelled["weight_kg"].equals(table["weight"])
codes = pd.Series(["0012", None], dtype="string")
print(codes + "-A")
assert pd.isna((codes + "-A").iloc[1])Expected output
weight_kg
0 2.5
1 3.0
0 0012-A
1 <NA>
dtype: stringadd_suffix() changes an entire axis. Use rename() with a mapping when only selected fields should change. Keep shared identifiers unchanged unless the join arguments explicitly account for their new names.
table = pd.DataFrame({"product_id": ["0012", "0045"], "quantity": [2, 3], "revenue": [11.0, 24.0]})
mapping = {column: str(column) + "_jan" for column in table.columns if column != "product_id"}
result = table.rename(columns=mapping)
print(result)
assert result.columns.tolist() == ["product_id", "quantity_jan", "revenue_jan"]Expected output
product_id quantity_jan revenue_jan
0 0012 2 11.0
1 0045 3 24.0Suffixing does not fix duplicated labels. Different original labels can also share the same string representation. Selective suffixing may collide with existing names, so validate the result before use.
table = pd.DataFrame([[5, 7]], columns=[1, "1"])
result = table.add_suffix("_source")
print(result)
print("Original labels unique:", table.columns.is_unique)
print("New labels unique:", result.columns.is_unique)
assert table.columns.is_unique and not result.columns.is_unique
partial = pd.DataFrame({"amount": [5], "amount_jan": [7]}).rename(columns={"amount": "amount_jan"})
assert not partial.columns.is_uniqueExpected output
1_source 1_source
0 5 7
Original labels unique: True
New labels unique: FalseThese synthetic tables contain one row per product per month. Append a month suffix to the payload fields and preserve product_id for the merge. An outer merge retains products present in only one month. Missing values are not automatically zero sales.
jan = pd.DataFrame({"product_id": ["0012", "0045"], "quantity": [2, 3], "revenue": [11.0, 24.0]})
feb = pd.DataFrame({"product_id": ["0012", "0070"], "quantity": [4, 1], "revenue": [22.0, 6.0]})
def suffix_payload(table, suffix, key):
assert table.columns.is_unique
assert table[key].notna().all() and table[key].is_unique
mapping = {column: str(column) + suffix for column in table.columns if column != key}
result = table.rename(columns=mapping)
assert result.columns.is_unique
return result
left = suffix_payload(jan, "_jan", "product_id")
right = suffix_payload(feb, "_feb", "product_id")
combined = left.merge(right, on="product_id", how="outer", validate="one_to_one", indicator=True)
print(combined)
assert len(combined) == 3 and combined.columns.is_uniqueExpected output
product_id quantity_jan revenue_jan quantity_feb revenue_feb _merge
0 0012 2.0 11.0 4.0 22.0 both
1 0045 3.0 24.0 NaN NaN left_only
2 0070 NaN NaN 1.0 6.0 right_onlyUse the merge indicator to identify comparable products. Keep unmatched products for review. Interpreting missing as zero requires confirmation that the source extract covers all products and that absence means no sales.
both = combined["_merge"].eq("both")
comparison = combined.loc[both].copy()
comparison["revenue_change"] = comparison["revenue_feb"] - comparison["revenue_jan"]
print(comparison[["product_id", "revenue_jan", "revenue_feb", "revenue_change"]])
print("Products in only one extract:")
print(combined.loc[~both])
assert comparison["product_id"].tolist() == ["0012"]
assert comparison["revenue_change"].iloc[0] == 11Expected output
product_id revenue_jan revenue_feb revenue_change
0 0012 11.0 22.0 11.0
Products in only one extract:
product_id quantity_jan revenue_jan quantity_feb revenue_feb _merge
1 0045 3.0 24.0 NaN NaN left_only
2 0070 NaN NaN 1.0 6.0 right_onlyConfirm each measurement retains its month and the identifiers retain leading zeros. Keep the merge indicator in an audit export. See column inspection and Excel export.
expected = ["product_id", "quantity_jan", "revenue_jan", "quantity_feb", "revenue_feb", "_merge"]
assert combined.columns.tolist() == expected
assert combined["product_id"].is_unique
print(combined.to_csv(index=False).rstrip())
# Optional Colab exports:
# combined.to_csv("monthly_product_audit.csv", index=False)
# comparison.to_csv("comparable_product_changes.csv", index=False)Expected output
product_id,quantity_jan,revenue_jan,quantity_feb,revenue_feb,_merge
0012,2.0,11.0,4.0,22.0,both
0045,3.0,24.0,,,left_only
0070,,,1.0,6.0,right_onlyRepeated calls append the suffix again. Conditional naming can help with mixed input schemas, but check for collisions after renaming. Preserve a naming map when downstream files depend on fixed labels.
table = pd.DataFrame({"amount": [5], "status_jan": ["paid"]})
mapping = {column: column if column.endswith("_jan") else column + "_jan" for column in table.columns}
result = table.rename(columns=mapping)
assert result.columns.is_unique
print(result)
print("Repeated suffix:", table[["amount"]].add_suffix("_jan").add_suffix("_jan").columns.tolist())Expected output
amount_jan status_jan
0 5 paid
Repeated suffix: ['amount_jan_jan']Use add_suffix() to change every label on an axis, add_prefix() for leading label text, and rename() for selected or arbitrary changes. Merge suffixes only disambiguate overlapping column names; proactively naming all payload columns can make the source or period clearer. Naming does not convert types, validate units or establish compatible business definitions.
Does add_suffix() change values? No. Is there inplace=True? No; store the returned object. Can I suffix just two fields? Use an explicit rename mapping. Does a month suffix prove a complete monthly extract? No; verify coverage and period separately. Why did the row index become text? A suffix forms string labels. Does suffixing remove duplicate labels? No. What about MultiIndex columns? Inspect the tuple structure and rename the intended level deliberately rather than treating a multi-level schema as flat labels.
Create columns id, math and english. Keep id unchanged and rename the measurements to math_exam and english_exam. Verify the original table is unchanged.
An explicit mapping preserves the key and communicates which labels change.
practice = pd.DataFrame({"id": [1, 2], "math": [75, 85], "english": [70, 80]})
answer = practice.rename(columns={"math": "math_exam", "english": "english_exam"})
print(answer)
assert answer.columns.tolist() == ["id", "math_exam", "english_exam"]
assert practice.columns.tolist() == ["id", "math", "english"]
assert answer["math_exam"].equals(practice["math"])Expected output
id math_exam english_exam
0 1 75 70
1 2 85 80Open in Google Colab View on GitHub
Run the examples in order, change suffixes and monthly data, then compare labels, unmatched products and calculated changes. Save your own copy to keep edits. All sample tables are included in the notebook.
Continue with the Data Cleaning hub, column inspection, rename() and add_prefix().
Reference: Pandas DataFrame.add_suffix 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.