Pandas drop(): Remove Rows and Columns by Label

Remove Pandas rows and columns using drop(), labels, index, axis and MultiIndex levels. Practise conditional cleaning and keep review records.

Create the original marks DataFrame 🔝

The original synthetic six-student sample is retained. Examples start from this unchanged base so earlier deletions do not affect later lessons.
import pandas as pd
df = pd.DataFrame({"NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"],
    "ID": [1, 2, 3, 4, 5, 6], "MATH": [80, 40, 70, 70, 70, 30],
    "ENGLISH": [80, 70, 40, 50, 60, 30]})
print(df)
assert df.shape == (6, 4)

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30

Keep the returned result 🔝

drop() returns a DataFrame by default; calling it without assignment leaves the original object unchanged. Assign the result to a new variable or back to your working variable.
df.drop(columns="ID")
assert "ID" in df.columns
trimmed = df.drop(columns="ID")
print(trimmed)
assert trimmed.columns.tolist() == ["NAME", "MATH", "ENGLISH"]
assert "ID" in df.columns

Expected output

   NAME  MATH  ENGLISH
0  Ravi    80       80
1  Raju    40       70
2  Alex    70       40
3   Ron    70       50
4  King    70       60
5  Jack    30       30

Use inplace=True on a deliberate working copy 🔝

inplace=True changes the receiving DataFrame and returns None. Do not assign that return value back to the DataFrame. It is not a general memory-saving guarantee.
working = df.copy()
result = working.drop(columns="ID", inplace=True)
print(working)
assert result is None and "ID" not in working.columns
assert "ID" in df.columns

Expected output

   NAME  MATH  ENGLISH
0  Ravi    80       80
1  Raju    40       70
2  Alex    70       40
3   Ron    70       50
4  King    70       60
5  Jack    30       30

Drop index labels 2 and 4 🔝

labels uses axis=0 by default. These are index labels rather than student IDs or guaranteed row positions. Remaining labels are retained; see reset_index() if you need new numbering.
remaining = df.drop(labels=[2, 4])
print(remaining)
assert remaining.index.tolist() == [0, 1, 3, 5]

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
3   Ron   4    70       50
5  Jack   6    30       30

Drop columns with columns or axis 🔝

columns is explicit. labels with axis=1 or axis="columns" has the same effect. Both original ID/MATH examples are retained in one verified comparison.
first = df.drop(columns=["ID", "MATH"])
second = df.drop(labels=["ID", "MATH"], axis=1)
pd.testing.assert_frame_equal(first, second)
print(first)
assert first.columns.tolist() == ["NAME", "ENGLISH"]

Expected output

   NAME  ENGLISH
0  Ravi       80
1  Raju       70
2  Alex       40
3   Ron       50
4  King       60
5  Jack       30

Drop an index label and a column together 🔝

index and columns can remove both axes in one call. The original index=2 and MATH example is retained. index=2 means a label; it is the third row here only because the base uses a RangeIndex.
result = df.drop(index=2, columns="MATH")
print(result)
assert 2 not in result.index and "MATH" not in result.columns
pd.testing.assert_frame_equal(df.drop(index=2, axis=0), df.drop(labels=2))

Expected output

   NAME  ID  ENGLISH
0  Ravi   1       80
1  Raju   2       70
3   Ron   4       50
4  King   5       60
5  Jack   6       30

Handle missing labels explicitly 🔝

errors="raise" is the default. errors="ignore" removes existing requested labels and skips absent ones; it can hide misspelled required columns, so validate important schemas first.
try:
    df.drop(index=[1, 3, 7])
except KeyError:
    print("Missing index label 7 raised KeyError")
else:
    raise AssertionError("Expected missing-label error")
result = df.drop(index=[1, 3, 7], errors="ignore")
print(result)
assert result.index.tolist() == [0, 2, 4, 5]

Expected output

Missing index label 7 raised KeyError
   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
