Skip to content

NumPy Basics: the ndarray and Array Attributes

NumPy (Numerical Python) is the library that makes Python fast with numbers. It gives Python a new type of object, the ndarray (n-dimensional array): a grid of values that all have the same type. Almost every data library you will use later, such as pandas, Matplotlib, Seaborn, scikit-learn and OpenCV, is built on top of NumPy arrays.

Think of a Python list as a shopping bag: you can throw anything into it (numbers, text, other lists), but you must look at each item one by one. A NumPy array is an egg tray: every slot has the same size and holds the same kind of thing, so you can work on the whole tray at once, very quickly.

About this NumPy series

The series has 17 lessons in five parts. Each lesson explains the code in small steps, in simple words, and shows the real output you will see when you run it. Every lesson ends with exercises and a short summary.

#PartLesson
01FundamentalsNumPy Basics: ndarray and Attributes
02FundamentalsCreating Arrays: zeros, arange, linspace
03FundamentalsRandom Numbers and Data Types
04Indexing and ReshapingIndexing and Slicing
05Indexing and ReshapingBoolean Masks, Fancy Indexing, np.where
06Indexing and ReshapingViews vs Copies, reshape, ravel, flatten
07Indexing and ReshapingTranspose, newaxis, expand_dims, squeeze
08Indexing and ReshapingConcatenate, Stack and Split
09ComputationVectorized Arithmetic and Ufuncs
10ComputationBroadcasting
11ComputationAggregations, axis and NaN-safe Functions
12ComputationSorting, Searching, unique and Sets
13Linear Algebra and MathMatrix Multiplication and np.linalg
14Linear Algebra and MathMissing Data, Rounding, clip, isclose
15AdvancedFile I/O and Structured Arrays
16AdvancedPerformance, Memory Layout and Strides
17AdvancedImages as NumPy Arrays

Setup

You need Python 3.10 or newer. Check your version in a terminal (Command Prompt or PowerShell on Windows):

python --version

Step 1: Create a folder for the project

mkdir NumpyTutorials
cd NumpyTutorials

You will keep all your NumPy practice files in this folder. Each lesson tells you which file to create.

Step 2: Create a virtual environment

A virtual environment is a private copy of Python just for this project. Packages you install in it do not mix with your other projects.

python -m venv venv                 # creates a folder named "venv"

.\venv\Scripts\Activate.ps1         # Windows PowerShell
venv\Scripts\activate.bat           # Windows Command Prompt
source venv/bin/activate            # macOS / Linux

Run only the activate line for your system. When it works, you see (venv) at the start of the terminal line.

If PowerShell says "running scripts is disabled on this system", run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once, then activate again.

Step 3: Create requirements.txt

requirements.txt is a normal text file that lists the packages a project needs, one per line. Instead of remembering every package, anyone can install all of them with one command. Create a file named requirements.txt in the project folder with these two lines:

numpy>=2.0
matplotlib      # only lesson 17 (reads and saves the image files)
LineMeaning
numpy>=2.0install NumPy, version 2.0 or newer (this series was run with NumPy 2.5)
matplotlibinstall the newest Matplotlib; it is used only in lesson 17 to read and save pictures
# ...a comment; pip ignores everything after #

Step 4: Install the packages

With the virtual environment active, install everything listed in the file:

pip install -r requirements.txt

-r means "read the package names from this file". If you prefer, you can skip the file and name the packages directly; the result is the same:

pip install numpy matplotlib

Step 5: Check that NumPy works

python -c "import numpy; print(numpy.__version__)"

If this prints a version number such as 2.5.4, you are ready.

How to run the examples

Create a new file named ndarray_basics.py in the NumpyTutorials folder you created above. Any name works; this one just matches the topic. Start the file with these lines:

import time

import numpy as np

Then copy the examples of this page into the same file, one after another, in the order shown, and run it with python ndarray_basics.py (with your virtual environment active). Some examples use variables created in earlier ones, so keep everything in the one file. Each Output box shows what that example prints.

Every NumPy program starts with import numpy as np. The short name np is used by everybody, so you will see it in every NumPy example online.

1. Python list vs NumPy array

You make an array with np.array(), usually from a list. At first sight it looks like a list without commas, but it behaves very differently with maths:

prices_list = [100, 200, 300]
prices = np.array([100, 200, 300])          # make an array from a list

