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
| Attribute | Means | Here |
|---|---|---|
.shape | size along each dimension, as a tuple | (3, 6): 3 rows, 6 columns |
.ndim | number of dimensions | 2 |
.size | total number of elements | 18 |
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
| dtype | Holds | Bytes per element |
|---|---|---|
int64, int32, int16, int8 | whole numbers | 8, 4, 2, 1 |
float64, float32 | decimals | 8, 4 |
bool | True / False | 1 |
<U8 | text, up to 8 characters | 4 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]
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
.shape(tuple of sizes),.ndim(number of dimensions),.size(total elements)..dtypeis the element type;.itemsizeand.nbytesgive memory use.- The default integer is
int64on most computers butint32here: be explicit when it matters. .astype(type)converts; float → int chops decimals, soround()first.- Fixed-size integers overflow silently; check limits with
np.iinfo.