One of the primary reasons NumPy is so effective for numerical computation is its ability to perform batch operations on data without requiring Python for
loops. These operations are applied element-wise, meaning the operation is performed independently on each corresponding element in the arrays.
Let's start with basic arithmetic: addition, subtraction, multiplication, and division. When you use the standard arithmetic operators (+
, -
, *
, /
) on NumPy arrays, NumPy automatically applies the operation to every element.
If you have two arrays of the same shape, you can perform arithmetic operations between them directly. The operation will be applied element by element.
Consider two simple arrays:
import numpy as np
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([10, 20, 30, 40])
print(f"arr1: {arr1}")
print(f"arr2: {arr2}")
Output:
arr1: [1 2 3 4]
arr2: [10 20 30 40]
Now, let's perform some arithmetic:
Addition:
sum_arr = arr1 + arr2
print(f"arr1 + arr2: {sum_arr}")
Output:
arr1 + arr2: [11 22 33 44]
Here, the first element of arr1
(1) is added to the first element of arr2
(10), the second element of arr1
(2) is added to the second element of arr2
(20), and so on.
Subtraction:
diff_arr = arr2 - arr1
print(f"arr2 - arr1: {diff_arr}")
Output:
arr2 - arr1: [ 9 18 27 36]
Multiplication:
prod_arr = arr1 * arr2
print(f"arr1 * arr2: {prod_arr}")
Output:
arr1 * arr2: [ 10 40 90 160]
Note: This is element-wise multiplication, not matrix multiplication. For matrix multiplication, you would use the
@
operator or thenp.dot()
function.
Division:
div_arr = arr2 / arr1
print(f"arr2 / arr1: {div_arr}")
Output:
arr2 / arr1: [10. 10. 10. 10.]
Notice that even though arr1
and arr2
contained integers, the division resulted in floating-point numbers. NumPy typically promotes the result to a floating-point type for division to maintain precision.
These operations work similarly for multi-dimensional arrays, as long as their shapes match:
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])
print(f"mat1:\n{mat1}\n")
print(f"mat2:\n{mat2}\n")
prod_mat = mat1 * mat2
print(f"mat1 * mat2:\n{prod_mat}")
Output:
mat1:
[[1 2]
[3 4]]
mat2:
[[5 6]
[7 8]]
mat1 * mat2:
[[ 5 12]
[21 32]]
NumPy also allows you to perform arithmetic operations between an array and a single number (a scalar). In this case, NumPy applies the operation between the scalar and every element in the array. This is a simple form of broadcasting, a concept we'll explore more formally later.
arr = np.array([1, 2, 3, 4])
scalar = 10
print(f"Array: {arr}")
print(f"Scalar: {scalar}\n")
add_scalar = arr + scalar
print(f"arr + scalar: {add_scalar}")
mul_scalar = arr * scalar
print(f"arr * scalar: {mul_scalar}")
pow_scalar = arr ** 2 # Element-wise squaring
print(f"arr ** 2: {pow_scalar}")
Output:
Array: [1 2 3 4]
Scalar: 10
arr + scalar: [11 12 13 14]
arr * scalar: [10 20 30 40]
arr ** 2: [ 1 4 9 16]
It's important to remember that arithmetic operations on NumPy arrays produce new arrays containing the results. The original arrays remain unchanged.
arr_a = np.array([5, 10, 15])
arr_b = np.array([1, 2, 3])
print(f"Original arr_a: {arr_a}")
print(f"Original arr_b: {arr_b}\n")
result = arr_a / arr_b
print(f"Result of division: {result}")
print(f"arr_a after division: {arr_a}") # Unchanged
print(f"arr_b after division: {arr_b}") # Unchanged
Output:
Original arr_a: [ 5 10 15]
Original arr_b: [1 2 3]
Result of division: [5. 5. 5.]
arr_a after division: [ 5 10 15]
arr_b after division: [1 2 3]
If you want to modify an array in place, you can use augmented assignment operators like +=
, -=
, *=
, /=
, though this is less common and requires careful consideration, especially regarding data types.
arr_a = np.array([5.0, 10.0, 15.0]) # Use floats to avoid type issues with division
arr_b = np.array([1.0, 2.0, 3.0])
print(f"arr_a before in-place division: {arr_a}")
arr_a /= arr_b # Modify arr_a in-place
print(f"arr_a after in-place division: {arr_a}")
Output:
arr_a before in-place division: [ 5. 10. 15.]
arr_a after in-place division: [5. 5. 5.]
These basic arithmetic operations form the bedrock of numerical computations in NumPy. Their element-wise application and ability to work with scalars provide a concise and efficient way to perform calculations compared to writing explicit loops in Python. Next, we'll look at Universal Functions (ufuncs), which expand on this capability for a wider range of mathematical operations.
© 2025 ApX Machine Learning