Skip to content

Array Attributes — shape, dtype, ndim, size

Every NumPy array has metadata you can inspect with attributes. These are the most useful ones.

The big four

Attribute What it tells you
.shape A tuple — size in each dimension
.ndim Number of dimensions
.size Total number of elements
.dtype The element type
import numpy as np

a = np.array([
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
])

print("shape:", a.shape)    # (3, 4) — 3 rows, 4 cols
print("ndim :", a.ndim)     # 2
print("size :", a.size)     # 12  (3*4)
print("dtype:", a.dtype)    # int64

.shape is the most important one

It's a tuple (d0, d1, d2, ...) describing dimensions.

import numpy as np

a1 = np.array([1, 2, 3])                              # 1D
a2 = np.array([[1, 2, 3], [4, 5, 6]])                  # 2D
a3 = np.ones((2, 3, 4))                                # 3D

print(a1.shape)        # (3,)        — note the comma!
print(a2.shape)        # (2, 3)
print(a3.shape)        # (2, 3, 4)

(3,) not (3) — that's because (3) in Python is just an integer in parentheses. The trailing comma marks a tuple.

Visualizing dimensions

ndim What it looks like Real-world
1 Vector — [1, 2, 3] a row of values, time series
2 Matrix — table spreadsheet, dataset (rows × cols)
3 Cube of data RGB image (height × width × 3), time series of matrices
4 Hyper-cube batch of images (batch × height × width × channels)
import numpy as np

# A 28x28 grayscale image (like an MNIST digit)
image = np.ones((28, 28))
print("image:    ", image.shape, "ndim=", image.ndim)

# An RGB image of the same size — 3 color channels
rgb = np.ones((28, 28, 3))
print("rgb:      ", rgb.shape, "ndim=", rgb.ndim)

# A batch of 64 RGB images
batch = np.ones((64, 28, 28, 3))
print("batch:    ", batch.shape, "ndim=", batch.ndim, "size=", batch.size)

.dtype — the data type

Each NumPy array holds one type of element. Common dtypes:

dtype Bytes per element Use for
bool 1 True/False masks
int8 / int16 / int32 / int64 ½/4/8 Signed integers
uint8 / uint16 / uint32 / uint64 ½/4/8 Unsigned (no negatives)
float16 / float32 / float64 2/4/8 Decimals
complex64 / complex128 8/16 Complex numbers
import numpy as np

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

b = np.array([1.5, 2.5])
print(b.dtype)                          # float64

c = np.array([True, False, True])
print(c.dtype)                          # bool

# Specify dtype explicitly
d = np.zeros(5, dtype=np.float32)
print(d.dtype)

Converting dtype — .astype()

import numpy as np

a = np.array([1.7, 2.3, 5.9])
print(a, a.dtype)

# Convert to int — decimals dropped
b = a.astype(np.int32)
print(b, b.dtype)

# Convert to bool — anything non-zero → True
c = a.astype(bool)
print(c)

Memory used — .nbytes

Useful for big-data work.

import numpy as np

a = np.zeros(1_000_000, dtype=np.float64)
b = np.zeros(1_000_000, dtype=np.float32)
c = np.zeros(1_000_000, dtype=np.int8)

print("float64:", a.nbytes / 1024, "KB")
print("float32:", b.nbytes / 1024, "KB")
print("int8   :", c.nbytes / 1024, "KB")

For a million points, float32 uses half the memory of float64 — and on modern CPUs, often faster too.

.T — transpose

A property — not a method. Flips rows and columns:

import numpy as np

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

print("Original:")
print(a)
print("shape:", a.shape)

print("\nTransposed:")
print(a.T)
print("shape:", a.T.shape)

For 1D arrays, .T does nothing — there's no second dimension to swap. To make a 1D array into a column vector, reshape it (next chapter).

.flat — iterate flat

import numpy as np

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

# Element-by-element regardless of shape
for x in a.flat:
    print(x, end=" ")

len() returns the size of the FIRST dimension

import numpy as np

a = np.zeros((5, 3))     # 5 rows, 3 cols
print(len(a))            # 5 (number of rows)
print(a.size)            # 15 (total elements)

This trips up beginners. Use .size for "total count of elements."

Putting it all together — a 2-line summary of any array

import numpy as np

def describe(arr):
    print(f"shape={arr.shape}  ndim={arr.ndim}  size={arr.size}  "
          f"dtype={arr.dtype}  nbytes={arr.nbytes}")

describe(np.zeros((28, 28)))
describe(np.ones((64, 28, 28, 3), dtype=np.uint8))
describe(np.array([1, 2, 3]))

Common pitfalls

  • shape vs len()shape is a tuple, len() is just the first dimension.
  • .T on 1D arrays — no effect. Need to reshape (n,) → (n, 1) for a column.
  • int arrays + astype(bool) — non-zero → True. Zero → False. Surprising sometimes.
  • array.dtype is dtype('int64') not the type int — for comparisons use arr.dtype == np.int64.

Practice

What does this print?

Expected: (3, 4)

import numpy as np
a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(a.shape)

Print the TOTAL number of elements, not the row count

Expected: 12

import numpy as np
a = np.zeros((3, 4))
print(len(a))      # bug: len returns first-axis size only

Quiz — Quick check

What you remember

Q1. For a 2D array a with shape (5, 3), what does a.size return?

  • 5
  • 3
  • (5, 3)
  • 15

Why: .size is the total number of elements (5 × 3 = 15). .shape is the tuple (5, 3). len(a) is just the first-axis length (5).

Q2. What does a.astype(np.int32) do?

  • Returns a new array converted to int32 dtype
  • Modifies a in place
  • Casts a to a Python list of ints
  • Raises an error if a has floats

Why: .astype() always returns a new array — it never modifies the original. Float values are truncated (toward zero) when converted to int.

Q3. What's a.T on a 1D array of shape (5,)?

  • Shape becomes (1, 5)
  • Shape becomes (5, 1)
  • Unchanged — still shape (5,)
  • Raises an error

Why: Transpose swaps axes. A 1D array has only one axis — nothing to swap. To make it a column, reshape: a.reshape(-1, 1) or a[:, np.newaxis].

Common doubts

Why is shape (3,) and not (3)?

(3) in Python is just the number 3 in parentheses — the parens group, they don't make a tuple. (3,) with the trailing comma is a single-element tuple. NumPy uses the tuple form so shape is always consistent (a tuple) regardless of ndim.

What's the difference between len(a) and a.size?

len(a) returns the length of the first axis only. a.size returns the total element count across all axes. For a (3, 4) matrix: len is 3, .size is 12.

When does .astype(int) truncate vs round?

Always truncates toward zero1.9 → 1, -1.9 → -1. If you want proper rounding, use np.round(a).astype(int) instead. Same rule as Python's int() on floats.

What's next

Indexing & Slicing