Skip to content
wannacode

Filters and sorting

In short: Filtering in pandas keeps only the rows where a mask is True (or isin, between, query), and sort_values orders them by a column.

Problem

The list for the supplier

The food supplier comes tomorrow. The manager wants a list: the food products we have fewer than 20 of, most urgent first. Then they also ask about the orders over the year: how many were cancelled and how many customers buy from us regularly.

Pythondata: the shop tables

You can scan 25 products by eye. The orders for the year are 11,116, and there your eyes are not enough. You need a way to tell the table “give me only these rows” and “order them like this”.

The idea

A mask on a table

The mask from NumPy works in pandas too. products['stock'] < 20 compares every row and gives a column (a Series) of True and False, with the table’s index. Put inside square brackets, the mask keeps the rows with True:

Pythondata: the shop tables

low.sum() counts the Trues: 9 products are under 20 in stock. This is called a filter: a table with only the rows for which the condition is true.

Look at the index on the left: 3, 6, 7, 12… The rows keep their labels from products, so you always know where each one came from. Inside [], a column name picks a column, and a mask picks rows.

The idea

Two conditions: &, | and ~

Under 20 in stock and under €5 are two conditions together. With and you get the same as in NumPy:

Pythondata: the shop tables

and wants one True or False, and the column has 25. So here too you write & for “and”, | for “or” and ~ for “not”, with every condition in brackets:

Pythondata: the shop tables

Under 20 in stock and under €5 are the banitsa and the cucumbers, both sold out. Under €1 or over €20 are the butter roll (€0.80), the water (€0.90), the frying pan (€24.90) and the knife set (€39). The gift card is exactly €20 and is not “over 20”.

Try it

The dearer dairy

Which dairy products (category 4) cost over €3? Put the table of them in pricey_dairy.

Pythondata: the shop tables

The idea

From a list and between two numbers: isin and between

Food is in four categories: 3, 4, 5 and 6. Instead of four conditions with | you write isin, which asks “is it in this list?”:

Pythondata: the shop tables

There are 18 food products. With ~ you get the other 7: the six home products and the gift card. The card has no category (NaN), and NaN is in no list, so ~ catches it.

For a range there is between. between(2, 5) is “from 2 to 5 inclusive”: both ends are in:

Pythondata: the shop tables

It gives 9 products, as many as (prices >= 2) & (prices <= 5) in the NumPy lesson.

More precisely, if you are curious

between(2, 5, inclusive='neither') leaves out both ends, and 'left' and 'right' keep only one of them. The default is 'both'.

The idea

Text that contains: str.contains

“Show me all the breads.” The names include “White bread” and “Wholegrain bread”, but you cannot write == 'bread': no name is exactly “bread”. str.contains asks whether the text appears anywhere in the name:

Pythondata: the shop tables

.str opens the text methods for the whole column. You will see it in more detail in the lesson on types.

The search tells capitals from small letters. “cheese” finds “White cheese” and “Kashkaval cheese”, so the first number is 2. “Cheese banitsa” starts with a capital C and is left out. With case=False the letters are not told apart and you get 3.

The idea

The condition as text: query

Long masks with products['...'] everywhere get hard to read. query takes the condition as text, where the column names stand on their own:

Pythondata: the shop tables

In query you write and, or and not. The text is not computed by Python but by pandas, and pandas knows these are columns. Under 20 in stock and over €5 are 7 products.

A Python variable goes in with @: category_id in @food is the same as isin(food). That gives the five food products under 20 in stock, which you will turn into the list for the supplier.

1. limit = 10 holds the price threshold. Which line takes the products above it?

The idea

Sorting: sort_values and nlargest

sort_values('price') orders the rows by price, from small to large. With ascending=False it is the other way round. For “the top N” there is nlargest(N, column) and nsmallest:

Pythondata: the shop tables

The most expensive are the knife set (€39), the frying pan (€24.90) and the gift card (€20). nlargest(3, 'price') gives the same three in one line.

You can also sort by several columns: first by category, and within it by price. ascending is then a list, one for each column. The categories go up and the prices inside them go down: the banitsa (€2.50) is first in bread, the kashkaval (€9.50) in dairy.

More precisely, if you are curious

Sorting moves the rows but does not change their labels: the index in the output is 22, 21, 24… If you need the numbers 0, 1, 2 in the new order, add .reset_index(drop=True).

The idea

How many of each: value_counts

“How many orders were cancelled?” is a counting question. value_counts counts how many times each value appears in a column, the most frequent on top:

Pythondata: the shop tables

Of the 11,116 orders for the year, 10,254 were delivered and 657 cancelled. With normalize=True you get a share instead of a count: 0.059, that is 5.9% of orders were cancelled.

The same on the customer number tells you who ordered how many times:

Pythondata: the shop tables

1,390 customers have ordered. The most loyal, customer 1, ordered 121 times, and 224 customers ordered only once. The index of the result is the customer numbers, and the values are how many orders each one made.

Try it

The open orders

Orders with the status new, paid or shipped have not reached the customer yet. Put the table of them in open_orders.

Pythondata: the shop tables

Exercise

The list and the regular customers

Make the list for the supplier: the food products (categories 3, 4, 5 and 6) with stock under 20, sorted by stock from the smallest. With equal stock, the dearer product goes first. Put the table in restock.

Pythondata: the shop tables

How many customers ordered at least 20 times in the year? Put their number in regulars.

Pythondata: the shop tables

Test

Check yourself

1. Why is every condition in brackets in the filter (products["stock"] < 20) & (products["price"] < 5)?
2. What does products["price"].between(2, 5) give for a product that costs exactly €5?
3. food = [3, 4, 5, 6] holds the food categories. Why does ~products["category_id"].isin(food) also catch the gift card?
4. year["status"].value_counts() returned 5 rows. In what order are they?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·