Read First, Then Look Before You Filter
The fastest way to waste an afternoon is to write a fancy filter on a file you haven't inspected. Load the data, print its shape, print its columns, then filter. You avoid the classic trap of filtering on a column name that has a trailing space or mixed case.
A DataFrame is just a table in memory: rows are records, columns are fields like Sales or Region. Almost everything you did with Excel filters maps to a one-line pandas expression, and it runs on files far larger than Excel's row limit.
Step 1: Load a CSV File
Type 'import pandas as pd'. Analysts almost always alias it as pd, so every example you find online will assume that name. Use it.
Run 'df = pd.read_csv("orders.csv")'. This returns a DataFrame. If your file uses semicolons instead of commas, add 'sep=";"' or the whole row lands in one column.
Run 'print(df.shape)'. On a 50,000-row Orders file this prints (50000, 6). The first number is rows, the second is columns. Use it to confirm the file read fully.
Step 2: Inspect With head() and info()
Run 'df.head()' to see the first five rows. For a wider view use 'df.head(10)'. This is your quick sanity check that Sales and Region look like numbers and text, not scrambled values.
Run 'print(df.columns)'. You might see Index(['OrderID', 'Region', 'Product', 'Sales', 'OrderDate'], dtype='object'). Note the exact spelling; filters are case-sensitive.
Run 'df.info()'. It shows each column's dtype and how many non-null values exist. If Sales is an 'object' instead of 'float64', a comma like '1,200' is blocking the conversion and your sums will break.
I recommend running 'df.head()' and 'df.info()' on every new file before writing a single filter. A five-second look at dtypes catches the comma-in-number problem that otherwise ruins your totals an hour later.
Step 3: Filter on One Condition
Run 'west = df[df['Region'] == 'West']'. The inner 'df['Region'] == 'West'' builds a True/False mask, and wrapping it in 'df[...]' keeps only the True rows. You now have every West order.
Run 'big = df[df['Sales'] > 1000]'. This returns rows where Sales exceeds 1000. On the sample data that's about 12 percent of orders, a useful cut for a high-value report.
Run 'print(len(west))' to see how many rows matched. If you expected 8,000 and got 0, your Region value is probably 'west' lowercase — masks are case-sensitive.
Step 4: Combine Multiple Conditions
Run 'mask = (df['Region'] == 'West') & (df['Sales'] > 1000)'. Note the single '&' and the parentheses around each test. The double '&&' from other languages will break here and throw an error.
Run 'mask = (df['Region'] == 'West') | (df['Region'] == 'East')'. The single '|' means OR. Wrap each condition in parentheses or Python evaluates it wrong and your filter returns everything.
Run 'result = df[mask]'. result holds only rows meeting all (or any) of your tests. Print 'result.shape' to confirm the row count dropped the way you intended.
A common mistake is forgetting parentheses around each condition in a combined mask. Without them, Python applies operator precedence you didn't intend and returns the full table instead of a filtered slice. Always wrap each test.
Step 5: Filter Text and Dates
Run 'df[df['Product'].str.contains('Pro')]'. This returns products whose name contains 'Pro'. Use 'case=False' to ignore upper and lower case in the match.
Run 'df['OrderDate'] = pd.to_datetime(df['OrderDate'])'. After that, 'df[df['OrderDate'] > '2024-01-01']' filters by date. Skip the parse and the comparison treats dates as text and returns nothing useful.
Run 'q1 = df[(df['OrderDate'] >= '2024-01-01') & (df['OrderDate'] < '2024-04-01)]'. This grabs the first quarter. Quarter cuts like this are how I build monthly sales decks from one raw export.

