NumPy - Convert Array to List (tolist)

• NumPy's `tolist()` method converts arrays to native Python lists while preserving dimensional structure, enabling seamless integration with standard Python operations and JSON serialization

Key Insights

• NumPy’s tolist() method converts arrays to native Python lists while preserving dimensional structure, enabling seamless integration with standard Python operations and JSON serialization • Converting multi-dimensional arrays returns nested Python lists, with each dimension mapped to a corresponding list level, maintaining the original array shape • Performance trade-offs exist between NumPy arrays and Python lists—conversions should be intentional, typically for interoperability rather than computation

Basic Array to List Conversion

The tolist() method is NumPy’s primary mechanism for converting arrays to Python lists. This operation creates a deep copy of the data, transforming NumPy’s optimized array structure into Python’s native list type.

import numpy as np

# 1D array conversion
arr_1d = np.array([1, 2, 3, 4, 5])
list_1d = arr_1d.tolist()

print(f"Array type: {type(arr_1d)}")  # <class 'numpy.ndarray'>
print(f"List type: {type(list_1d)}")   # <class 'list'>
print(f"Content: {list_1d}")           # [1, 2, 3, 4, 5]

# Verify independence (deep copy)
list_1d[0] = 999
print(f"Array unchanged: {arr_1d[0]}")  # 1

The conversion creates completely independent data structures. Modifying the resulting list doesn’t affect the original array, as tolist() performs a deep copy rather than creating a view.

Multi-Dimensional Array Conversion

Multi-dimensional arrays convert to nested list structures, where each dimension becomes a list level. This preserves the array’s shape while transitioning to Python’s native data structures.

import numpy as np

# 2D array conversion
arr_2d = np.array([[1, 2, 3],
                    [4, 5, 6],
                    [7, 8, 9]])

list_2d = arr_2d.tolist()
print(list_2d)
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 3D array conversion
arr_3d = np.array([[[1, 2], [3, 4]],
                    [[5, 6], [7, 8]]])

list_3d = arr_3d.tolist()
print(list_3d)
# [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

# Accessing nested elements
print(list_2d[1][2])  # 6
print(list_3d[1][0][1])  # 6

The nesting depth matches the array’s number of dimensions. A 3D array produces a three-level nested list structure, maintaining all spatial relationships from the original array.

Data Type Conversion

NumPy arrays store data in specific dtypes optimized for computation. Converting to lists transforms these into Python’s native numeric types, which can affect precision and behavior.

import numpy as np

# Integer types
int_arr = np.array([1, 2, 3], dtype=np.int64)
int_list = int_arr.tolist()
print(type(int_list[0]))  # <class 'int'>

# Float types
float_arr = np.array([1.5, 2.7, 3.9], dtype=np.float32)
float_list = float_arr.tolist()
print(type(float_list[0]))  # <class 'float'>

# Complex numbers
complex_arr = np.array([1+2j, 3+4j])
complex_list = complex_arr.tolist()
print(complex_list)  # [(1+2j), (3+4j)]
print(type(complex_list[0]))  # <class 'complex'>

# Boolean arrays
bool_arr = np.array([True, False, True])
bool_list = bool_arr.tolist()
print(type(bool_list[0]))  # <class 'bool'>

NumPy’s specialized types like np.int64 and np.float32 convert to Python’s built-in int and float types. This ensures compatibility with standard Python operations but may introduce subtle differences in precision or range.

Handling Special Values

NumPy arrays can contain special floating-point values like infinity and NaN. These convert to Python’s equivalent representations.

import numpy as np
import math

# Special float values
special_arr = np.array([1.0, np.inf, -np.inf, np.nan])
special_list = special_arr.tolist()

print(special_list)  # [1.0, inf, -inf, nan]

# Verify Python equivalence
print(math.isinf(special_list[1]))   # True
print(math.isinf(special_list[2]))   # True
print(math.isnan(special_list[3]))   # True

# NaN comparison behavior persists
print(special_list[3] == special_list[3])  # False (NaN != NaN)

