Skip to content

Python NumPy: How to Find the Memory Size of a NumPy Array in Python

Learn how to check NumPy array memory usage with .nbytes, understand data type impact on memory consumption, and optimize storage for large datasets in Python.

Understanding memory consumption is essential when working with large datasets in scientific computing and machine learning. NumPy arrays are designed for memory efficiency, storing data in contiguous blocks rather than as scattered Python objects. Knowing exactly how much RAM your arrays consume helps you optimize data pipelines, prevent out-of-memory errors, and choose appropriate data types for your hardware constraints.

The .nbytes attribute provides the most direct way to get total memory used by array elements:

import numpy as np

small_array = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
large_array = np.zeros((1000, 1000))

print(f"Small array: {small_array.nbytes} bytes")
print(f"Large array: {large_array.nbytes:,} bytes")
print(f"Large array: {large_array.nbytes / (1024**2):.2f} MB")

Output:

Small array: 40 bytes
Large array: 8,000,000 bytes
Large array: 7.63 MB

Understanding Array Memory Components

NumPy provides attributes to understand how memory is calculated:

import numpy as np

matrix = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int32)

print(f"Shape: {matrix.shape}")                 # Shape: (2, 3)
print(f"Total elements: {matrix.size}")         # Total elements: 6
print(f"Bytes per element: {matrix.itemsize}")  # Bytes per element: 4
print(f"Data type: {matrix.dtype}")             # Data type: int32
print(f"Total bytes: {matrix.nbytes}")          # Total bytes: 24

assert matrix.size * matrix.itemsize == matrix.nbytes

The relationship is straightforward:

  • size: Total number of elements
  • itemsize: Bytes per element
  • nbytes: Total bytes = size × itemsize

Impact of Data Types on Memory

Choosing the right dtype dramatically affects memory consumption:

import numpy as np

n_elements = 1_000_000

arrays = {
    'int8': np.zeros(n_elements, dtype=np.int8),
    'int32': np.zeros(n_elements, dtype=np.int32),
    'int64': np.zeros(n_elements, dtype=np.int64),
    'float32': np.zeros(n_elements, dtype=np.float32),
    'float64': np.zeros(n_elements, dtype=np.float64),
    'complex128': np.zeros(n_elements, dtype=np.complex128),
}

print("Memory usage for 1 million elements:")
for dtype_name, arr in arrays.items():
    mb = arr.nbytes / (1024 ** 2)
    print(f"  {dtype_name:12}: {mb:6.2f} MB ({arr.itemsize} bytes/element)")

Output:

Memory usage for 1 million elements:
  int8        :   0.95 MB (1 bytes/element)
  int32       :   3.81 MB (4 bytes/element)
  int64       :   7.63 MB (8 bytes/element)
  float32     :   3.81 MB (4 bytes/element)
  float64     :   7.63 MB (8 bytes/element)
  complex128  :  15.26 MB (16 bytes/element)

Data Type Reference

dtype Bytes Value Range Use Case
bool 1 True/False Flags, masks
int8 1 -128 to 127 Small integers
uint8 1 0 to 255 Image pixels, ages
int32 4 ±2.1 billion General integers
int64 8 ±9.2 quintillion Large counts
float32 4 ±3.4×10³⁸ ML models, graphics
float64 8 ±1.8×10³⁰⁸ Scientific computing
complex128 16 Complex numbers Signal processing

NumPy vs Python List Memory

NumPy arrays are far more memory-efficient than Python lists for numeric data:

import numpy as np
import sys

n = 10000

py_list = list(range(n))

np_array = np.arange(n, dtype=np.int64)

list_size = sys.getsizeof(py_list)  # Just the list structure

array_size = np_array.nbytes

print(f"NumPy array data: {array_size:,} bytes")
print(f"Python list structure: {list_size:,} bytes")
print(f"Python list estimate (with objects): ~{n * 28 + list_size:,} bytes")

Output:

NumPy array data: 80,000 bytes
Python list structure: 80,056 bytes
Python list estimate (with objects): ~360,056 bytes

Memory Profiling Utility

Create a helper function for detailed memory analysis:

import numpy as np

def memory_report(arr, name="Array"):
    """Generate detailed memory report for a NumPy array."""
    bytes_total = arr.nbytes
    
    if bytes_total >= 1024**3:
        size_str = f"{bytes_total / 1024**3:.2f} GB"
    elif bytes_total >= 1024**2:
        size_str = f"{bytes_total / 1024**2:.2f} MB"
    elif bytes_total >= 1024:
        size_str = f"{bytes_total / 1024:.2f} KB"
    else:
        size_str = f"{bytes_total} bytes"
    
    print(f"=== {name} ===")
    print(f"  Shape: {arr.shape}")
    print(f"  Dtype: {arr.dtype}")
    print(f"  Elements: {arr.size:,}")
    print(f"  Bytes/element: {arr.itemsize}")
    print(f"  Total memory: {size_str}")
    
    return bytes_total


image = np.random.randint(0, 256, (1920, 1080, 3), dtype=np.uint8)
features = np.random.randn(50000, 768).astype(np.float32)

memory_report(image, "HD Image")
print()
memory_report(features, "ML Features")

Output:

=== HD Image ===
  Shape: (1920, 1080, 3)
  Dtype: uint8
  Elements: 6,220,800
  Bytes/element: 1
  Total memory: 5.93 MB

=== ML Features ===
  Shape: (50000, 768)
  Dtype: float32
  Elements: 38,400,000
  Bytes/element: 4
  Total memory: 146.48 MB

Estimating Memory Before Allocation

Calculate required memory before creating large arrays:

import numpy as np

def estimate_memory(shape, dtype=np.float64):
    """Estimate memory needed for an array without creating it."""
    dtype = np.dtype(dtype)
    elements = np.prod(shape)
    bytes_needed = elements * dtype.itemsize
    
    gb = bytes_needed / (1024**3)
    return bytes_needed, f"{gb:.2f} GB" if gb >= 1 else f"{bytes_needed / 1024**2:.2f} MB"


shape = (100000, 10000)

for dtype in [np.float64, np.float32, np.float16]:
    bytes_needed, readable = estimate_memory(shape, dtype)
    print(f"{dtype}: {readable}")

Output:

<class 'numpy.float64'>: 7.45 GB
<class 'numpy.float32'>: 3.73 GB
<class 'numpy.float16'>: 1.86 GB

By understanding NumPy's memory model and choosing appropriate data types, you can build applications that efficiently handle datasets ranging from megabytes to terabytes.

Navigation