2  Alex   3    70       40
4  King   5    70       60
5  Jack   6    30       30

Remove row positions deliberately 🔝

Convert desired positions into labels when using drop(). With duplicate labels, dropping one label removes every matching row; use iloc when you mean only specific positions.
labeled = df.copy()
labeled.index = [10, 20, 30, 40, 50, 60]
result = labeled.drop(index=labeled.index[[1, 3, 5]])
print(result)
assert result.index.tolist() == [10, 30, 50]
duplicate = df.head(3).copy()
duplicate.index = ["same", "same", "other"]
assert len(duplicate.drop(index="same")) == 1
assert len(duplicate.iloc[1:]) == 2

Expected output

    NAME  ID  MATH  ENGLISH
10  Ravi   1    80       80
30  Alex   3    70       40
50  King   5    70       60

Delete matching IDs or names 🔝

Find matching index labels, then drop them. With duplicate indexes, a label-based deletion can remove additional rows sharing those labels; Boolean selection is safer when you need to keep individual records. See filtering rows.
remove = df.loc[df["ID"].eq(2)].index
result = df.drop(index=remove)
print(result)
assert result["ID"].tolist() == [1, 3, 4, 5, 6]
without_ravi = df.loc[~df["NAME"].eq("Ravi")].copy()
assert "Ravi" not in without_ravi["NAME"].tolist()

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
5  Jack   6    30       30

Combine AND and OR conditions 🔝

Preserve the original Ravi/ID tests. Parenthesize each Boolean condition. The AND rule removes one record; the OR rule removes two.
both = df["NAME"].eq("Ravi") & df["ID"].eq(1)
either = df["NAME"].eq("Ravi") | df["ID"].eq(2)
and_result = df.drop(index=df.index[both])
or_result = df.drop(index=df.index[either])
print("AND remaining IDs:", and_result["ID"].tolist())
print("OR remaining IDs:", or_result["ID"].tolist())
assert len(and_result) == 5 and len(or_result) == 4

Expected output

AND remaining IDs: [2, 3, 4, 5, 6]
OR remaining IDs: [3, 4, 5, 6]

Keep rows with loc instead of deleting them 🔝

Retain the original MATH>=45 and ID==3 selection concepts as independent examples. loc is a purple indexer rather than a called function; see loc.
passing = df.loc[df["MATH"].ge(45)].copy()
one_student = df.loc[df["ID"].eq(3)].copy()
print(passing)
print(one_student)
assert passing["ID"].tolist() == [1, 3, 4, 5]
assert one_student["NAME"].tolist() == ["Alex"]

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
2  Alex   3    70       40
3   Ron   4    70       50
4  King   5    70       60
   NAME  ID  MATH  ENGLISH
2  Alex   3    70       40

Apply the original string-length deletion rules 🔝

These synthetic page records demonstrate Linking page length >256 and Page length strictly between 3 and 12. Missing values are kept by this policy. Threshold boundaries matter: lengths 3, 12 and 256 are not removed by their respective strict comparisons. See str.len().
pages = pd.DataFrame({"Linking page": ["a" * 256, "b" * 257, None],
    "Page": ["abc", "abcd", "x" * 12]})
too_long = pages["Linking page"].str.len().gt(256).fillna(False)
bad_page_length = pages["Page"].str.len().gt(3) & pages["Page"].str.len().lt(12)
links_kept = pages.drop(index=pages.index[too_long])
pages_kept = pages.drop(index=pages.index[bad_page_length])
print("Long-link removal kept labels:", links_kept.index.tolist())
print("Page-length removal kept labels:", pages_kept.index.tolist())
assert links_kept.index.tolist() == pages_kept.index.tolist() == [0, 2]

Expected output

Long-link removal kept labels: [0, 2]
Page-length removal kept labels: [0, 2]

Remove all rows while retaining columns 🔝

