Skillversitas

Creating NumPy Arrays

Arrays are created from existing data (np.array), from filled templates (zeros, ones, full, empty), or from numeric ranges (arange, linspace). The dtype parameter controls the element type, and getting it right at creation time - rather than converting later - is both faster and clearer. This chapter is the creation toolkit, which is also the chapter where the dtype habit starts.

From Existing Data

import numpy as np

a = np.array([1, 2, 3, 4])                 # int64 (platform default)
b = np.array([[1.0, 2.0], [3.0, 4.0]])     # float64, shape (2, 2)
c = np.array((1, 2, 3), dtype="int32")     # explicit dtype

print(a.dtype, a.shape)
print(b.dtype, b.shape)

# nested Python structures - the array takes on their regular shape
m = np.array([[1, 2, 3], [4, 5, 6]])
print(m.shape)          # (2, 3)

# ragged input needs explicit handling (a list of arrays, or dtype=object)
ragged = np.array([[1, 2], [3, 4, 5]], dtype=object)

np.array infers dtype from the data: ints become the platform's default integer (int64 on most systems), floats become float64, and mixed int/float promotes to float. Ragged (uneven) sequences cannot form a regular array - they either become an object array of arrays (dtype=object) or should be restructured, because object arrays lose the speed advantage entirely.

Filled Arrays

z = np.zeros((3, 4))              # 3x4 of 0.0 - the standard initialization
o = np.ones(5)                    # 5 of 1.0
f = np.full((2, 3), 7.5)          # every element 7.5
e = np.empty(4)                   # uninitialized - fast, values are whatever
i = np.zeros((2, 2), dtype=int)   # integer zeros
n = np.full(3, np.nan)            # NaN-filled (useful for "no value yet")
print(z, f, sep="\n")

zeros and ones are the default choice for accumulation and identity-like buffers. full is any constant. empty skips the fill for speed - use it only when you will write every element before reading, or the stale values will surprise you. zeros_like(a)/ones_like(a)/full_like(a, v) derive the shape (and optionally the dtype) from an existing array.

Ranges and Grids

r = np.arange(0, 10, 2)           # [0 2 4 6 8] - like range(), as an array
f = np.linspace(0.0, 1.0, 5)      # [0.   0.25 0.5  0.75 1. ] - endpoint included
t = np.linspace(0, 2 * np.pi, 100)  # the standard "sample a curve" range

# grids for 2-D functions
x = np.linspace(-1, 1, 5)
y = np.linspace(-2, 2, 4)
X, Y = np.meshgrid(x, y)          # X, Y each shape (4, 5) - a coordinate grid
print(X)
print(X.shape, Y.shape)

arange is the float-friendly range (mind floating-point accumulation at the end - prefer linspace when the endpoint matters). linspace is the right tool for "N evenly spaced points between a and b" and the default for sampling curves. meshgrid turns two 1-D coordinate vectors into the full 2-D coordinate pairs that imshow and contour expect - the standard entry point to 2-D function plotting.

Choosing the dtype

dtypeSizeUse for
float648 Bdefault for computation - the safe choice
float324 Bhalf the memory, half the speed cost - when precision allows
int64 / int328 / 4 Bcounts, indexes; int64 is the default for Python ints
bool_1 Bmasks and flags
uint81 Braw image bytes, serialized data

The rule: default to float64 for anything computed, drop to float32 when memory or throughput matters and the precision loss is acceptable (a decision to make deliberately, with a test), and use integer types only for data that is genuinely discrete. Mixing dtypes in an operation promotes to the wider type - a visible, predictable behavior, but one you should know about.

Summary

Create arrays with np.array for data, zeros/ones/full for buffers, empty only when you fill before you read, arange/linspace for ranges, and meshgrid for coordinate grids. Decide the dtype at creation - float64 by default, float32 as a deliberate memory trade - and treat ragged data as a signal to restructure, not to reach for dtype=object.