NumPy Data Type System
A data type determines a value’s representation in memory and the operations NumPy can perform on it. A NumPy boolean occupies one byte, while a complex number stores separate real and imaginary components.
Every element in a regular NumPy array uses the same data type. This layout lets loops implemented by NumPy in C process elements without checking each value's type one by one.
Basic Data Types
NumPy accepts Python's built-in scalar types and also provides fixed-width scalar types when the exact representation matters:
- Boolean (
boolornp.bool_) forTrueandFalse, stored as one byte per element - Integer (
np.int8throughnp.int64) for signed whole numbers with explicit widths - Float (
np.float16,np.float32, ornp.float64) for floating-point values at different precisions - Complex (
np.complex64ornp.complex128) for values with real and imaginary components
import numpy as np
# Basic data type examples
bool_array = np.array([True, False, True], dtype=np.bool_)
print("Boolean array:", bool_array) # Output: Boolean array: [ True False True]
print("Dtype:", bool_array.dtype) # Output: Dtype: bool
int_array = np.array([1, 2, 3], dtype=np.int64)
print("Integer array:", int_array) # Output: Integer array: [1 2 3]
print("Dtype:", int_array.dtype) # Output: Dtype: int64
float_array = np.array([1.0, 2.5, 3.7], dtype=np.float64)
print("Float array:", float_array) # Output: Float array: [1. 2.5 3.7]
print("Dtype:", float_array.dtype) # Output: Dtype: float64Specific Numeric Data Types
NumPy provides detailed precision control with various sizes of numeric data types:
| Category | Data Type | Description | Value Range |
|---|---|---|---|
| Signed Integer | int8 | -bit signed integer | |
int16 | -bit signed integer | ||
int32 | -bit signed integer | ||
int64 | -bit signed integer | ||
| Unsigned Integer | uint8 | -bit unsigned integer | |
uint16 | -bit unsigned integer | ||
uint32 | -bit unsigned integer | ||
uint64 | -bit unsigned integer | ||
| Float | float16 | Half-precision float | , |
float32 | Single-precision float | , | |
float64 | Double-precision float | , | |
| Complex | complex64 | Complex number | Two -bit floats |
complex128 | Complex number | Two -bit floats |
Specifying Data Types
You can specify a data type when creating an array or convert the values into a new array with another dtype:
import numpy as np
# Specify data type during array creation
a = np.array([0, 1, 2], dtype=float)
print("Array with float dtype:", a) # Output: Array with float dtype: [0. 1. 2.]
print("Dtype:", a.dtype) # Output: Dtype: float64
# Default is float for ones function
a = np.ones((3, 3))
print("Default ones dtype:", a.dtype) # Output: Default ones dtype: float64
# Change to integer
a = np.ones((3, 3), dtype=np.int64)
print("Ones with int dtype:", a.dtype) # Output: Ones with int dtype: int64
print("Array:")
print(a)
# Output:
# [[1 1 1]
# [1 1 1]
# [1 1 1]]Automatic Data Type Detection
NumPy automatically detects data types based on provided elements:
import numpy as np
# All integers
a = np.array([0, 1, 2])
print("All int - dtype:", a.dtype) # Typical 64-bit output: All int - dtype: int64
# All floats
a = np.array([0., 1., 2.])
print("All float - dtype:", a.dtype) # Output: All float - dtype: float64
# Mixed int and float
a = np.array([0, 1, 2.])
print("Mixed - dtype:", a.dtype) # Output: Mixed - dtype: float64
print("Array result:", a) # Output: Array result: [0. 1. 2.]NumPy Array Attributes
Attributes describe the array structure without reading every value: its shape, element count, dtype, and bytes occupied by elements. nbytes measures element storage, not all Python process memory or the array object’s metadata.
Dimension and Shape Attributes
The following example reads the core structural attributes from one two-dimensional array.
import numpy as np
# Create 2D array as example
a = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.int64)
print("Array:")
print(a)
# Output:
# [[0 1 2]
# [3 4 5]]
print("Shape:", a.shape) # Output: Shape: (2, 3)
print("Ndim (dimensions):", a.ndim) # Output: Ndim (dimensions): 2
print("Size (total elements):", a.size) # Output: Size (total elements): 6
print("Dtype (data type):", a.dtype) # Output: Dtype (data type): int64
print("Bytes used by elements:", a.nbytes) # Output: Bytes used by elements: 48Array Attributes
An array reports its own structure through attributes. The table lists the ones you read most often and the value each one returns for a array.
| Attribute | Function | Example Result |
|---|---|---|
ndarray.shape | Number of elements in each axis | (2, 3) for array |
ndarray.ndim | Number of axes/dimensions | 2 for two-dimensional array |
ndarray.size | Total number of elements | 6 for array |
ndarray.dtype | Element data type | int64, float64, etc |
ndarray.nbytes | Bytes used by the array elements | 48 for six int64 values |
Data Type Consistency
All elements in a regular NumPy array share one data type. The numpy.dtype object explains how those values are stored and interpreted in memory. When inputs have different types, NumPy applies its promotion rules to find a compatible common dtype. Precision and type category determine the result. The number of times each input type appears does not affect this rule.
import numpy as np
# All integers
a = np.array([0, 1, 2])
print("All int - dtype:", a.dtype) # Typical 64-bit output: All int - dtype: int64
# All floats
a = np.array([0., 1., 2.])
print("All float - dtype:", a.dtype) # Output: All float - dtype: float64
# Mixed integer and float (automatically becomes float)
a = np.array([0, 1, 2.])
print("Mixed - dtype:", a.dtype) # Output: Mixed - dtype: float64
print("Mixed result:", a) # Output: Mixed result: [0. 1. 2.]Data Type Conversion and Manipulation
Convert when the required type differs from the current dtype, for example to satisfy a function’s input contract or reduce memory use. Conversion can lose precision or fractional values. Storing integers also does not guarantee that every later operation returns integers.
Data Type Conversion Methods
Data type conversion changes the representation used in later analysis. The astype() method returns a converted array. It does not change the original array in place. When converting a floating-point value to an integer, NumPy cuts off the fractional part toward zero.
import numpy as np
# Original float array
original = np.array([1.1, 2.7, 3.9])
print("Original array:", original) # Output: Original array: [1.1 2.7 3.9]
print("Original dtype:", original.dtype) # Output: Original dtype: float64
# Convert to integer using astype
converted = original.astype(np.int64)
print("Converted to int:", converted) # Output: Converted to int: [1 2 3]
print("Converted dtype:", converted.dtype) # Output: Converted dtype: int64
# Convert to specific data type
float32_array = original.astype(np.float32)
print("Float32 dtype:", float32_array.dtype) # Output: Float32 dtype: float32
# Convert string to integer
string_array = np.array(['1', '2', '3'])
int_from_string = string_array.astype(np.int64)
print("From string:", int_from_string) # Output: From string: [1 2 3]
print("String to int dtype:", int_from_string.dtype) # Output: String to int dtype: int64Memory Optimization with Data Types
Choosing the narrowest data type that still covers the required values and precision can reduce memory use substantially. Before narrowing a dtype, check its range so values do not overflow or lose needed precision.
import numpy as np
# Array with an explicit 64-bit integer data type
large_array_int64 = np.arange(1000000, dtype=np.int64)
print("Int64 itemsize:", large_array_int64.itemsize, "bytes") # Output: Int64 itemsize: 8 bytes
print("Int64 element memory:", large_array_int64.nbytes, "bytes") # Output: Int64 element memory: 8000000 bytes
# Array with smaller data type (int32)
large_array_int32 = np.arange(1000000, dtype=np.int32)
print("Int32 itemsize:", large_array_int32.itemsize, "bytes") # Output: Int32 itemsize: 4 bytes
print("Int32 element memory:", large_array_int32.nbytes, "bytes") # Output: Int32 element memory: 4000000 bytes
# Memory savings
memory_saved = large_array_int64.nbytes - large_array_int32.nbytes
print("Memory saved:", memory_saved, "bytes") # Output: Memory saved: 4000000 bytes
print("Memory saved percentage:", (memory_saved / large_array_int64.nbytes) * 100, "%") # Output: Memory saved percentage: 50.0 %Detailed Data Type Information
The itemsize attribute reports the number of bytes used by each element, so we can compare the precision and memory cost of different data types.
import numpy as np
# Create arrays with various data types
arrays = {
'int8': np.array([1, 2, 3], dtype=np.int8),
'int32': np.array([1, 2, 3], dtype=np.int32),
'int64': np.array([1, 2, 3], dtype=np.int64),
'float32': np.array([1.0, 2.0, 3.0], dtype=np.float32),
'float64': np.array([1.0, 2.0, 3.0], dtype=np.float64)
}
print("Data Type Information:")
print("=" * 50)
for name, arr in arrays.items():
print(f"{name:8} - itemsize: {arr.itemsize:2} bytes, dtype: {arr.dtype}")
# Output:
# Data Type Information:
# ==================================================
# int8 - itemsize: 1 bytes, dtype: int8
# int32 - itemsize: 4 bytes, dtype: int32
# int64 - itemsize: 8 bytes, dtype: int64
# float32 - itemsize: 4 bytes, dtype: float32
# float64 - itemsize: 8 bytes, dtype: float64Practical Array Attribute Usage
Array attributes report the array's structure and memory layout. Inspect them when a shape, type, or memory assumption causes an incorrect result or poor performance.
Data Structure Analysis
The following function reports shape, dimensions, dtype, and element memory together. The three arrays differ in their dimensions and element counts, while each uses int64.
import numpy as np
def analyze_array(arr, name="Array"):
"""Function to analyze array structure"""
print(f"\n=== Analysis {name} ===")
print(f"Shape: {arr.shape}")
print(f"Dimensions: {arr.ndim}")
print(f"Size: {arr.size}")
print(f"Data type: {arr.dtype}")
print(f"Item size: {arr.itemsize} bytes")
print(f"Element memory: {arr.nbytes} bytes")
if arr.ndim <= 2:
print(f"Array content:\n{arr}")
# Example analysis of various arrays
array_1d = np.array([1, 2, 3, 4, 5], dtype=np.int64)
array_2d = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int64)
array_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=np.int64)
analyze_array(array_1d, "1D")
analyze_array(array_2d, "2D")
analyze_array(array_3d, "3D")
# Output:
# === Analysis 1D ===
# Shape: (5,)
# Dimensions: 1
# Size: 5
# Data type: int64
# Item size: 8 bytes
# Element memory: 40 bytes
# Array content:
# [1 2 3 4 5]
#
# === Analysis 2D ===
# Shape: (2, 3)
# Dimensions: 2
# Size: 6
# Data type: int64
# Item size: 8 bytes
# Element memory: 48 bytes
# Array content:
# [[1 2 3]
# [4 5 6]]
#
# === Analysis 3D ===
# Shape: (2, 2, 2)
# Dimensions: 3
# Size: 8
# Data type: int64
# Item size: 8 bytes
# Element memory: 64 bytesData Validation and Debugging
Before passing an array to a model, check the shape, dtype, missing values, and memory footprint that the model actually expects.
import numpy as np
def validate_feature_matrix(arr):
"""Inspect a numeric feature matrix before modeling."""
print("=== ML Array Validation ===")
# Check dimensions
if arr.ndim != 2:
print(f"WARNING: Array is not 2D (current: {arr.ndim}D)")
else:
print(f"✓ 2D Array with shape: {arr.shape}")
# Check whether the matrix contains numeric values
if not np.issubdtype(arr.dtype, np.number):
print(f"WARNING: Data type may need conversion: {arr.dtype}")
return
print(f"✓ Numeric data type: {arr.dtype}")
# Check missing values (NaN)
if np.isnan(arr).any():
nan_count = np.isnan(arr).sum()
print(f"WARNING: Found {nan_count} NaN values")
else:
print("✓ No NaN values")
# Memory information
memory_mib = arr.nbytes / (1024 * 1024)
print(f"Memory usage: {memory_mib:.2f} MiB")
# Test with various arrays
test_arrays = [
np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64),
np.array([1, 2, 3, 4, 5], dtype=np.int64),
np.array([[1, 2, np.nan], [4, 5, 6]], dtype=np.float64)
]
for i, arr in enumerate(test_arrays):
print(f"\n--- Test Array {i+1} ---")
validate_feature_matrix(arr)
# Output:
# --- Test Array 1 ---
# === ML Array Validation ===
# ✓ 2D Array with shape: (2, 3)
# ✓ Numeric data type: float64
# ✓ No NaN values
# Memory usage: 0.00 MiB
#
# --- Test Array 2 ---
# === ML Array Validation ===
# WARNING: Array is not 2D (current: 1D)
# ✓ Numeric data type: int64
# ✓ No NaN values
# Memory usage: 0.00 MiB
#
# --- Test Array 3 ---
# === ML Array Validation ===
# ✓ 2D Array with shape: (2, 3)
# ✓ Numeric data type: float64
# WARNING: Found 1 NaN values
# Memory usage: 0.00 MiB