print("list       :", prices_list)
print("array      :", prices)
print("type       :", type(prices))

print("list * 2   :", prices_list * 2)      # list is repeated
print("array * 2  :", prices * 2)           # every element is multiplied
print("list + list:", prices_list + [1, 2, 3])          # lists are joined
print("array + array:", prices + np.array([1, 2, 3]))   # elements are added
Output
list       : [100, 200, 300]
array      : [100 200 300]
type       : <class 'numpy.ndarray'>
list * 2   : [100, 200, 300, 100, 200, 300]
array * 2  : [200 400 600]
list + list: [100, 200, 300, 1, 2, 3]
array + array: [101 202 303]
  • list * 2 repeats the list; array * 2 multiplies every element by 2.
  • list + list joins two lists; array + array adds the elements in matching positions.
  • The type of the array is numpy.ndarray.

This "do the maths on every element at once" behaviour is called vectorization, and it is the main reason to use NumPy. You never need to write a for loop to add GST to 10,000 prices: prices * 1.18 does it.

2. 1D, 2D and 3D arrays

An array can have any number of dimensions (also called axes). You create them from nested lists:

one_d = np.array([10, 20, 30, 40])                       # a row of values

two_d = np.array([[1, 2, 3],                             # rows and columns (a table)
                  [4, 5, 6]])

three_d = np.array([[[1, 2], [3, 4]],                    # several tables stacked
                    [[5, 6], [7, 8]]])

print("1D array:\n", one_d)
print("2D array:\n", two_d)
print("3D array:\n", three_d)
Output
1D array:
 [10 20 30 40]
2D array:
 [[1 2 3]
 [4 5 6]]
