DataLens

Array attributes (shape, dtype, ndim)

Read an array's shape, size and element type, convert between types, and avoid silent overflow.

Level: Foundations · 9 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

When a DataFrame or a model complains, the first thing to check is the array underneath: what shape is it, and what type are its elements? "Expected 2D array, got 1D" and "cannot convert string to float" are among the most common errors in data work, and both are answered by an array's attributes.

Shape, ndim and size

This is the same table of units sold from the last module: three stores as rows, six months as columns. Three attributes tell you its size, which is the first thing to check whenever an error mentions dimensions:

import numpy as np

units = np.array([
    [120, 135, 150, 160, 155, 170],
    [ 80,  85,  90,  88,  95, 100],
    [ 95,  92, 105, 110, 108, 115],
])
print(units.shape)
print(units.ndim)
print(units.size)
(3, 6)
2
18
AttributeMeansHere
.shapesize along each dimension, as a tuple(3, 6): 3 rows, 6 columns
.ndimnumber of dimensions2
.sizetotal number of elements18

They're attributes, not methods, so there are no parentheses: units.shape, not units.shape().

A 1-D array's shape is a tuple with one number, written with a trailing comma:

daily = np.array([42, 38, 51, 47])
print(daily.shape, daily.ndim)
(4,) 1

(4,) is "4 elements along one dimension". Keep an eye out for the difference between (4,) and (4, 1) (4 rows, 1 column): machine learning libraries care about it a lot, and you'll learn to convert between them in the reshaping module.

dtype: the element type

Every array has one dtype (data type) shared by all its elements:

print(np.array([1.5, 2.0]).dtype)
print(np.array([True, False]).dtype)
print(np.array(["Jakarta", "Bandung"]).dtype)
float64
bool
<U7
dtypeHoldsBytes per element
int64, int32, int16, int8whole numbers8, 4, 2, 1
float64, float32decimals8, 4
boolTrue / False1
<U8text, up to 8 characters4 per character

The number in the name is the size in bits: int64 uses 64 bits = 8 bytes per element.

The default integer depends on your computer

What's the dtype of np.array([1, 2, 3])? On most laptops and servers it's int64. On this site it's int32, because Python here runs inside your browser on a 32-bit platform (WebAssembly). Older Windows installations of NumPy also used int32. Try it in the editor:

print(np.array([1, 2, 3]).dtype)
int32

When the integer size matters, for example for large money totals, say it explicitly with dtype=np.int64. Explicit is better than "whatever this machine does".

itemsize and nbytes: memory

The dtype also decides how much memory each number takes. That doesn't matter for small arrays, but it adds up quickly on large ones. These two attributes show you the cost:

prices = np.array([185.0, 320.0, 95.0, 65.0], dtype=np.float64)
print(prices.itemsize, prices.nbytes)

small = prices.astype(np.float32)
print(small.itemsize, small.nbytes)
8 32
4 16

itemsize is bytes per element, nbytes is the total. On four numbers it's trivia; on a 100-million-row array, float32 instead of float64 saves 400 MB. Machine learning often uses float32 for exactly that reason.

Converting types: astype

Sometimes an array has the right values but the wrong type for what comes next, like prices with decimals that need to be whole rupiah. .astype(new_dtype) returns a new array with every value converted to another type:

prices = np.array([185.9, 320.4, 95.5])
print(prices.astype(int))
print(prices.round().astype(int))
[185 320  95]
[186 320  96]
Warning

Converting floats to integers chops off the decimals (185.9 becomes 185); it doesn't round. Round first if you mean rounding.

It also converts text read from a file into numbers:

raw = np.array(["120", "135", "150"])
print(raw.astype(int).sum())
405

Overflow: when numbers don't fit

Each integer type has a range. An int8 holds -128 to 127. Go past the end, and the value wraps around silently:

stock = np.array([120, 125], dtype=np.int8)
print(stock + 10)
[-126 -121]

No error, no warning, just wrong numbers: 130 doesn't fit in int8, so it wrapped round to -126. Python's own integers never overflow, but NumPy's fixed-size ones do. int32 tops out around 2.1 billion, which a year of Rupiah revenue can easily pass.

print(np.iinfo(np.int32).max)
print(np.iinfo(np.int64).max)
2147483647
9223372036854775807

That's why this track stores Rupiah in thousands, and why you'd use int64 (or float64) for big money totals.

Why this matters for clean code

Shape and dtype bugs are silent: a wrong dtype gives wrong numbers, not errors. Make types explicit where they matter (dtype=np.int64 for money, float32 for big ML arrays), round before converting to integers, and when something behaves oddly, print .shape and .dtype first. It's the NumPy equivalent of checking your units in physics.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Indexing and slicing →