Fancy Indexing with Integer Arrays

Reviewed & published by Brayan K

Fancy indexing is selecting elements by passing an array of integer positions, letting you pick, repeat, and reorder any items in one expression — and it always returns a fresh copy rather than a view.

Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll select and reorder rows, grab specific (row, column) pairs from a 2D array, assign through fancy indexing, and see exactly how it differs from basic slicing.

What You'll Learn in This Lesson

1 Selecting with a List of Positions

Instead of one index like arr[2], fancy indexing takes a list of indices. NumPy returns the elements at exactly those positions, in that order. You can even repeat a position to duplicate an element.

import numpy as np

arr = np.array([10, 20, 30, 40, 50])

arr[[0, 2, 4]]      # [10 30 50]  -> picked positions
arr[[4, 3, 2]]      # [50 40 30]  -> reversed order
arr[[1, 1, 1]]      # [20 20 20]  -> repeated
import numpy as np

arr = np.array([10, 20, 30, 40, 50])

# Pick positions 0, 2 and 4
print(arr[[0, 2, 4]])      # [10 30 50]

# Reorder however you like
print(arr[[4, 0, 2]])      # [50 10 30]

# Repeat a position to duplicate
print(arr[[1, 1, 3]])      # [20 20 40]

# ✅ Expected output:
# [10 30 50]
# [50 10 30]
# [20 20 40]

2 Rows and (Row, Column) Pairs in 2D

On a 2D array, a single index list selects whole rows — perfect for reordering or duplicating rows. To pull out specific cells, pass two index arrays: one for rows and one for columns, paired element by element.

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

grid[[2, 0]]              # rows 2 then 0 -> [[6 7 8], [0 1 2]]

# Two arrays -> pick cells (0,0), (1,2), (2,1)
grid[[0, 1, 2], [0, 2, 1]]   # [0 5 7]
import numpy as np

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

# Reorder rows: row 2, then row 0
print(grid[[2, 0]])
# [[6 7 8]
#  [0 1 2]]

# Pick the diagonal cells (0,0), (1,1), (2,2)
print(grid[[0, 1, 2], [0, 1, 2]])    # [0 4 8]

# Pick (0,2), (2,0)
print(grid[[0, 2], [2, 0]])          # [2 6]

# ✅ Expected output:
# [[6 7 8]
#  [0 1 2]]
# [0 4 8]
# [2 6]

3 Assignment & Copy vs View

You can assign through fancy indexing to update scattered positions in place — no loop needed. But remember: when you read with fancy indexing you get a copy, so modifying that result never touches the original. Basic slicing, by contrast, returns a view that shares memory.

arr = np.array([10, 20, 30, 40, 50])

# Assignment updates the original in place
arr[[0, 3]] = 0           # arr -> [0 20 30 0 50]

# Reading makes a COPY
picked = arr[[1, 2]]      # copy, not a view
picked[0] = 999           # arr is unchanged

# Slicing makes a VIEW
window = arr[1:3]         # view shares memory
window[0] = 999           # this DOES change arr
import numpy as np

arr = np.array([10, 20, 30, 40, 50])

# Assign to several positions at once
arr[[0, 3]] = 0
print(arr)                # [ 0 20 30  0 50]

# Fancy indexing READ -> copy (original safe)
picked = arr[[1, 2]]
picked[0] = 999
print(arr)                # [ 0 20 30  0 50]  (unchanged)

# Slicing READ -> view (original changes)
window = arr[1:3]
window[0] = 777
print(arr)                # [  0 777  30   0  50]

# ✅ Expected output:
# [ 0 20 30  0 50]
# [ 0 20 30  0 50]
# [  0 777  30   0  50]

🎯 YOUR TURN: Fill in the Blank

Replace ___ with the index list that reorders the array from largest to smallest.

import numpy as np

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

# 🎯 YOUR TURN:
# Reorder arr to [5, 4, 3, 2, 1] using fancy indexing.
# Hint: 5 is at position 0, 4 at position 2, 3 at position 4,
#       2 at position 3, 1 at position 1.
order = ___

print(arr[order])
# Expected output:
# [5 4 3 2 1]

Answer: [0, 2, 4, 3, 1] — those positions hold 5, 4, 3, 2, 1 in order.

! Common Errors (And How to Fix Them)

❌ "index N is out of bounds"

An index in your list is larger than the array allows:

np.array([10, 20, 30])[[0, 5]]   # ❌ position 5 does not exist

✅ Fix: keep every index between 0 and len(arr) − 1 (negatives count from the end).

❌ Expecting a full grid from two index arrays

grid[[0, 1], [0, 1]]   # returns 2 cells, NOT a 2x2 block

✅ Fix: for a sub-grid, index in steps: grid[[0, 1]][:, [0, 1]].

❌ Surprised the original did not change

Reading with fancy indexing makes a copy.

✅ Fix: assign directly (arr[[0, 3]] = 0) to modify in place.

🎯 Mini Challenge: Top-3 Leaderboard

