Skip to content
wannacode

Vectorization

In short: Vectorization in NumPy is computing on the whole array at once, with no loop: arithmetic, comparisons, masks with & and |, and choices with np.where.

Problem

A loop for every question

The manager wants the prices with VAT. Right after that they ask how many products cost over €5. With a list every question is a new loop:

Pythondata: the shop tables

It works: €1.20 becomes €1.44, and 8 products are over €5. But for every next question you write one more for, one more empty variable and one more counter. The question itself gets lost between the lines.

The idea

Maths on the whole array

With an array you write the calculation once, for all the numbers:

Pythondata: the shop tables

prices * 1.2 multiplies each of the 25 prices. The loop is inside NumPy, in C. This is called a vectorized operation: it works on the whole array, not on a single number. The knife set at €39 becomes €46.80.

+, -, *, / and ** work this way, and so do functions like np.round(array, 2), which rounds every number to 2 places.

The idea

Array by array

Two arrays of the same length are worked out number by number: the first with the first, the second with the second. In order_items.csv every line has a quantity and a unit price. The value of the line is one times the other:

Pythondata: the shop tables

The first line is 4 units at €7.90, that is €31.60. Without a single loop you have the value of all 152 lines. .sum() adds them up: €1,963.50. We get to summaries like sum in lesson 3.

Try it

What the stock is worth

Put in value the value of the stock for every product: its price times the number in stock.

Pythondata: the shop tables

The idea

Comparison and mask

A comparison is vectorized too. prices > 5 does not give one True or False, but one for every price:

Pythondata: the shop tables

An array of True and False like this is called a mask. Put in square brackets, it keeps only the places with True. And .sum() of a mask counts the Trues, because in a sum True is 1 and False is 0:

Pythondata: the shop tables

8 prices are over €5: the same answer as with the loop, on one line. The mask from the stock also works on another array of the same length: the names of the sold-out products are the banitsa and the cucumbers.

The idea

Two conditions: &, | and ~

The prices from €2 to €5 are two conditions together. In plain Python you would write and. With arrays and does not work:

Pythondata: the shop tables

and asks “is this whole thing true?” and wants one True or False. The array has 25. NumPy does not guess whether you mean “all” or “at least one”, so it stops with an error.

For masks there are three signs that work number by number:

  • & is “and”: both conditions are true;
  • | is “or”: at least one is true;
  • ~ is “not”: it flips the mask.
Pythondata: the shop tables

9 prices are from €2 to €5. Under €1 or over €20 are €0.80, €0.90, €24.90 and €39. The prices of the products that are not in stock are €2.50 and €2.80.

See it

The bracket trap

Every condition around & and | is in brackets. If you leave them out, Python works out & before >= and <=, that is, it starts with 2 & prices:

Pythondata: the shop tables

& between a number and decimal prices makes no sense, so you get a TypeError about bitwise_and. The rule is: every condition in brackets, (prices >= 2) & (prices <= 5).

More precisely, if you are curious

In Python &, | and ~ are bitwise operations on whole numbers. That is why they bind tighter than comparisons: 2 & 6 > 1 is (2 & 6) > 1. NumPy took them for masks because and, or and not cannot be redefined for arrays: they always want one True or False. You will see the same with filters in pandas.

Try it

Cheap and in stock

How many products cost under €3 and are in stock (stock over 0)? Put the count in count.

Pythondata: the shop tables

The idea

One or the other: np.where

A mask chooses which numbers stay. Sometimes you want all of them, but different depending on the condition. np.where(mask, if_yes, if_no) takes one or the other for every place:

Pythondata: the shop tables

Where the stock is 0 you get “sold out”, otherwise “in stock”. In place of the text you can also have arrays: np.where(prices > 10, prices * 0.9, prices) cuts only the expensive prices by 10%.

Exercise

The price list with VAT

Back to the manager: the prices with VAT and a promotion.

Put in with_vat the prices with VAT (times 1.2), rounded to 2 places. Put in over_10 how many of them are over €10.

Pythondata: the shop tables

Promotion: the products over €10 are 10% cheaper, and the others keep the old price. Put the whole new price list (25 prices) in promo.

Pythondata: the shop tables

Test

Check yourself

1. What does np.array([2, 3]) * np.array([1.5, 4.0]) give?
2. What does (prices > 5).sum() give?
3. Which line takes the prices from €2 to €5?
4. Why is the discount for the expensive products np.where(prices > 10, prices * 0.9, prices) and not prices[prices > 10] * 0.9?

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