Renaming, sorting and dropping
Housekeeping — and the two footguns hiding in it.
.rename().sort_values().sort_index().drop()na_position.nlargest()Watch it happen
Play it through, or step back and forth yourself.
df.rename(columns={"cups": "cups_sold"})df.rename(columns={"cups": "cups_sold"}) takes a mapping, so you only name the ones you're changing. It also renames index labels via index=, and takes a function — columns=str.lower lowercases the lot.
The idea
The unglamorous operations that make a table readable. Two things in here catch people out, so they're flagged as we go.
Renaming
orders.rename(columns={"cups": "cups_sold"})
orders.rename(index={0: "first"})
orders.rename(columns=str.lower) # a function works too
orders.columns = ["a", "b", "c", "d", "e", "f"] # replace them allThe mapping form only touches the columns you name, so it's safe against a table whose other columns you don't know about. Assigning to .columns replaces every name and requires exactly the right count — fine after a groupby, risky otherwise.
A quick way to tidy a whole set of messy headers:
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")Sorting by value
orders.sort_values("cups", ascending=False)
orders.sort_values(["city", "cups"]) # city, then cups within it
orders.sort_values(["city", "cups"], ascending=[True, False])
orders.nlargest(3, "cups") # top 3 without a full sortFirst footgun. Sorting moves rows but not their labels. After sorting by cups, the index reads 3, 0, 2, 1, 4 — so .iloc[0] is now the biggest row while .loc[0] is still whichever row was always called 0. Exactly the same rule as filtering, and the same source of quiet bugs.
If you want the labels renumbered to match the new order, say so: .sort_values("cups").reset_index(drop=True).
Missing values sort last
orders.sort_values("rating") # NaN at the end
orders.sort_values("rating", na_position="first") # NaN at the topGaps go last whichever direction you sort, which is usually what you want — and na_position="first" is a neat way to bring the incomplete rows to the top when you're hunting for them.
Sorting by index
orders.sort_index() # by the row labels
orders.sort_index(axis=1) # puts the COLUMNS in alphabetical orderWorth doing after a concat or a merge. A sorted index makes label lookups a binary search rather than a scan, and label slicing (df.loc["a":"c"]) expects one.
Dropping
orders.drop(columns=["rating"])
orders.drop(index=[0, 1])
orders.drop(columns=["rating"], errors="ignore") # do not raise if absentAlways name the axis. drop has an older positional form that guesses, and the guess is not always what you meant.
The second footgun
Every method on this page returns a copy. None of them changes the frame you called them on:
orders.drop(columns=["rating"]) # computes a new frame and throws it away
orders = orders.drop(columns=["rating"]) # this is what you meantYou'll see inplace=True in older code. It's discouraged now — it doesn't actually save memory, it breaks method chaining, and it returns None, so df = df.drop(..., inplace=True) silently destroys your frame. Assign the result instead.
Which points at the better habit — chaining these together, since each returns a frame:
clean = (
orders
.drop_duplicates()
.dropna(subset=["rating"])
.rename(columns={"cups": "cups_sold"})
.sort_values("cups_sold", ascending=False)
.reset_index(drop=True)
)That reads top to bottom as a list of steps, and there's no half-modified frame anywhere.
See it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
tryna_position="first" on a sort by rating and watch the gaps come to the top.
Your turn
4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Return orders with cups renamed to cups_sold.
Return orders sorted by cups, largest first.
Sort by city ascending, and within each city by cups descending.
Return orders without the rating and revenue columns.
