Method chaining writes a sequence of steps as one expression, top to bottom, inside parentheses:
(df
.query("channel == 'Online'")
.assign(month=lambda d: d["order_date"].dt.strftime("%Y-%m"))
.groupby("month")["revenue"].sum()
)
assign returns a new table with added columns. Use lambda d: ... to refer to the table as it is at that point in the chain, including columns made earlier in the same chain. df["new"] inside the chain refers to the original df, which doesn't have them.
query filters with a string; @name uses a Python variable.
pipe(func, ...) passes the table to your own function: .pipe(add_margin, products) is add_margin(table, products).
Chains leave df unchanged and have no half-finished variables lying around. Keep each step on its own line so you can comment one out while debugging.