The special values maintain their mathematical properties after conversion. NaN remains not equal to itself, and infinity values retain their signed characteristics.

Alternative Conversion Methods

While tolist() is the standard approach, alternative methods exist for specific use cases. Understanding the differences helps choose the right tool.

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

# Using tolist() - recommended
list_tolist = arr.tolist()

# Using list() constructor - creates list of arrays
list_constructor = list(arr)
print(type(list_constructor[0]))  # <class 'numpy.ndarray'>

# Using nested comprehension - manual control
list_comprehension = [[item for item in row] for row in arr]

# Flattening multi-dimensional arrays
flat_list = arr.flatten().tolist()
print(flat_list)  # [1, 2, 3, 4, 5, 6]

# Raveling (similar to flatten but may return view)
ravel_list = arr.ravel().tolist()
print(ravel_list)  # [1, 2, 3, 4, 5, 6]

The list() constructor creates a shallow conversion, producing a list of NumPy arrays rather than nested Python lists. For complete conversion, tolist() remains the most reliable choice.

JSON Serialization Use Case

A common reason for converting arrays to lists is JSON serialization. NumPy arrays aren’t JSON-serializable by default, but Python lists are.

import numpy as np
import json

# Array with mixed data
data_arr = np.array([[1, 2, 3],
                      [4, 5, 6],
                      [7, 8, 9]])

# Direct serialization fails
try:
    json.dumps(data_arr)
except TypeError as e:
    print(f"Error: {e}")
    # Object of type ndarray is not JSON serializable

# Convert to list for serialization
data_list = data_arr.tolist()
json_str = json.dumps(data_list)
print(json_str)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Round-trip conversion
recovered_list = json.loads(json_str)
recovered_arr = np.array(recovered_list)
print(np.array_equal(data_arr, recovered_arr))  # True

This pattern is essential for API responses, configuration files, and data persistence where JSON is the exchange format.

Performance Considerations

Converting between NumPy arrays and Python lists involves overhead. Understanding performance implications guides architectural decisions.

import numpy as np
import time

# Large array performance test
large_arr = np.random.rand(1000, 1000)

# Measure conversion time
start = time.perf_counter()
large_list = large_arr.tolist()
conversion_time = time.perf_counter() - start
print(f"Conversion time: {conversion_time:.4f} seconds")

# Memory comparison
import sys
print(f"Array size: {large_arr.nbytes / 1024 / 1024:.2f} MB")
print(f"List size: {sys.getsizeof(large_list) / 1024 / 1024:.2f} MB")

# Operation performance
arr_for_calc = np.random.rand(10000)
list_for_calc = arr_for_calc.tolist()

# NumPy operation
start = time.perf_counter()
arr_result = arr_for_calc * 2 + 1
numpy_time = time.perf_counter() - start

# List operation
start = time.perf_counter()
list_result = [x * 2 + 1 for x in list_for_calc]
list_time = time.perf_counter() - start

print(f"NumPy: {numpy_time:.6f}s, List: {list_time:.6f}s")

NumPy arrays typically use less memory and provide significantly faster numerical operations. Convert to lists only when necessary for interoperability, not for computation.

Structured Arrays and Record Arrays

Structured arrays with named fields require special handling during conversion, as Python lists don’t natively support field names.

import numpy as np

# Structured array
dt = np.dtype([('name', 'U10'), ('age', 'i4'), ('score', 'f4')])
structured_arr = np.array([('Alice', 25, 92.5),
                            ('Bob', 30, 88.0),
                            ('Charlie', 28, 95.5)], dtype=dt)

# Convert to list of tuples
tuple_list = structured_arr.tolist()
print(tuple_list)
# [('Alice', 25, 92.5), ('Bob', 30, 88.0), ('Charlie', 28, 95.5)]

# Convert to list of dictionaries for named access
dict_list = [dict(zip(structured_arr.dtype.names, row)) 
             for row in structured_arr]
print(dict_list[0])
# {'name': 'Alice', 'age': 25, 'score': 92.5}

Structured arrays convert to tuples by default. For applications requiring named field access, manually construct dictionaries using the dtype’s field names.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.