Given scores and matching names, use argsort plus fancy indexing to print the names of the top three scorers, highest first.

import numpy as np

names  = np.array(["Ana", "Ben", "Cy", "Dee", "Eli"])
scores = np.array([88, 95, 72, 99, 85])

# argsort gives ascending order; take the last 3 and flip to descending
order = np.argsort(scores)          # [2 4 0 1 3]
top3 = order[-3:][::-1]             # [3 1 0]

print(top3)                          # [3 1 0]
print(names[top3])                   # ['Dee' 'Ben' 'Ana']
print(scores[top3])                  # [99 95 88]

# ✅ Expected output:
# [3 1 0]
# ['Dee' 'Ben' 'Ana']
# [99 95 88]

❓ Frequently Asked Questions

Lesson complete — fancy indexing mastered!

You can select, reorder, and repeat elements with integer arrays, grab exact (row, column) pairs, assign in place, and remember that fancy indexing copies while slicing views.

Practice quiz

What is fancy indexing in NumPy?

  • Selecting with start:stop:step slices
  • Selecting elements with an array of integer positions
  • Selecting with a single integer
  • Selecting with a boolean mask only

Answer: Selecting elements with an array of integer positions. Fancy indexing passes a list or array of integer positions to pick elements.

Does reading with fancy indexing return a copy or a view?

  • Always a copy
  • Always a view
  • A view for 1D, a copy for 2D
  • It depends on the dtype

Answer: Always a copy. Fancy indexing always returns a new copy, unlike basic slicing which returns a view.

For arr = [10, 20, 30, 40, 50], what does arr[[4, 0, 2]] return?

  • [10, 30, 50]
  • [40, 0, 20]
  • [50, 10, 30]
  • [10, 20, 30]

Answer: [50, 10, 30]. It picks positions 4, 0, 2 in that order: 50, 10, 30.

What shape does the result of fancy indexing follow?

  • The shape of the original array
  • The shape of the index array
  • Always 1D
  • Always 2D

Answer: The shape of the index array. Three indices give three values; the result matches the index array's shape.

For a 3x3 grid, what does grid[[0, 1, 2], [0, 1, 2]] return?

  • A 3x3 block
  • The first row
  • The whole grid
  • The diagonal [0, 4, 8]

Answer: The diagonal [0, 4, 8]. Two index arrays pair element by element, selecting cells (0,0), (1,1), (2,2).

On a 2D array, what does a single index list like grid[[2, 0]] select?

  • Rows 2 then 0
  • Columns 2 then 0
  • Cells (2,0)
  • An error

Answer: Rows 2 then 0. A single index list on a 2D array selects whole rows in that order.

Can you assign to several positions at once with arr[[0, 3]] = 0?

  • No, it raises an error
  • Only on copies
  • Yes, it updates those positions in place
  • Only for floats

Answer: Yes, it updates those positions in place. Assignment through fancy indexing updates the listed positions in place.

Two index arrays like grid[[0,1,2],[0,2,1]] return how many cells?

  • 9 (a full grid)
  • 1
  • 6
  • 3 (paired element by element)

Answer: 3 (paired element by element). They are paired position by position, returning 3 cells, not a 3x3 grid.

What does grid[[0, 2], [2, 0]] return for a 3x3 grid of 0..8?

  • [2, 6]
  • [0, 8]
  • [6, 2]
  • [2, 0]

Answer: [2, 6]. It selects cells (0,2)=2 and (2,0)=6.

Why does modifying picked = arr[[1, 2]] leave arr unchanged?

  • arr is read-only
  • Fancy indexing read returns a copy
  • The indices were invalid
  • NumPy caches the result

Answer: Fancy indexing read returns a copy. Reading with fancy indexing makes a copy, so edits to it do not touch the original.

Continue this course

Frequently asked questions

What is fancy indexing in NumPy?

Fancy indexing means selecting elements with an array (or list) of integer indices instead of a single number or a slice. You hand NumPy a list of positions like arr[[0, 2, 2, 1]] and it returns the elements at exactly those positions, in exactly that order — even repeating a position is allowed. It is the most flexible way to pick and reorder data.

Does fancy indexing return a copy or a view?

Fancy indexing always returns a new copy, never a view. This is the big difference from basic slicing, which returns a view that shares memory with the original. So modifying the result of arr[[0, 2]] will not change the original array, while modifying arr[0:2] would.

How do I select specific (row, column) pairs from a 2D array?

Pass two index arrays, one for rows and one for columns. arr[[0, 1, 2], [2, 0, 1]] pairs them element by element and returns arr[0,2], arr[1,0], arr[2,1] as a 1D array. The two index arrays must be the same length (or broadcastable), and the result follows their shape.

Can I assign to an array using fancy indexing?

Yes. arr[[0, 3, 4]] = 0 sets those three positions to zero in place, and you can assign a matching list of values too. If the same index appears twice during assignment, the last write wins. This makes fancy indexing a clean way to update scattered elements without a loop.