Indexing & Slicing Arrays
Once your data is in an array, you need to pull out parts of it: one student's marks, one subject for every student, or only the days hotter than 40 °C. NumPy gives you four ways to do this: plain indexes, slices, boolean masks and lists of positions.
In this lesson you will use all four on 1-D and 2-D arrays, and you will learn the one behaviour that surprises almost every beginner: slices are views, not copies.
Indexing and slicing 1-D arrays
A 1-D array works much like a Python list. Positions start at 0, negative positions count from the end, and a slice start:stop:step takes a range where stop is not included.
import numpy as np
runs = np.array([45, 12, 78, 0, 101, 33, 64]) # one batter, seven matches
print(runs[0], runs[-1]) # first and last
print(runs[2:5]) # positions 2, 3, 4
print(runs[:3]) # first three
print(runs[::2]) # every second match
print(runs[::-1]) # reversed45 64 [ 78 0 101] [45 12 78] [ 45 78 101 64] [ 64 33 101 0 78 12 45]
Leaving out start means "from the beginning", leaving out stop means "to the end", and a step of -1 walks backwards.
Indexing 2-D arrays
A 2-D array has rows and columns, so you give two positions separated by a comma: array[row, column]. A slice can be used for either part, and a bare : means "all".
import numpy as np
# rows = students, columns = maths, science, english, hindi
marks = np.array([
[78, 82, 65, 90], # Aarav
[91, 88, 79, 84], # Priya
[55, 61, 70, 76], # Kabir
[84, 79, 92, 88], # Meera
])
print(marks[1, 2]) # Priya's english
print(marks[2]) # all of Kabir's marks
print(marks[:, 0]) # maths for every student
print(marks[1:3, 2:]) # Priya and Kabir, english and hindi
print(marks[-1, ::2]) # Meera: maths and english79 [55 61 70 76] [78 91 55 84] [[79 84] [70 76]] [84 92]
Read marks[:, 0] as "every row, column 0". Notice that marks[2] with one index returns a whole row. Taking a single row or column gives a 1-D array; taking a block with slices keeps it 2-D.
Slices are views
When you slice an array, NumPy does not copy the data. It gives you a view: a new array object that looks at the same memory. Changing the view changes the original.
import numpy as np
stock = np.array([40, 25, 60, 10, 35])
first_three = stock[:3]
first_three[0] = 0 # also changes stock!
print(stock)
safe = stock[:3].copy()
safe[1] = 999 # stock is not affected
print(stock)
print(safe)[ 0 25 60 10 35] [ 0 25 60 10 35] [ 0 999 60]
Views make NumPy fast and memory-friendly, because slicing a million-value array costs almost nothing. But if you plan to change a part of an array and keep the original, call .copy().
Boolean masks
A boolean mask is an array of True and False values of the same shape as your data. Comparing an array with a value creates one, and putting the mask inside square brackets keeps only the True positions. This is the NumPy way to filter.
import numpy as np
temps = np.array([38.5, 41.2, 43.0, 39.8, 44.6, 40.1, 36.9]) # Jaipur, one week in May
hot = temps > 40
print(hot)
print(temps[hot])
print("Days above 40:", hot.sum())
print(temps[(temps > 39) & (temps < 42)]) # and
print(temps[(temps < 37) | (temps > 44)]) # or
print(temps[~hot]) # not
temps[temps > 43] = 43 # cap extreme readings
print(temps)[False True True False True True False] [41.2 43. 44.6 40.1] Days above 40: 4 [41.2 39.8 40.1] [44.6 36.9] [38.5 39.8 36.9] [38.5 41.2 43. 39.8 43. 40.1 36.9]
Since True counts as 1 and False as 0, hot.sum() counts the hot days. To combine conditions, use & (and), | (or) and ~ (not), and wrap each condition in brackets. You can also assign through a mask: the last lines replace every value above 43 with 43.
Fancy indexing
You can also pass a list of positions. This is called fancy indexing, and it always returns a copy, not a view.
import numpy as np
cities = np.array(["Delhi", "Pune", "Kochi", "Jaipur", "Lucknow"])
sales = np.array([5400, 3100, 2800, 3900, 2600])
print(cities[[0, 3]])
order = np.argsort(sales)[::-1] # positions from highest to lowest sales
print(order)
print(cities[order])
print(sales[order][:3])
marks = np.array([[78, 82, 65], [91, 88, 79], [55, 61, 70], [84, 79, 92]])
print(marks[marks[:, 0] > 80]) # students with more than 80 in maths['Delhi' 'Jaipur'] [0 3 1 2 4] ['Delhi' 'Jaipur' 'Pune' 'Kochi' 'Lucknow'] [5400 3900 3100] [[91 88 79] [84 79 92]]
np.argsort() returns the positions that would sort the array. Reversing it with [::-1] gives highest first, and using it as a fancy index sorts a second array, the city names, in the same order. The last line is a pattern you will use constantly: build a mask from one column, then use it to pick whole rows.
Common mistakes
- Using
and/orwith arrays.temps > 39 and temps < 42raisesValueError: The truth value of an array with more than one element is ambiguous. Use&and|with brackets. - Forgetting brackets.
temps > 39 & temps < 42is read astemps > (39 & temps) < 42because&binds tighter than>. - Changing a slice by accident. A slice is a view; call
.copy()if the original must stay the same. - Writing
marks[1][2]everywhere. It works, butmarks[1, 2]is clearer and faster.
Key points
- Positions start at 0; slices use
start:stop:stepand excludestop. - For 2-D arrays write
array[row, column];:selects everything along that axis. - Slices are views of the original data; use
.copy()to get independent data. - A boolean mask such as
temps > 40filters values; combine masks with&,|and~. - Fancy indexing with a list of positions returns a copy;
np.argsort()gives sorting positions.
Exercise
A Kochi shop sold these numbers of umbrellas over 10 days: 12, 30, 45, 8, 51, 60, 22, 5, 38, 47. Print the sales from the last three days, the days (positions starting at 1) when more than 40 were sold, and the average of the days with fewer than 25 sales.
Show solution
import numpy as np
umbrellas = np.array([12, 30, 45, 8, 51, 60, 22, 5, 38, 47])
print(umbrellas[-3:])
busy = umbrellas > 40
print("Busy days:", np.arange(1, 11)[busy])
print("Quiet average:", umbrellas[umbrellas < 25].mean())[ 5 38 47] Busy days: [ 3 5 6 10] Quiet average: 11.75
np.arange(1, 11) creates day numbers 1 to 10, and the same mask picks the day numbers where sales were above 40.
Quick Quiz
1. For a 2-D array a, what does a[:, 1] return?
: means all rows, and 1 picks column 1, the second column.
2. b = a[:2] and then b[0] = 0. What happens to a?
NumPy slices share memory with the original array.
3. Which expression keeps values between 10 and 20?
Use & for element-wise "and", with each condition in brackets.
4. What does np.argsort(np.array([30, 10, 20])) return?
It returns the positions that would sort the array: 10 is at 1, 20 at 2, 30 at 0.
Learn Data Science with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
