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¶
- ❗
shapevslen()—shapeis a tuple,len()is just the first dimension. - ❗
.Ton 1D arrays — no effect. Need to reshape(n,) → (n, 1)for a column. - ❗
intarrays +astype(bool)— non-zero →True. Zero →False. Surprising sometimes. - ❗
array.dtypeisdtype('int64')not the typeint— for comparisons usearr.dtype == np.int64.
Practice¶
What does this print?
Expected: (3, 4)
Print the TOTAL number of elements, not the row count
Expected: 12
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:
.sizeis the total number of elements (5 × 3 = 15)..shapeis 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
int32dtype - Modifies
ain place - Casts
ato a Python list of ints - Raises an error if
ahas 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)ora[:, 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 zero — 1.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.