NumPy-2

NumPy Array Copy vs View

The main difference between a copy and a view of an array is that the copy is a new array, and the view is just a view of the original array.

The copy owns the data and any changes made to the copy will not affect original array, and any changes made to the original array will not affect the copy.

The view does not own the data and any changes made to the view will affect the original array, and any changes made to the original array will affect the view.

Copy

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
x = arr.copy()
arr[0] = 42

print(arr)
>>> [42  2  3  4  5]
print(x)
>>> [1 2 3 4 5]

The copy SHOULD NOT be affected by the changes made to the original array.

View

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
x = arr.view()
arr[0] = 42

print(arr)
>>> [42  2  3  4  5]
print(x)
>>> [42  2  3  4  5]

The view SHOULD be affected by the changes made to the original array.
The original array SHOULD be affected by the changes made to the view.

Check if Array Owns it's Data

As mentioned above, copies owns the data, and views does not own the data, but how can we check this?
Every NumPy array has the attribute base that returns None if the array owns the data.
Otherwise, the base attribute refers to the original object.

Example

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

x = arr.copy()
y = arr.view()

print(x.base)
>>> None
print(y.base)
>>> [1 2 3 4 5]

NumPy Array Shape

The shape of an array is the number of elements in each dimension.
NumPy arrays have an attribute called shape that returns a tuple with each index having the number of corresponding elements.

2-D Array

import numpy as np
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

print(arr.shape)
>>> (2,4)

5-D Array

import numpy as np

arr = np.array([1, 2, 3, 4], ndmin=5)

print(arr)
>>> [[[[[1 2 3 4]]]]]
print(arr.shape)
>>> (1, 1, 1, 1, 4)

NumPy Array Reshaping

Reshaping means changing the shape of an array.
The shape of an array is the number of elements in each dimension.
By reshaping we can add or remove dimensions or change number of elements in each dimension.

Reshape from 1-D to 2-D

import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(4, 3)

print(newarr)
>>> [[ 1  2  3]
    [ 4  5  6]
    [ 7  8  9]
    [10 11 12]]

Reshape from 1-D to 3-D

import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(2, 3, 2)

print(newarr)
>>> [[[ 1  2]
    [ 3  4]
    [ 5  6]]

    [[ 7  8]
    [ 9 10]
    [11 12]]]

Note

"We can reshape the array into any shape as long as the elements required for reshaping are equal in both shapes."

Returns Copy or View?

Example

import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
print(arr.reshape(2, 4).base)
>>> [1 2 3 4 5 6 7 8]

The example above returns the original array, so it is a view.

Unknown Dimension

We are allowed to have one "unknown" dimension.
Meaning that we do not have to specify an exact number for one of the dimensions in the reshape method.
Pass -1 as the value, and NumPy will calculate this number.

Example

import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
newarr = arr.reshape(2, -1)
print(newarr)
>>> [[[1 2]
    [3 4]]

    [[5 6]
    [7 8]]]

We can not pass -1 to more than one dimension.

Flattening the arrays

Flattening array means converting a multidimensional array into a 1D array.
We can use reshape(-1) to do this.

Example

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])
newarr = arr.reshape(-1)
print(newarr)
>>> [1 2 3 4 5 6]

NumPy Array Iterating

In basic for loops, iterating through each scalar of an array we need to use n for loops which can be difficult to write for arrays with very high dimensionality.

Iterating Arrays Using nditer()

The function nditer() is a helping function that can be used from very basic to very advanced iterations.

Iterating on Each Scalar Element

Example

import numpy as np
arr = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
for x in np.nditer(arr):
print(x)

>>> 1
    2
    3
    4
    5
    6
    7
    8

Iterating With Different Step Size

Example

import numpy as np

arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

for x in np.nditer(arr[:, ::2]):
print(x)

>>> 1
    3
    5
    7

Enumerated Iteration Using ndenumerate()

Enumeration means mentioning sequence number of somethings one by one.
Sometimes we require corresponding index of the element while iterating, the ndenumerate() method can be used for those usecases.

For 1-D Array

import numpy as np

arr = np.array([1, 2, 3])

for idx, x in np.ndenumerate(arr):
print(idx, x)

>>> (0,) 1
    (1,) 2
    (2,) 3

For 2-D Array

import numpy as np

arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

for idx, x in np.ndenumerate(arr):
print(idx, x)

>>> (0, 0) 1
    (0, 1) 2
    (0, 2) 3
    (0, 3) 4
    (1, 0) 5
    (1, 1) 6
    (1, 2) 7
    (1, 3) 8

NumPy Joining Array

Joining means putting contents of two or more arrays in a single array.
In SQL we join tables based on a key, whereas in NumPy we join arrays by axes.
We pass a sequence of arrays that we want to join to the concatenate() function, along with the axis. If axis is not explicitly passed, it is taken as 0.
(axis-0 represents rows and axis-1 represents columns)

Join two 1-D Arrays

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.concatenate((arr1, arr2))

print(arr)
>>> [1,2,3,4,5,6]

Join two 2-D Arrays

import numpy as np

arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
arr = np.concatenate((arr1, arr2), axis=1)

print(arr)
>>> [[1 2 5 6]
    [3 4 7 8]]

Joining Arrays Using Stack Functions

Stacking is same as concatenation, the only difference is that stacking is done along a new axis.
We can concatenate two 1-D arrays along the second axis which would result in putting them one over the other, ie. stacking.
We pass a sequence of arrays that we want to join to the concatenate() method along with the axis. If axis is not explicitly passed it is taken as 0.

Example

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.stack((arr1, arr2), axis=1)

print(arr)
>>> [[1 4]
    [2 5]
    [3 6]]

Row Wise Stacking

hstack() to stack along rows.

Example

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.hstack((arr1, arr2))

print(arr)
>>> [1,2,3,4,5,6]

Column Wise stacking

vstack() to stack along columns.

Example

import numpy as np

arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.vstack((arr1, arr2))

print(arr)
>>> [[1 2 3]
    [4 5 6]]