Speed is always a concern for developers — especially for data-savvy work.
The ability to iterate is the basis of all automation and scaling. The first and foremost choice for all of us is a for-loop. It’s excellent, simple, and flexible. Yet, they are not built for scaling up to massive datasets.
This is where vectorization comes in. When you do extensive data processing in for-loops, consider vectorization. And Numpy comes in handy there.
This post explains how fast NumPy operations are compared to for-loops.
Related: How to Speed up Python Data Pipelines up to 91X
Comparing For-loops with NumPy
Let’s take a simple summation operation. We have to sum up all the elements in a list.
The sum is an inbuilt operation in Python you can use over a list of numbers. But let’s assume there isn’t one, and you need to implement it.
Any programmer would opt to iterate over the list and add the numbers to a variable. But experienced developers know the limitations and go for an optimized version.
Related: How to Identify Memory Leakage in Your Python program
Here are both the list and NumPy versions of our summation. We create an array with a million random numbers between 0 and 100. Then we use both methods and record the execution times.
import numpy as np import timeit def sum_with_for_loop(array) -> int: sum = 0 for i in array: sum += i return sum def sum_with_np_sum(array) -> int: return np.sum(array) if __name__ == "__main__": array = np.random.randint(0, 100, 1000000) # print time for for loop print(timeit.timeit(lambda: sum_with_for_loop(array), number=100)) # print time for np.sum print(timeit.timeit(lambda: sum_with_np_sum(array), number=100))
Let’s run the program and see what we get. The output may look like the one below.
$ python main.py Summation time with for-loop: 14.793345853999199 Summation time with np.sum: 0.1294808290003857
The NumPy version is faster. It took roughly one-hundredth of the time for-loops took.
More examples of using Numpy to Speed up calculations
NumPy is used heavily for numerical computation. That said if you’re working with colossal dataset vectorization and the use of NumPy is unavoidable.
Most machine learning libraries use NumPy under the hood to optimize algorithms. If you’ve ever created a scikit learn-to model, you’d have used NumPy already.
Here are some more examples you’d frequently use when dealing with extensive numerical data.
Sum products in NumPy vs. Lists
It’s a popular numerical computation you can even use in Excel. Let’s measure the performances of lists and NumPy versions.
The following code multiplies each element of an array with a corresponding element in another array. Finally, we sum up all the individual products.
import numpy as np import timeit def sum_product_with_for_loop(array1, array2) -> int: sum = 0 for i, j in zip(array1, array2): sum += i * j return sum def sum_product_with_np_sum(array1, array2) -> int: return np.sum(array1 * array2) if __name__ == "__main__": array1 = np.random.randint(0, 100, 1000000) array2 = np.random.randint(0, 100, 1000000) # Print the time taken to execute the function print(timeit.timeit(lambda: sum_product_with_for_loop(array1, array2), number=100)) print(timeit.timeit(lambda: sum_product_with_np_sum(array1, array2), number=100))
Here’s the output of the above code:
$ python main.py Sum of products with for loop: 26.099454337999987 Sum of products with np.sum: 0.28206900699990456
Once again, the NumPy version was about 100 times faster than iterating over a list.
Matrix multiplication performance of NumPy and lists.
Matrix multiplication is an extended version of sum-product. It involves not a single array but an array of arrays.
Matrix multiplication is also very common when implementing algorithms that involve a lot of data. Here’s the benchmark.
import numpy as np import timeit def matrix_muliplication_with_np(matrix1, matrix2): return np.matmul(matrix1, matrix2) def matrix_multiplication_with_for_loop(matrix1, matrix2): result = np.zeros((len(matrix1), len(matrix2))) for i in range(len(matrix1)): for k in range(len(matrix2)): for j in range(len(matrix2)): result[i][j] += matrix1[i][k] * matrix2[k][j] return result if __name__ == "__main__": matrix1 = np.random.randint(1, 10, (1000, 1000)) matrix2 = np.random.randint(1, 10, (1000, 1000)) print( "Matrix multiplication with numpy: ", timeit.timeit(lambda: matrix_muliplication_with_np(matrix1, matrix2), number=1), ) print( "Matrix multiplication with for loop: ", timeit.timeit( lambda: matrix_multiplication_with_for_loop(matrix1, matrix2), number=1 ), )
$ python main.py Matrix multiplication with for loop: 1597.9121425140002 Matrix multiplication with numpy: 2.8506258010002057
The results of using NumPy were profound. Our vectorized version ran more than 500 times faster.
NumPy’s benefits are more prominent as the size and dimensions of arrays grow.
Why is NumPy faster than lists?
Simple; They are designed for different purposes.
NumPy’s role is to provide an optimized interface for numerical computation. A Python list, however, is only a collection of objects.
A NumPy array allows only homogeneous data types. Thus the NumPy operations don’t have to worry about types before every step of an algorithm. This is where we gain a lot of speed — quick wins.
Also, in NumPy, the whole array, not individual elements, is an object known as densely packed. Thus it takes much less memory.
Further, NumPy operations are (primarily) implemented using C, not in Python itself.
Related: Challenging Cython — the Python Module for High-Performance Computing.
Lists in Python are not more than an object store. Individual objects take up space, and you’ll quickly need more memory to process them. Also, lists could accommodate different types of objects in it. But on the downside, you’d have to do element-wise-type checks on every operation. This makes it costly.
This post encourages you to convert your lists to NumPy arrays and use vectorized operations to speed executions.
It’s natural for people to use for-loops over a list because it’s straightforward. But if it involves a lot of numbers, it’s not the optimal way. To understand it better, we’ve compared the performances of trivial operations such as summation, sum-product, and matrix multiplication. In all cases, NumPy performed far better than lists.
For-loops, too have their place in programming. The rule of thumb is to use them when your data structures are more complex and have fewer items to iterate.
You may be better off summing a few hundred numbers without NumPy. Also, if you have to do more work than numerical computation in each iteration, NumPy isn’t your option.
Related: How to Serve Massive Computations Using Python Web Apps.
Thanks for reading, friend! Say Hi to me on LinkedIn, Twitter, and Medium.
Not a Medium member yet? Please use this link to become a member because, at no extra cost for you, I earn a small commission referring.