NumPy: ndarray Core Concepts

Last updated: 2026-08-26

NumPy's core data structure is ndarray — the N-dimensional array. It looks like a Python list, but underneath it's completely different: fixed type, contiguous memory, vectorized operations. Understanding ndarray's internals is the first step to using NumPy effectively.

1. What You'll Learn



2. A Data Analyst's True Story

(1) The Problem: Excel in Python, Confusion

Bob has a 3-row, 4-column Excel table. He stores it as a Python list of lists — 3 sublists nested inside an outer list. He wants to sum the second column, so he writes a for loop to iterate through each row. For 1 million rows, that takes 5 seconds. Worse, when a string sneaks into one of the rows, the loop doesn't fail until it hits that cell.

(2) The ndarray Solution

Charlie suggests Bob convert the table to an ndarray with np.array. NumPy flattens the data into a contiguous block of memory and performs the column sum in one C-level vectorized operation — the same 1 million rows take just 5 milliseconds. Types are enforced at creation time, so a stray string gets caught immediately, not halfway through the computation.

(3) The Payoff



3. The Essence of ndarray

(1) Homogeneous Data Types

All elements of an ndarray must be the same type. This is fundamentally different from Python lists — lists can mix integers, strings, and even objects, while ndarray allows only one dtype.

PYTHON
import numpy as np

a = np.array([1, 2, 3])
print(a.dtype)  # int64

b = np.array([1.0, 2.0, 3.0])
print(b.dtype)  # float64

c = np.array([1, 2.0, 3])
print(c.dtype)  # float64 (auto upcast)
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.

(2) Contiguous Memory Storage

Python lists store an array of pointers — each pointer points to a separate PyObject scattered across heap memory. ndarray, on the other hand, arranges all data in a single contiguous block of memory, with no pointer overhead and excellent CPU cache efficiency.

Feature Python list ndarray
Element type Any mix Same dtype
Storage Pointer array → scattered PyObjects Contiguous memory block
Per-element overhead 28+ bytes (PyObject header) dtype-dependent (1/2/4/8 bytes)
Cache-friendly Poor (pointer jumping) Excellent (sequential access)
Type safety None (caught at runtime) Enforced at creation

▶ Example

TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

: 0D/1D/2D/3D array properties (Difficulty ⭐)

PYTHON
import numpy as np

a0 = np.array(42)                   # 0D scalar
a1 = np.array([1, 2, 3])            # 1D
a2 = np.array([[1, 2], [3, 4]])     # 2D
a3 = np.array([[[1, 2], [3, 4]],
               [[5, 6], [7, 8]]])   # 3D

for name, arr in [("0D", a0), ("1D", a1), ("2D", a2), ("3D", a3)]:
    print(f"{name}: ndim={arr.ndim}, shape={arr.shape}, size={arr.size}")
# 0D: ndim=0, shape=(), size=1
# 1D: ndim=1, shape=(3,), size=3
# 2D: ndim=2, shape=(2,2), size=4
# 3D: ndim=3, shape=(2,2,2), size=8
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.



4. Dimensions, Shape, and Size

(1) ndim / shape / size

Attribute Meaning Example (shape=(3,4))
ndim Number of dimensions (axes) 2
shape Length of each axis, as a tuple (3, 4)
size Total number of elements = product of axis lengths 12
PYTHON
import numpy as np

a = np.zeros((3, 4))
print(a.ndim)    # 2
print(a.shape)   # (3, 4)
print(a.size)    # 12
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.

(2) Strides

strides tells you how many bytes to skip in memory to move one step along a given axis.

Attribute Meaning shape=(3,4) dtype=int64
strides Stride per axis (bytes) (32, 8)
itemsize Bytes per element 8
PYTHON
import numpy as np

a = np.zeros((3, 4), dtype=np.int64)
print(a.strides)   # (32, 8)
# axis 0: move 1 row = 4 * 8 = 32 bytes
# axis 1: move 1 col = 1 * 8 = 8 bytes
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.

▶ Example

TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

: Strides visualization (Difficulty ⭐⭐)

PYTHON
import numpy as np

a = np.arange(12, dtype=np.int32).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
print(f"shape:   {a.shape}")    # (3, 4)
print(f"strides: {a.strides}")  # (16, 4)
print(f"itemsize: {a.itemsize}")  # 4
# axis 0 stride: 4 elements * 4 bytes = 16
# axis 1 stride: 1 element  * 4 bytes = 4
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.



5. Intuition Behind Axes

Axes are the key to understanding ndarray operations. axis=0 is the outermost dimension, axis=1 is the next, and so on.

Axis Position in shape Intuition 2D shape=(3,4) 3D shape=(2,3,4)
axis=0 0th Outermost Row direction (across rows) Along the "layer" axis
axis=1 1st Second Column direction (across columns) Along the "row" axis
axis=2 2nd Innermost Along the "column" axis

Memory trick: axis=k corresponds to the direction of shape[k]. Doing sum/reduce along that axis "eliminates" that dimension.

PYTHON
import numpy as np

a = np.arange(24).reshape(2, 3, 4)
print(a.shape)         # (2, 3, 4)
print(a.sum(axis=0).shape)  # (3, 4) — axis 0 eliminated
print(a.sum(axis=1).shape)  # (2, 4) — axis 1 eliminated
print(a.sum(axis=2).shape)  # (2, 3) — axis 2 eliminated
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.



6. dtype and Memory Usage

(1) dtype Type System

dtype Description Bytes Range
np.int8 Signed 8-bit integer 1 -128 ~ 127
np.int32 Signed 32-bit integer 4 -2^31 ~ 2^31-1
np.int64 Signed 64-bit integer 8 -2^63 ~ 2^63-1
np.float32 32-bit float 4 ~±3.4e38, 7-digit precision
np.float64 64-bit float 8 ~±1.8e308, 15-digit precision
np.bool_ Boolean 1 True/False
np.complex128 128-bit complex 16 Two float64