Step 6: Read Excel and SQL Too
Run 'df = pd.read_excel("orders.xlsx", sheet_name="2024")'. Name the sheet or pandas reads the first one, which may not be the tab you want.
With a connection 'conn', run 'df = pd.read_sql("SELECT * FROM Orders WHERE Region = 'West'", conn)'. This pulls straight from the database, so you filter at the source and move less data.
Run 'df = pd.read_csv("orders.csv", usecols=['Region','Sales'])'. Skipping columns you won't use cuts memory on big files and speeds up every later step.
Worked Example: A 14 Percent High-Value Slice
Take the Orders table with 50,000 rows and columns Region and Sales. Filter to 'df[df['Sales'] > 1000]' and you get 7,020 rows. That's 14.04 percent of the file. Sum those with 'df[df['Sales'] > 1000]['Sales'].sum()' and you get 9,310,400 in revenue. One mask turned a 50,000-row blob into a single defensible number for the board.
Run 'share = len(big) / len(df) * 100' and print it. Seeing 14.04 tells you the high-value segment is small but worth a chart.
Run 'big.to_csv("high_value_orders.csv", index=False)'. The 'index=False' keeps pandas from writing its row numbers as a stray first column you'd have to delete in Excel.
How This Compares to Excel Filters
Excel's filter button is great for a one-time look. But when you repeat the same Sales-over-1000 cut every Monday, a saved notebook reruns in one command while the manual clicks eat 20 minutes. If your data already lives in a database, our SQL for data analysis guide shows how to filter there before it ever reaches Python.
Step 7: Sort and Rank the Filtered Rows
A filter gives you a subset; sorting tells you which rows matter most. After narrowing to high-value orders, rank them so the biggest deal sits on top. This is the view a manager actually scrolls through, not the raw 50,000-row file.
Run 'df[df["Sales"] > 1000].sort_values("Sales", ascending=False)'. The biggest order now leads. On the sample data the top row might be a 9,800 deal that would have been row 41,234 in the unsorted file.
Run 'top.head(10)'. This shows your ten largest orders, a list you can paste straight into a follow-up email. A common mistake is forgetting ascending=False and presenting the smallest deals as the highlights.
Run 'top = top.reset_index(drop=True)'. The drop=True throws away the old row numbers so your exported top-ten starts at 1 and reads like a clean leaderboard, not a slice of a bigger table.
Combine filter, sort, and head into one line once you are comfortable: 'df[df["Region"]=="West"]["Sales"].sort_values(ascending=False).head(5)'. Chaining reads top to bottom and removes the temporary variables that clutter a longer script. I reach for the chain on quick questions and keep the named steps for anything a teammate will maintain.
Watch out for chained assignment warnings. When you write 'df[df["Region"]=="West"]["Sales"] = 0' pandas may complain it cannot tell which copy to modify. Filter into a variable first, change it, then assign back, and the warning disappears.
Read the File Before You Trust It
Before any filter, confirm the file actually loaded the way you think. A CSV with a stray quote on row 9,000 can shift every column after it, so your Sales column holds text and your Region holds numbers. Print 'df.dtypes' and 'df.shape' on every new file; a two-second look saves an hour of chasing a filter that returns nothing because the types are wrong.
Watch the delimiter. European exports often use semicolons, and some systems pad fields with spaces, so 'West ' never equals 'West'. Read with 'sep=";"' when needed and run 'df['Region'] = df['Region'].str.strip()' to drop the padding. A filter that silently matches zero rows is the most common way a report ships with a hidden hole the reader never sees.
Save your cleaned view, and don't overwrite the raw one. Write the filtered result to a new CSV and keep the original untouched, so you can always rerun the steps from the source. When a manager asks why a number changed, you compare against the raw file instead of guessing what the script did to it, and that audit trail is worth more than the few seconds you save by writing in place.
Next Steps
Filtering gets you subsets; grouping turns those subsets into summaries. The next move is 'df.groupby('Region')['Sales'].sum()', which totals Sales per region in one line instead of dozens of filtered copies.
Take any CSV with a numeric column and a category column, load it, and write one AND mask plus one OR mask. Confirm each result's row count makes sense.
Move to the groupby guide below to learn aggregation that reads like SQL, turning filtered rows into Region-level totals.