Drop all index labels or use an empty positional slice. Columns and their dtypes remain available; this does not remove the DataFrame itself.
empty = df.drop(index=df.index)
print(empty)
assert empty.shape == (0, 4)
assert empty.dtypes.equals(df.dtypes)

Expected output

Empty DataFrame
Columns: [NAME, ID, MATH, ENGLISH]
Index: []

Remove the last two rows 🔝

Retain both original approaches. drop(tail(2).index) is label-based; iloc[:-2] removes the last two positions. They agree for this unique index. With duplicate labels the drop version can remove more rows.
by_labels = df.drop(index=df.tail(2).index)
by_positions = df.iloc[:-2].copy()
pd.testing.assert_frame_equal(by_labels, by_positions)
print(by_positions)
assert by_positions["ID"].tolist() == [1, 2, 3, 4]

Expected output

   NAME  ID  MATH  ENGLISH
0  Ravi   1    80       80
1  Raju   2    40       70
2  Alex   3    70       40
3   Ron   4    70       50

Drop labels from a MultiIndex level 🔝

level chooses which named MultiIndex level to match. A tuple represents one complete MultiIndex label when dropping a specific pair.
multi = df.head(4).copy()
multi.index = pd.MultiIndex.from_tuples([("North", 1), ("North", 2), ("South", 3), ("South", 4)],
    names=["region", "student"])
regional = multi.drop(index="North", level="region")
one_removed = multi.drop(index=("North", 1))
print(regional)
assert len(regional) == 2 and len(one_removed) == 3

Expected output

                NAME  ID  MATH  ENGLISH
region student                         
South  3        Alex   3    70       40
       4         Ron   4    70       50

Keep a review table when cleaning records 🔝

This synthetic sales policy rejects missing or negative amounts. Preserve rejected records rather than losing them silently. Use Boolean row selection for this workflow, then drop the internal review-note column from the public output.
sales = pd.DataFrame({"id": ["001", "002", "003"], "amount": [5.25, -1, None],
    "review_note": ["", "negative", "missing"]})
valid = sales["amount"].notna() & sales["amount"].ge(0)
review = sales.loc[~valid].copy()
accepted = sales.loc[valid].drop(columns="review_note")
print(accepted)
print("Review IDs:", review["id"].tolist())
assert accepted["amount"].sum() == 5.25
assert len(accepted) + len(review) == len(sales)

Expected output

    id  amount
0  001    5.25
Review IDs: ['002', '003']

Parameter choices and common pitfalls 🔝

ParameterMeaning
labels / axisMatch labels on rows (0) or columns (1).
index / columnsExplicit row-label / column-label selection.
levelNamed or numbered MultiIndex level.
inplaceFalse returns a DataFrame; True mutates and returns None.
errorsraise by default; ignore skips absent labels.

drop() removes labels. dropna() removes missing-value records; drop_duplicates() handles repeated records. Do not confuse row positions, index labels and ID column values. Reset independent examples before trying another deletion. Check duplicate labels before using a mask's index to drop rows. Prefer explicit reassignment when retaining a clear original/cleaned pair.

Exercise: remove low marks and an identifier column 🔝

Remove students whose MATH is below 45, omit ID from the resulting table and keep the original unchanged. Verify the four remaining names.

Exercise solution 🔝

Drop matching labels from this unique-index sample, then remove the selected column.
remove = df.index[df["MATH"].lt(45)]
answer = df.drop(index=remove, columns="ID")
print(answer)
assert answer["NAME"].tolist() == ["Ravi", "Alex", "Ron", "King"]
assert df.shape == (6, 4) and "ID" in df.columns

Expected output

   NAME  MATH  ENGLISH
0  Ravi    80       80
2  Alex    70       40
3   Ron    70       50
4  King    70       60

Practice in Google Colab 🔝

Open in Google Colab View on GitHub

Run examples in order, inspect database records and try the exercise. Save a copy to keep edits.

Reference: Pandas drop() documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer