4c473de95c
Signed-off-by: David Rotermund <54365609+davrot@users.noreply.github.com>
192 lines
4.5 KiB
Markdown
192 lines
4.5 KiB
Markdown
# Slices and views
|
||
{:.no_toc}
|
||
|
||
<nav markdown="1" class="toc-class">
|
||
* TOC
|
||
{:toc}
|
||
</nav>
|
||
|
||
## The goal
|
||
|
||
Sometimes we want to use or see only a part of the matrix. This can be done via slices and views
|
||
|
||
Questions to [David Rotermund](mailto:davrot@uni-bremen.de)
|
||
|
||
## 1-d slices
|
||
|
||
We assume N as the number of elements and 1d:
|
||
|
||
* A valid index starts at **0** and runs until N-1
|
||
* [start:stop:step]
|
||
start = 1, stop=N, step=1 -> this results in the sequence 1,2,3,...,(N-1)
|
||
* [start:stop:1] can be shortened to [start:stop]
|
||
* [0:stop] can be shortened to [:stop]
|
||
* [start:N] can be shortened to [start:]
|
||
* B = A[:] or B = A[...] gives you a view of A. B has the same shape and size of A.
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.arange(0, 10)
|
||
print(a[1:10:1]) # -> [1 2 3 4 5 6 7 8 9]
|
||
print(a[3:7:2]) # -> [3 5]
|
||
print(a[3:6]) # -> [3 4 5]
|
||
print(a[:6]) # -> [0 1 2 3 4 5]
|
||
print(a[5:]) # -> [5 6 7 8 9]
|
||
print(a[:]) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[...]) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[:9999]) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[9999:]) # ->[]
|
||
```
|
||
|
||
* Negative values for start and stop are understood as N-\|start\| and N-\|stop\|
|
||
* N-1 is the last valid index.
|
||
* Thus A[-1] gives us the last element of A.
|
||
|
||
Extracting a value based on a negative index:
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.arange(0, 10)
|
||
print(a[-1]) # -> 9
|
||
print(a[-2]) # -> 8
|
||
print(a[-9]) # -> 1
|
||
print(a[-10]) # -> 0
|
||
print(a[-11]) # IndexError: index -11 is out of bounds for axis 0 with size 10
|
||
```
|
||
|
||
Extracting a slice based on a negative stop point:
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.arange(0, 10)
|
||
print(a) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[:-1]) # -> [0 1 2 3 4 5 6 7 8]
|
||
print(a[:-5]) # -> [0 1 2 3 4]
|
||
print(a[:-8]) # -> [0 1]
|
||
print(a[:-11]) # -> []
|
||
print(a[:-12]) # -> []
|
||
print(a[:-999]) # -> []
|
||
```
|
||
|
||
Extracting a slice based on a negative start point:
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.arange(0, 10)
|
||
print(a) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[-3:-1]) # -> [7 8]
|
||
print(a[-1:-8]) # -> []
|
||
print(a[-9999:]) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
```
|
||
|
||
Negative step sizes:
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.arange(0, 10)
|
||
print(a) # -> [0 1 2 3 4 5 6 7 8 9]
|
||
print(a[::-1]) # -> [9 8 7 6 5 4 3 2 1 0]
|
||
print(a[4:-2:-1]) # -> []
|
||
print(a[-1:5:-1]) # -> [9 8 7 6]
|
||
```
|
||
|
||
### [... (Ellipsis)](https://docs.python.org/dev/library/constants.html#Ellipsis)
|
||
|
||
> The same as the ellipsis literal “...”. Special value used mostly in conjunction with extended slicing syntax for user-defined container data types. Ellipsis is the sole instance of the types.EllipsisType type.
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.empty((2, 3, 4, 5, 6, 7, 8))
|
||
print(a.shape) # -> (2, 3, 4, 5, 6, 7, 8)
|
||
print(a[..., 1:2].shape) # -> (2, 3, 4, 5, 6, 7, 1)
|
||
print(a[:, :, 1:2, ...].shape) # -> (2, 3, 1, 5, 6, 7, 8)
|
||
print(a[0, ...].shape) # -> (3, 4, 5, 6, 7, 8)
|
||
print(a[0, ..., 0].shape) # -> (3, 4, 5, 6, 7)
|
||
```
|
||
|
||
## Views
|
||
|
||
What does **view** mean? It means that two objects share the same memory.
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.zeros((2, 3))
|
||
b=a
|
||
print(a)
|
||
print()
|
||
b[0,0] = 1
|
||
|
||
print(a)
|
||
```
|
||
|
||
Output
|
||
|
||
```python
|
||
[[0. 0. 0.]
|
||
[0. 0. 0.]]
|
||
|
||
[[1. 0. 0.]
|
||
[0. 0. 0.]]
|
||
```
|
||
|
||
a and b are not independent. If I change b this changes automatically a too. **It is of high importance to understand when a view is created.** Otherwise you will get totally wrong results.
|
||
|
||
|
||
* Using [start:stop:step] for slicing out segments results in a view. b = a[:-1]
|
||
* A simple assignment keeps a view as a view. e.g. b = a
|
||
* A mathematical operation on a view **may** create a new real ndarray.
|
||
|
||
### [numpy.may_share_memory](https://numpy.org/doc/stable/reference/generated/numpy.may_share_memory.html)
|
||
|
||
```python
|
||
numpy.may_share_memory(a, b, /, max_work=None)
|
||
```
|
||
|
||
> Determine if two arrays might share memory
|
||
>
|
||
> A return of True does not necessarily mean that the two arrays share any element. It just means that they might.
|
||
>
|
||
> Only the memory bounds of a and b are checked by default.
|
||
|
||
A simple example:
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.zeros((2, 3))
|
||
|
||
b=a
|
||
print(np.may_share_memory(a,b)) # -> True
|
||
```
|
||
|
||
```python
|
||
import numpy as np
|
||
|
||
a = np.zeros((2, 3))
|
||
b = a[1:2, 2:3]
|
||
print(np.may_share_memory(a, b)) # -> True
|
||
|
||
b = a[:, 2:3]
|
||
print(np.may_share_memory(a, b)) # -> True
|
||
|
||
b = a[:, ::2]
|
||
print(np.may_share_memory(a, b)) # -> True
|
||
|
||
b = a[0, :]
|
||
print(np.may_share_memory(a, b)) # -> True
|
||
|
||
b = a[0, 0]
|
||
print(np.may_share_memory(a, b)) # -> False
|
||
```
|
||
|
||
The a[0,0] does not create a view, because this creates an interger instead of a np.ndarray. And this kind of type conversion requires the creation of a new object.
|
||
|
||
|
||
|
||
|