(2) Memory Model: ndarray Is Not a Nested List

100%
graph TB
    A["ndarray object"] --> B["data pointer<br/>raw memory block"]
    A --> C["dtype<br/>element type"]
    A --> D["shape<br/>(3, 4)"]
    A --> E["strides<br/>(32, 8)"]
    B --> F["0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | ... | 2,3"]
    style A fill:#4CAF50,color:#fff
    style B fill:#2196F3,color:#fff
    style F fill:#FF9800,color:#fff
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

(3) Shape-Memory Reference Table

shape ndim size dtype=float64 nbytes strides
(10,) 1 10 80 (8,)
(3, 4) 2 12 96 (32, 8)
(2, 3, 4) 3 24 192 (96, 32, 8)
(5, 2, 3, 4) 4 120 960 (192, 96, 32, 8)

▶ Example

TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

: dtype memory comparison (Difficulty ⭐)

PYTHON
import numpy as np

for dtype in [np.int8, np.int32, np.int64, np.float32, np.float64]:
    a = np.zeros(1000, dtype=dtype)
    print(f"{dtype.__name__:10s} itemsize={a.itemsize}  nbytes={a.nbytes}")
# int8       itemsize=1  nbytes=1000
# int32      itemsize=4  nbytes=4000
# int64      itemsize=8  nbytes=8000
# float32    itemsize=4  nbytes=4000
# float64    itemsize=8  nbytes=8000
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.

▶ Example

TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

: Verify reshape doesn't copy data (Difficulty ⭐⭐)

PYTHON
import numpy as np

a = np.arange(12, dtype=np.int64)
b = a.reshape(3, 4)

print(a.shape, b.shape)       # (12,) (3, 4)
print(a.strides, b.strides)   # (8,) (32, 8)
print(a.nbytes, b.nbytes)     # 96 96

b[0, 0] = 999
print(a[0])  # 999 — same memory!
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.



7. Comprehensive Example: 3D Array Deep Dive

▶ Example

TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.

: Full property analysis of a 3×4×2 array (Difficulty ⭐⭐⭐)

PYTHON
import numpy as np

a = np.arange(24, dtype=np.int64).reshape(3, 4, 2)

print("=== Full array ===")
print(a)
# [[[ 0  1]
#   [ 2  3]
#   [ 4  5]
#   [ 6  7]]
#  [[ 8  9]
#   [10 11]
#   [12 13]
#   [14 15]]
#  [[16 17]
#   [18 19]
#   [20 21]
#   [22 23]]]

print(f"ndim:    {a.ndim}")     # 3
print(f"shape:   {a.shape}")    # (3, 4, 2)
print(f"size:    {a.size}")     # 24
print(f"dtype:   {a.dtype}")    # int64
print(f"itemsize:{a.itemsize}") # 8
print(f"nbytes:  {a.nbytes}")   # 192  (24 * 8)
print(f"strides: {a.strides}")  # (64, 16, 8)
# axis 0: 4*2*8 = 64 bytes per step
# axis 1: 2*8   = 16 bytes per step
# axis 2: 1*8   = 8  bytes per step

# Verify strides manually
expected_strides = (
    a.shape[1] * a.shape[2] * a.itemsize,  # 4*2*8 = 64
    a.shape[2] * a.itemsize,                # 2*8   = 16
    a.itemsize                               # 8
)
print(f"manual strides: {expected_strides}")  # (64, 16, 8)
print(f"match: {a.strides == expected_strides}")  # True

# axis reduction
print(a.sum(axis=0).shape)  # (4, 2)
print(a.sum(axis=1).shape)  # (3, 2)
print(a.sum(axis=2).shape)  # (3, 4)
TEXT 📖 Display only
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
⚠️ Note: The code below needs to run in a local Python environment.



❓ FAQ

Q Can ndarray store different types?
A No. ndarray is homogeneous — all elements must share the same dtype. If you try to mix types, NumPy will auto-upcast (e.g., int → float). Incompatible types will raise an error.
Q Which is the row — axis=0 or axis=1?
A In a 2D array, axis=0 is the row direction (across rows), and axis=1 is the column direction (across columns). Summing along axis=0 "squeezes rows into one row"; summing along axis=1 "squeezes columns into one column."
Q What are strides?
A Strides is a tuple indicating how many bytes to skip in memory to move one step along each axis. They are the key to how ndarray can reshape/transpose without copying data.
Q What's the difference between float64 and float32?
A float64 takes 8 bytes with ~15 decimal digits of precision; float32 takes 4 bytes with ~7 digits. float32 uses half the memory but has lower precision. Deep learning commonly uses float32, while scientific computing typically uses float64.
Q Why is ndarray called "homogeneous"?
A Because all elements are packed tightly in memory, each taking the same number of bytes and interpreted the same way (determined by the shared dtype). This allows the CPU to process multiple elements at once with vectorized instructions — the fundamental reason for NumPy's high performance.

📖 Summary



📝 Exercises

  1. Beginner (Difficulty ⭐): Create four arrays of length 5000 with dtypes int8, int32, float32, and float64. Print each array's nbytes and explain why different dtypes use different amounts of memory.

  2. Intermediate (Difficulty ⭐⭐): Given an ndarray with shape=(5, 3, 2) and dtype=int16, calculate its strides and nbytes by hand, then verify your calculation with code.

  3. Advanced (Difficulty ⭐⭐⭐): Explain in your own words how an ndarray with shape=(2,3,4) is laid out in memory. Draw a diagram, label the strides, and show the memory offset for each element.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