# Indexing and Slicing

## 1-D array

1-D NumPy Array elements can be accessed by index, using syntax identical to Python lists: `array[index]` for a single element, or `array[start:end]` for a slice, where `start` and `end` are the starting and ending indexes for the slice.

```python
arr = np.arange(10)

arr
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr[5]
# 5

arr[-1]
# 9

arr[5:8]
# array([5, 6, 7])

arr[:3]
# array([0, 1, 2])

arr[-3:]
# array([7, 8, 9])

# ndarray slicing is shallow copy
arr[5:8] = 12
arr
# array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])

# assign new variable to ndarray slicing is still 
# shallow copy
arr_slice = arr[5:8]
arr_slice[1] = 12345
arr
array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])
```

Notice that when we select multiple elements, we get an array.

## 2-D Array

Nested Arrays can be accessed by adding additional comma-separated parameters.

```python
arr2d = np.array([[1, 2, 3], 
                  [4, 5, 6], 
                  [7, 8, 9]])
arr2d[2]
# array([7, 8, 9])

arr2d[0, 2]
# 3

arr2d[:2, 1:]
# array([[2, 3],[5, 6]])

arr2d[1, :2]
# array([4, 5])

arr2d[:, :1]
# array([[1],[4],[7]])
```

![](https://226450351-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M2dsVcABTFJ8-1ot2lc%2F-M2dsXVMGCtSH8FOGDYd%2F-M2dsXqnwGKoRLH5p8x7%2FScreen%20Shot%202018-12-03%20at%2010.16.05%20PM.png?generation=1584471880766089\&alt=media)

Note that when we work with arrays that have more than one dimension, the relationship between the interior arrays is defined in terms of _axes_. A two-dimensional array has two axes: axis 0 represents the values that share the same indexical position (are in the same column), and axis 1 represents the values that share an array (are in the same row). This is illustrated below.

![Diagram showing the axes in an array](https://s3.amazonaws.com/codecademy-content/courses/numpy/NumPy+Array+fixed.svg)

## Boolean Indexing

NumPy elements can be indexed using conditionals. The syntax to filter an array using a conditional is `array_name[conditional]`.

The returned array will contain only the elements for which the conditional evaluates to `True`.

```python
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 
                  'Will', 'Joe', 'Joe'])
data = randn(7, 4)
names
# array(['Bob', 'Joe', 'Will', 'Bob', 
#        'Will', 'Joe', 'Joe'], dtype='|S4')
data
# array([[-0.048 , 0.5433, -0.2349, 1.2792],
#        [-0.268 , 0.5465, 0.0939, -2.0445],
#        [-0.047 , -2.026 , 0.7719, 0.3103],
#        [ 2.1452, 0.8799, -0.0523, 0.0672],
#        [-1.0023, -0.1698, 1.1503, 1.7289],
#        [ 0.1913, 0.4544, 0.4519, 0.5535],
#        [ 0.5994, 0.8174, -0.9297, -1.2564]])

names == 'Bob'
# array([True, False, False, True, 
#        False, False, False], dtype=bool)

data[names == 'Bob']
# array([[-0.048 , 0.5433, -0.2349, 1.2792],
#        [2.1452, 0.8799, -0.0523, 0.0672]])

data[names == 'Bob', 2:]
# array([[-0.2349, 1.2792],
#        [-0.0523, 0.0672]])

data[names == 'Bob', 3]
# array([ 1.2792, 0.0672])

data[-(names == 'Bob')]
# array([[-0.268 , 0.5465, 0.0939, -2.0445],
#        [-0.047 , -2.026 , 0.7719, 0.3103],
#        [-1.0023, -0.1698, 1.1503, 1.7289],
#        [ 0.1913, 0.4544, 0.4519, 0.5535],
#        [ 0.5994, 0.8174, -0.9297, -1.2564]])

data[names != 'Bob']
# array([[-0.268 , 0.5465, 0.0939, -2.0445],
#        [-0.047 , -2.026 , 0.7719, 0.3103],
#        [-1.0023, -0.1698, 1.1503, 1.7289],
#        [ 0.1913, 0.4544, 0.4519, 0.5535],
#        [ 0.5994, 0.8174, -0.9297, -1.2564]])

# use & (and) and | (or) for more than one comparision
data[(names == 'Bob') | (names == 'Will')]
# array([[-0.048 , 0.5433, -0.2349, 1.2792],
#        [-0.047 , -2.026 , 0.7719, 0.3103],
#        [ 2.1452, 0.8799, -0.0523, 0.0672],
#        [-1.0023, -0.1698, 1.1503, 1.7289]])
```

## Fancy Indexing

Fancy indexing is a term adopted by NumPy to describe indexing using integer arrays. Passing multiple index arrays does something slightly different; it selects a 1D array of elements corresponding to each tuple of indices:

```python
arr = np.empty((8, 4))
for i in range(8):
    arr[i] = i
arr
# array([[ 0., 0., 0., 0.],
#        [ 1., 1., 1., 1.],
#        [ 2., 2., 2., 2.],
#        [ 3., 3., 3., 3.],
#        [ 4., 4., 4., 4.],
#        [ 5., 5., 5., 5.],
#        [ 6., 6., 6., 6.],
#        [ 7., 7., 7., 7.]])

arr[[4, 3, 0, 6]]
# array([[ 4., 4., 4., 4.],
#        [ 3., 3., 3., 3.],
#        [ 0., 0., 0., 0.],
#        [ 6., 6., 6., 6.]])

# Using negative indices select rows from the end
arr[[-3, -5, -7]]
# array([[ 5., 5., 5., 5.],
#        [ 3., 3., 3., 3.],
#        [ 1., 1., 1., 1.]])

# create 1-dimension then reshape
arr = np.arange(32).reshape((8, 4))
# array([[ 0, 1, 2, 3],
#        [ 4, 5, 6, 7],
#        [ 8, 9, 10, 11],
#        [12, 13, 14, 15],
#        [16, 17, 18, 19],
#        [20, 21, 22, 23],
#        [24, 25, 26, 27],
#        [28, 29, 30, 31]])

arr[[1, 5, 7, 2], [0, 3, 1, 2]]
# array([ 4, 23, 29, 10])

arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
# array([[ 4, 7, 5, 6],
# [20, 23, 21, 22],
# [28, 31, 29, 30],
# [ 8, 11, 9, 10]])

arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
# array([[ 4, 7, 5, 6],
# [20, 23, 21, 22],
# [28, 31, 29, 30],
# [ 8, 11, 9, 10]])
```