3D array:
 [[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]
DimensionsLooks likeReal-life example
1Da single row of valuestemperatures of 7 days
2Da table with rows and columnsmarks of students (rows) in subjects (columns)
3Dseveral tables stackedsales of several stores, each a table of weeks x days; or a colour image

When NumPy prints an array, every extra [ is one more dimension. The 3D array starts with [[[, and an empty line separates the two tables.

3. Array attributes: shape, ndim, size, dtype

Every array carries information about itself in its attributes. You read them with a dot, without brackets, because they are values, not functions:

marks = np.array([[78, 85, 92, 66],
                  [88, 79, 95, 70],
                  [65, 90, 81, 99]])          # 3 students x 4 subjects

print("shape    :", marks.shape)      # (rows, columns)
print("ndim     :", marks.ndim)       # number of dimensions
print("size     :", marks.size)       # total elements = 3 * 4
print("dtype    :", marks.dtype)      # type of every element
print("itemsize :", marks.itemsize, "bytes per element")
print("nbytes   :", marks.nbytes, "bytes in total")
print("len()    :", len(marks), "(length of the first dimension)")
Output
shape    : (3, 4)
ndim     : 2
size     : 12
dtype    : int64
itemsize : 8 bytes per element
nbytes   : 96 bytes in total
len()    : 3 (length of the first dimension)
AttributeMeaningHere
shapeThe size of each dimension, as a tuple(3, 4): 3 rows, 4 columns
ndimNumber of dimensions = len(shape)2
sizeTotal number of elements = all sizes multiplied3 * 4 = 12
dtypeThe data type of every elementint64 (64-bit whole numbers)
itemsizeBytes used by one element8 (64 bits = 8 bytes)
nbytesBytes used by all elements = size * itemsize12 * 8 = 96

shape is the attribute you will check most often. When something goes wrong in NumPy, the first thing to do is print(arr.shape).

4. Comparing attributes

The same attributes for arrays of different dimensions and types, side by side:

arrays = {
    "number 5": np.array(5),
    "1D": one_d,
    "2D": two_d,
    "3D": three_d,
    "floats": np.array([1.5, 2.5]),
    "booleans": np.array([True, False, True]),
    "text": np.array(["pen", "book"]),
}

print(f"{'array':<10}{'shape':<12}{'ndim':<6}{'size':<6}dtype")
for name, arr in arrays.items():
    print(f"{name:<10}{str(arr.shape):<12}{arr.ndim:<6}{arr.size:<6}{arr.dtype}")
Output
array     shape       ndim  size  dtype
number 5  ()          0     1     int64
1D        (4,)        1     4     int64
2D        (2, 3)      2     6     int64
3D        (2, 2, 2)   3     8     int64
floats    (2,)        1     2     float64
booleans  (3,)        1     3     bool
text      (2,)        1     2     <U4
  • np.array(5) is a 0-dimensional array (a single value): its shape is the empty tuple ().
  • A 1D shape is written (4,) with a comma, because it is a tuple with one number.
  • Floats are float64, True/False values are bool.
  • Text gets a type like <U4: Unicode text of at most 4 characters ("book" has 4).

5. All elements get the same type

A list can hold a mix of types. An array cannot: NumPy picks one type that can hold every value, and converts everything to it.

mixed_numbers = np.array([1, 2.5, 3])          # ints and a float
print(mixed_numbers, "->", mixed_numbers.dtype)           # all become float

mixed_text = np.array([1, "two", 3.0])         # a string is present
print(mixed_text, "->", mixed_text.dtype)                 # all become text

flags = np.array([True, 0, 5])                 # bool and ints
print(flags, "->", flags.dtype)                           # all become int
Output
[1.  2.5 3. ] -> float64
['1' 'two' '3.0'] -> <U32
[1 0 5] -> int64
  • Ints and a float: everything becomes float64 (1 becomes 1.).
  • Anything with text: everything becomes text. '1' is now a string, so you can no longer do maths with it. Watch out for this when reading data from files.
  • True with integers becomes 1.

This single type is what makes arrays fast: NumPy knows exactly how many bytes each element uses and where it is in memory. Lesson 03 covers data types in detail.

6. Speed: list loop vs NumPy

Let's square and add up one million numbers, once with a Python loop and once with NumPy:

numbers_list = list(range(1_000_000))
numbers = np.arange(1_000_000)

start = time.perf_counter()
total_list = sum(n * n for n in numbers_list)       # Python loop
list_time = time.perf_counter() - start

start = time.perf_counter()
total_numpy = (numbers * numbers).sum()             # one NumPy expression
numpy_time = time.perf_counter() - start

print("same result :", total_list == total_numpy)
print(f"list loop   : {list_time * 1000:.1f} ms")
print(f"NumPy       : {numpy_time * 1000:.1f} ms")
print(f"NumPy is about {list_time / numpy_time:.0f} times faster")
Output
same result : True
list loop   : 84.2 ms
NumPy       : 3.5 ms
NumPy is about 24 times faster

Both give exactly the same answer, but NumPy is many times faster. (Your timings will be different; they depend on your computer.) The Python loop handles one number at a time, and each one is a full Python object. NumPy runs the whole calculation in optimized C code over one block of memory. Lesson 16 looks at speed in detail.

7. Real example: student marks

A 2D array is a natural way to store a table. Here, each row is a student and each column is a subject:

subjects = ["Maths", "Science", "English", "Hindi"]

print("Marks table (rows = students):\n", marks)
print("Total of each student :", marks.sum(axis=1))
print("Average per subject   :", marks.mean(axis=0).round(1))
print("Highest mark          :", marks.max())
print("Best subject on average:", subjects[marks.mean(axis=0).argmax()])
Output
Marks table (rows = students):
 [[78 85 92 66]
 [88 79 95 70]
 [65 90 81 99]]
Total of each student : [321 332 335]
Average per subject   : [77.  84.7 89.3 78.3]
Highest mark          : 99
Best subject on average: English
  • sum(axis=1) adds across each row: the total of each student.
  • mean(axis=0) goes down each column: the average of each subject.
  • argmax() gives the position of the biggest value, which we use to pick the subject's name.

Don't worry about axis yet; lesson 11 explains it step by step.

Exercises

Try these exercises on your own before moving on:

  1. Make an array of the temperatures of 7 days and print shape, ndim, size and dtype.
  2. Make a 2D array of 4 products x 3 months of sales. What is its shape?
  3. Make np.array([1, 2, "3"]) and print its dtype. Why is it not int?
  4. Multiply the sales array by 1.18 (add 18% GST). What is the new dtype?

Summary

  • NumPy's ndarray is a grid of values that all have the same type.
  • Maths on an array works on every element at once (vectorization), with no loop.
  • Arrays can be 1D (a row), 2D (a table), 3D (stacked tables) or more.
  • shape, ndim, size, dtype, itemsize and nbytes describe an array.
  • Mixing types converts everything to one type; text "wins" over numbers.
  • NumPy is much faster and smaller than Python lists for numbers.

Next: Lesson 02: Creating Arrays: zeros, arange, linspace.