owned this note
owned this note
Published
Linked with GitHub
# Numpy 4
---
title: Agenda
description:
duration: 300
card_type: cue_card
---
### Content
- **Shallow vs Deep Copy**
- `view()`
- `copy()`
- `copy.deepcopy()`
- **Array Splitting**
- `split()`
- `hsplit()`
- `vsplit()`
- **Array Stacking**
- `hstack()`
- `vstack()`
- `concatenate()`
- **Image Manipulation** (Post-lecture content)
---
title: Views vs Copies (Shallow vs Deep Copy)
description:
duration: 900
card_type: cue_card
---
## Views vs Copies (Shallow vs Deep Copy)
- Numpy **manages memory very efficiently**,
- which makes it really **useful while dealing with large datasets**.
#### But how does it manage memory so efficiently?
- Let's create some arrays to understand what's happening in memory while using numpy.
```
import numpy as np
```
Code
```python=
# We'll create a np array
a = np.arange(4)
a
```
> Output
array([0, 1, 2, 3])
Code
```python=
# Reshape array `a` and store in `b`
b = a.reshape(2, 2)
b
```
> Output
array([[0, 1],
[2, 3]])
Now we will make some changes to our original array `a`.
Code
```python=
a[0] = 100
a
```
> Output
array([100, 1, 2, 3])
**What will be values if we print array `b`?**
Code
```python=
b
```
> Output
array([[100, 1],
[ 2, 3]])
- **Array `b` got automatically updated**
**This is an example of numpy using "Shallow Copy" of data.**
What happens here?
- Numpy **re-uses data** as much as possible **instead of duplicating** it.
- This helps numpy to be efficient.
When we created `b=a.reshape(2,2)`
- Numpy **did NOT make a copy of `a` to store in `b`**, as we can clearly see.
- It is **using the same data as in `a`**.
- It **just looks different (reshaped)** in `b`.
- That is why, **any changes in `a` automatically gets reflected in `b`**.
**Now, let's see an example where Numpy will create a "Deep Copy" of data**
Code
```python=
a = np.arange(4)
a
```
> Output
array([0, 1, 2, 3])
Code
```python=
# Create `c`
c = a + 2
c
```
> Output
array([2, 3, 4, 5])
Code
```python=
# We make changes in `a`
a[0] = 100
a
```
> Output
array([100, 1, 2, 3])
Code
```python=
c
```
> Output
array([2, 3, 4, 5])
Code
```python=
np.shares_memory(a, c) # Deep Copy
```
> Output
False
**As we can see, `c` did not get affected on changing `a`.**
- Because it is an operation.
- A more **permanent change in data**.
- So, Numpy **had to create a separate copy for `c`** - i.e., **deep copy of array `a` for array `c`**.
#### Conclusion:
- Numpy is able to **use same data** for **simpler operations** like **reshape** $\rightarrow$ **Shallow Copy**.
- It creates a **copy of data** where operations make **more permanent changes** to data $\rightarrow$ **Deep Copy**.
---
title: np.shares_memory()
description:
duration: 900
card_type: cue_card
---
**Is there a way to check whether two arrays are sharing memory or not?**
- Yes, `np.shares_memory()` function
Code
```python=
a= np.arange(10)
a
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Code
```python=
b = a[::2]
b
```
> Output
array([0, 2, 4, 6, 8])
Code
```python=
np.shares_memory(a,b)
```
> Output
True
**Notice that Slicing creates shallow copies.**
Code
```python=
a[0] = 1000
b
```
> Output
array([1000, 2, 4, 6, 8])
Code
```python=
a = np.arange(6)
a
```
> Output
array([0, 1, 2, 3, 4, 5])
Code
```python=
b = a[a % 1 == 0]
b
```
> Output
array([0, 1, 2, 3, 4, 5])
Code
```python=
b[0] = 10
a[0]
```
> Output
0
Code
```python=
np.shares_memory(a,b)
```
> Output
False
**Note:**
- Shallow Copy $\rightarrow$ Reshaping, Slicing...
- Deep Copy $\rightarrow$ Arithmetic Operations, Masking...
```python=
a = np.arange(10)
```
Code
```python=
a_shallow_copy = a.view()
# Creates a shallow copy of `a`
np.shares_memory(a_shallow_copy, a)
```
> Output
True
Code
```python=
a_deep_copy = a.copy()
# Creates a deep copy of `a`
np.shares_memory(a_deep_copy, a)
```
> Output
False
---
title: Quiz-1
description: Quiz-1
duration: 60
card_type: quiz_card
---
# Question
```python=
a = [0,1,2,3,4,5]
b = a[a%1 == 0]
b[0] = 10
a[:2] = ?
```
# Choices
- [x] [0,1]
- [ ] [0,1,2]
- [ ] [10,1]
- [ ] [10,1,2]
---
title: .view()
description:
duration: 900
card_type: cue_card
---
#### `.view()`
- Returns view of the original array
- Any changes made in new array will be reflected in original array.
Documentation:
<https://numpy.org/doc/stable/reference/generated/numpy.ndarray.view.html>
Code
```python=
arr = np.arange(10)
arr
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Code
```python=
view_arr = arr.view()
view_arr
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Let's modify the content of `view_arr` and check whether it modified the original array as well.
Code
```python=
view_arr[4] = 420
view_arr
```
> Output
array([ 0, 1, 2, 3, 420, 5, 6, 7, 8, 9])
Code
```python=
arr
```
> Output
array([ 0, 1, 2, 3, 420, 5, 6, 7, 8, 9])
Code
```python=
np.shares_memory(arr, view_arr)
```
> Output
True
Notice that changes in view array are reflected in original array.
---
title: .copy()
description:
duration: 900
card_type: cue_card
---
#### `.copy()`
- Returns a copy of the array.
- Changes made in new array are not reflected in the original array.
Documentation (`.copy()`): https://numpy.org/doc/stable/reference/generated/numpy.ndarray.copy.html#numpy.ndarray.copy
Documentation: (`np.copy()`): https://numpy.org/doc/stable/reference/generated/numpy.copy.html
Code
```python=
arr = np.arange(10)
arr
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Code
```python=
copy_arr = arr.copy()
copy_arr
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Let's modify the content of `copy_arr` and check whether it modified the original array as well.
Code
```python=
copy_arr[3] = 45
copy_arr
```
> Output
array([ 0, 1, 2, 45, 4, 5, 6, 7, 8, 9])
Code
```python=
arr
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Code
```python=
np.shares_memory(arr, copy_arr)
```
> Output
False
otice that the content of original array were not modified as we changed our copy array.
##### `Instructor Note`:
- Cover the **object arrays** and **deepcopy()** function only if time permits.
- This has already been added as a post-read to the learner's dashboard.
### What are object arrays?
- Object arrays are basically array of any Python datatype.
Documentation: https://numpy.org/devdocs/reference/arrays.scalars.html#numpy.object
Code
```python=
arr = np.array([1, 'm', [1,2,3]], dtype = 'object')
arr
```
> Output
array([1, 'm', list([1, 2, 3])], dtype=object)
There is an exception to `.copy()`:
- **`.copy()` behaves as shallow copy when using `dtype='object'` array**.
- It will not copy object elements within arrays.
#### But arrays are supposed to be homogeous data. How is it storing data of various types?
Remember that everything is object in Python.
Just like Python list,
- The data actually **stored** in object arrays are **references to Python objects**, not the objects themselves.
Hence, their elements need not be of the same Python type.
**As every element in array is an object, therefore the dtype=object.**
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/065/263/original/img.png?1708017404" width="700" height="100">
\
Let's make a copy of object array and check whether it returns a shallow copy or deep copy.
Code
```python=
copy_arr = arr.copy()
copy_arr
```
> Output
array([1, 'm', list([1, 2, 3])], dtype=object)
Now, let's try to modify the list elements in `copy_arr`.
Code
```python=
copy_arr[2][0] = 999
copy_arr
```
> Output
array([1, 'm', list([999, 2, 3])], dtype=object)
Let's see if it changed the original array as well.
Code
```python=
arr
```
> Output
array([1, 'm', list([999, 2, 3])], dtype=object)
It did change the original array.
Hence, **`.copy()` will return shallow copy when copying elements of array in object array.**
Any change in the 2nd level elements of array will be reflected in original array as well.
So, how do we create deep copy then?
We can do so using `copy.deepcopy()` method.
#### `copy.deepcopy()`
- Returns the deep copy of an array.
Documentation: https://docs.python.org/3/library/copy.html#copy.deepcopy
```python=
import copy
```
Code
```python=
arr = np.array([1, 'm', [1,2,3]], dtype = 'object')
arr
```
> Output
array([1, 'm', list([1, 2, 3])], dtype=object)
Let's make a copy using `deepcopy()`.
Code
```python=
copy = copy.deepcopy(arr)
copy
```
> Output
array([1, 'm', list([1, 2, 3])], dtype=object)
Let's modify the array inside copy array.
Code
```python=
copy[2][0] = 999
copy
```
> Output
array([1, 'm', list([999, 2, 3])], dtype=object)
Code
```python=
arr
```
> Output
array([1, 'm', list([1, 2, 3])], dtype=object)
Notice that the changes in copy array didn't reflect back to original array.
`copy.deepcopy()` **returns deep copy of an array.**
---
title: Break & Doubt Resolution
description:
duration: 600
card_type: cue_card
---
### Break & Doubt Resolution
`Instructor Note:`
* Take this time (up to 5-10 mins) to give a short break to the learners.
* Meanwhile, you can ask the them to share their doubts (if any) regarding the topics covered so far.
---
title: Array Splitting
description:
duration: 1800
card_type: cue_card
---
## Splitting
In addition to reshaping and selecting subarrays, it is often necessary to split arrays into smaller arrays or merge arrays into bigger arrays.
#### `np.split()`
- Splits an array into multiple sub-arrays as views.
#### It takes an argument `indices_or_sections`.
- If `indices_or_sections` is an **integer, n**, the array will be **divided into n equal arrays along axis**.
- If such a split is not possible, an error is raised.
- If `indices_or_sections` is a **1-D array of sorted integers**, the entries indicate **where along axis the array is split**.
- If an index **exceeds the dimension of the array along axis**, an **empty sub-array is returned** correspondingly.
Code
```python=
x = np.arange(9)
x
```
> Output
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
Code
```python=
np.split(x, 3)
```
> Output
[array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8])]
**IMPORTANT REQUISITE**
- Number of elements in the array should be divisible by number of sections.
Code
```python=
b = np.arange(10)
np.split(b, 3)
```
> Output
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-30-5033f171e13f> in <cell line: 2>()
1 b = np.arange(10)
----> 2 np.split(b, 3)
/usr/local/lib/python3.10/dist-packages/numpy/core/overrides.py in split(*args, **kwargs)
/usr/local/lib/python3.10/dist-packages/numpy/lib/shape_base.py in split(ary, indices_or_sections, axis)
870 N = ary.shape[axis]
871 if N % sections:
--> 872 raise ValueError(
873 'array split does not result in an equal division') from None
874 return array_split(ary, indices_or_sections, axis)
ValueError: array split does not result in an equal division
Code
```python=
np.split(b[0:-1], 3)
```
> Output
[array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8])]
Code
```python=
# Splitting on the basis of exact indices
c = np.arange(16)
np.split(c, [3, 5, 6])
```
> Output
[array([0, 1, 2]),
array([3, 4]),
array([5]),
array([ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])]
#### `np.hsplit()`
- Splits an array into multiple sub-arrays **horizontally (column-wise)**.
Code
```python=
x = np.arange(16.0).reshape(4, 4)
x
```
> Output
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])
Think of it this way:
- There are 2 axis to a 2D array
1. **1st axis - Vertical axis**
2. **2nd axis - Horizontal axis**
**Along which axis are we splitting the array?**
- The split we want happens across the **2nd axis (Horizontal axis)**
- That is why we use `hsplit()`
**So, try to think in terms of "whether the operation is happening along vertical axis or horizontal axis".**
- We are splitting the horizontal axis in this case.
Code
```python=
np.hsplit(x, 2)
```
> Output
[array([[ 0., 1.],
[ 4., 5.],
[ 8., 9.],
[12., 13.]]),
array([[ 2., 3.],
[ 6., 7.],
[10., 11.],
[14., 15.]])]
Code
```python=
np.hsplit(x, np.array([3, 6]))
```
> Output
[array([[ 0., 1., 2.],
[ 4., 5., 6.],
[ 8., 9., 10.],
[12., 13., 14.]]),
array([[ 3.],
[ 7.],
[11.],
[15.]]),
array([], shape=(4, 0), dtype=float64)]
#### `np.vsplit()`
- Splits an array into multiple sub-arrays **vertically (row-wise)**.
Code
```python=
x = np.arange(16.0).reshape(4, 4)
x
```
> Output
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])
**Now, along which axis are we splitting the array?**
- The split we want happens across the **1st axis (Vertical axis)**
- That is why we use `vsplit()`
**Again, always try to think in terms of "whether the operation is happening along vertical axis or horizontal axis".**
- We are splitting the vertical axis in this case.
Code
```python=
np.vsplit(x, 2)
```
> Output
[array([[0., 1., 2., 3.],
[4., 5., 6., 7.]]),
array([[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])]
Code
```python=
np.vsplit(x, np.array([3]))
```
> Output
[array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
array([[12., 13., 14., 15.]])]
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/735/original/hvsp1.png?1698041133" width="600" height="400">
---
title: Array Stacking
description:
duration: 1200
card_type: cue_card
---
## Stacking
#### `np.vstack()`
- Stacks a list of arrays **vertically (along axis 0 or 1st axis)**.
- For **example**, **given a list of row vectors, appends the rows to form a matrix**.
Code
```python=
a = np.arange(1, 5)
b = np.arange(2, 6)
c = np.arange(3, 7)
np.vstack([b, c, a])
```
> Output
array([[2, 3, 4, 5],
[3, 4, 5, 6],
[1, 2, 3, 4]])
Code
```python=
a = np.arange(1, 5)
b = np.arange(2, 4)
c = np.arange(3, 10)
np.vstack([b, c, a])
```
> Output
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-40-5148cb6ebc5f> in <cell line: 1>()
----> 1 np.vstack([b, c, a])
/usr/local/lib/python3.10/dist-packages/numpy/core/overrides.py in vstack(*args, **kwargs)
/usr/local/lib/python3.10/dist-packages/numpy/core/shape_base.py in vstack(tup)
280 if not isinstance(arrs, list):
281 arrs = [arrs]
--> 282 return _nx.concatenate(arrs, 0)
283
284
/usr/local/lib/python3.10/dist-packages/numpy/core/overrides.py in concatenate(*args, **kwargs)
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 2 and the array at index 1 has size 7
#### `np.vstack()`
- Stacks a list of arrays **horizontally (along axis 1 or 2nd axis)**.
Code
```python=
a = np.arange(5).reshape(5, 1)
a
```
> Output
array([[0],
[1],
[2],
[3],
[4]])
Code
```python=
b = np.arange(15).reshape(5, 3)
b
```
> Output
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]])
Code
```python=
np.hstack([a, b])
```
> Output
array([[ 0, 0, 1, 2],
[ 1, 3, 4, 5],
[ 2, 6, 7, 8],
[ 3, 9, 10, 11],
[ 4, 12, 13, 14]])
---
title: Quiz-2
description: Quiz-2
duration: 60
card_type: quiz_card
---
# Question
What will be the output of following code?
```python=
a = np.array([[1], [2], [3]])
b = np.array([[4], [5], [6]])
np.vstack((a, b))
```
```
A. array([1, 2, 3, 4, 5, 6])
B. array([[1, 4],
[2, 5],
[3, 6]])
C. array([[1],
[2],
[3],
[4],
[5],
[6]])
```
# Choices
- [ ] A
- [ ] B
- [x] C
---
title: Quiz-3
description: Quiz-3
duration: 60
card_type: quiz_card
---
# Question
What will be the output of following code?
```python=
a = np.array([[1], [2], [3]])
b = np.array([[4], [5], [6]])
np.hstack((a, b))
```
```
A. [[1]
[2]
[3]
[4]
[5]
[6]]
B. [[1 2]
[3 4]
[5 6]]
C. [[4 1]
[5 2]
[6 3]]
D. [[1 4]
[2 5]
[3 6]]
```
# Choices
- [ ] A
- [ ] B
- [ ] C
- [x] D
---
title: np.concatenate()
description:
duration: 1200
card_type: cue_card
---
#### `np.concatenate()`
- Can perform both `vstack` and `hstack`
- Creates a new array by appending arrays after each other, along a given axis.
Provides similar functionality, but it takes a **keyword argument `axis`** that specifies the **axis along which the arrays are to be concatenated**.
The input array to `concatenate()` needs to be of dimensions atleast equal to the dimensions of output array.
Code
```python=
a = np.array([1,2,3])
a
```
> Output
array([1, 2, 3])
Code
```python=
b = np.array([[1,2,3], [4,5,6]])
```
> Output
array([[1, 2, 3],
[4, 5, 6]])
Code
```python=
np.concatenate([a, b], axis=0)
```
> Output
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-47-1a93c4fe21df> in <cell line: 1>()
----> 1 np.concatenate([a, b], axis = 0)
/usr/local/lib/python3.10/dist-packages/numpy/core/overrides.py in concatenate(*args, **kwargs)
ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)
**`concatenate()` can only work if both `a` and `b` have the same number of dimensions**
Code
```python=
a = np.array([[1,2,3]])
b = np.array([[1,2,3], [4,5,6]])
np.concatenate([a, b], axis = 0) # axis=0 -> vstack
```
> Output
array([[1, 2, 3],
[1, 2, 3],
[4, 5, 6]])
Code
```python=
a = np.arange(6).reshape(3, 2)
b = np.arange(9).reshape(3, 3)
np.concatenate([a, b], axis = 1) # axis=1 -> hstack
```
> Output
array([[0, 1, 0, 1, 2],
[2, 3, 3, 4, 5],
[4, 5, 6, 7, 8]])
Code
```python=
a = np.array([[1,2], [3,4]])
b = np.array([[5,6,7,8]])
np.concatenate([a, b], axis = None)
# axis=None joins and converts to 1D
```
> Output
array([1, 2, 3, 4, 5, 6, 7, 8])
\
**Question:** What will be the output of this?
```python=
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
np.concatenate((a, b), axis=0)
```
Code
```python=
a = np.array([[1, 2], [3, 4]])
a
```
> Output
array([[1, 2],
[3, 4]])
Code
```python=
b = np.array([[5, 6]])
b
```
> Output
array([[5, 6]])
Code
```python=
np.concatenate((a, b), axis=0)
```
> Output
array([[1, 2],
[3, 4],
[5, 6]])
**How did it work?**
- Dimensions of `a` is $2\times2$
**What is the dimensions of `b` ?**
- 1-D array ?? - **NO**
- Look carefully!!
- **`b` is a 2-D array of dimensions $1\times2$**
**`axis = 0` $\rightarrow$ It's a vertical axis**
- So, **changes will happen along vertical axis**
- So, **`b` gets concatenated below `a`**
\
**Question:** What will be the result of this concatenation operation?
```python=
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
np.concatenate((a, b), axis=1)
```
Code
```python=
a = np.array([[1, 2], [3, 4]])
a
```
> Output
array([[1, 2, 5],
[3, 4, 6]])
Code
```python=
b = np.array([[5, 6]])
b
```
> Output
array([[5, 6]])
Code
```python=
np.concatenate((a, b.T), axis=1)
```
> Output
array([[1, 2, 5],
[3, 4, 6]])
**What happened here?**
- **Dimensions of `a`** is again $2\times2$
- **Dimensions of `b`** is again $1\times2$
- So, **Dimensions of `b.T`** will be $2\times1$
**`axis = 1`** $\rightarrow$ It's a horizontal axis
- So, **changes will happen along horizontal axis**
- So, **`b.T` gets concatenated horizontally to `a`**
### Extra-reading material
- [**Object arrays**](https://colab.research.google.com/drive/1X-IfuzHoE27IG37wCVjv_AooCXQG3Gnr?usp=sharing)
- [**Image Manipulation**](https://colab.research.google.com/drive/1SkyA5iF7UTDR8VFhCWEy525XlcaJ8YdI#scrollTo=VBD8uhb9M63e)
---
title: Unlock Assignment & ask learner to solve in live class
description:
duration: 1800
card_type: cue_card
---
* <span style=“color:skyblue”>Unlock the assignment for learners</span> by clicking the **“question mark”** button on the top bar.
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/078/685/original/Screenshot_2024-06-19_at_7.17.12_PM.png?1718804854" width=200 />
* If you face any difficulties using this feature, please refer to this video on how to unlock assignments.
* <span style=“color:red”>**Note:** The following video is strictly for instructor reference only. [VIDEO LINK](https://www.loom.com/share/15672134598f4b4c93475beda227fb3d?sid=4fb31191-ae8c-4b18-bf81-468d2ffd9bd4)</span>
### Conducting a Live Assignment Solution Session:
1. Once you unlock the assignments, ask if anyone in the class would like to solve a question live by sharing their screen.
2. Select a learner and grant permission by navigating to <span style=“color:skyblue”>**Settings > Admin > Unmuted Audience Can Share**, then select **Audio, Video, and Screen**.</span>
<img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/111/113/original/image.png?1740484517" width=400 />
3. Allow the selected learner to share their screen and guide them through solving the question live.
4. Engage with both the learner sharing the screen and other students in the class to foster an interactive learning experience.
### Practice Coding Question(s)
You can pick the following question and solve it during the lecture itself.
This will help the learners to get familiar with the problem solving process and motivate them to solve the assignments.
<span style="background-color: pink;">Make sure to start the doubt session before you solve this question.</span>
Q. https://www.scaler.com/hire/test/problem/101483/ - Split and cycle