Skip to content

Python NumPy: How to Check and Define NumPy Array Data Types in Python

Learn how to inspect and set NumPy array data types using .dtype, type shortcuts like i4/f8, and memory-efficient type selection for integers, floats, strings, and structured arrays.

Every NumPy array has a dtype attribute that determines how data is stored in memory. Understanding and controlling data types is essential for memory optimization, numerical precision, and compatibility with external libraries.

Checking Data Types with .dtype

The .dtype attribute reveals an array's data type:

import numpy as np

int_arr = np.array([1, 2, 3])
print(int_arr.dtype)    # int64 (or int32 on Windows)

float_arr = np.array([1.0, 2.0, 3.0])
print(float_arr.dtype)  # float64

mixed = np.array([1, 2.5, 3])
print(mixed.dtype)      # float64

strings = np.array(['apple', 'banana'])
print(strings.dtype)    # <U6 (Unicode, 6 chars max)

Output:

int64
float64
float64
<U6

Detailed Type Information

import numpy as np

arr = np.array([1.0, 2.0, 3.0])

print(f"dtype: {arr.dtype}")                        # float64
print(f"dtype name: {arr.dtype.name}")              # 'float64'
print(f"item size: {arr.dtype.itemsize} bytes")     # 8
print(f"total bytes: {arr.nbytes}")                 # 24 (3 elements × 8 bytes)

Output:

dtype: float64
dtype name: float64
item size: 8 bytes
total bytes: 24

Defining Type at Creation

Specify dtype when creating arrays to control storage:

import numpy as np

arr = np.array([1, 2, 3], dtype='float32')
print(arr.dtype)    # float32
print(arr)          # [1. 2. 3.]

arr = np.array([1.9, 2.1, 3.5], dtype='int32')
print(arr)          # [1 2 3] (truncated!)

arr = np.array([0, 1, 2, 0], dtype='bool')
print(arr)          # [False  True  True False]

Output:

float32
[1. 2. 3.]
[1 2 3]
[False  True  True False]

With Array Creation Functions

import numpy as np

zeros = np.zeros((3, 3), dtype='int8')
print(zeros.dtype)      # int8

ones = np.ones((2, 2), dtype='float32')
print(ones.dtype)       # float32

sequence = np.arange(10, dtype='float16')
print(sequence.dtype)   # float16

linear = np.linspace(0, 1, 5, dtype='float32')
print(linear.dtype)     # float32

Output:

int8
float32
float16
float32

Type Specification Formats

NumPy accepts multiple formats for specifying types:

import numpy as np

np.array([1, 2, 3], dtype='float64')
np.array([1, 2, 3], dtype=np.float64)
np.array([1, 2, 3], dtype=float)
np.array([1, 2, 3], dtype='f8')  # shorthand

Type Shortcuts Reference

Symbol Meaning Examples
i Signed integer i1, i2, i4, i8
u Unsigned integer u1, u2, u4, u8
f Floating point f2, f4, f8
c Complex c8, c16
b Boolean b or bool
S Byte string S10 (10 chars)
U Unicode string U10 (10 chars)

The number indicates bytes: i4 = 4-byte (32-bit) integer, f8 = 8-byte (64-bit) float.

import numpy as np

arr_i4 = np.array([1, 2, 3], dtype='i4')    # int32
arr_f4 = np.array([1, 2, 3], dtype='f4')    # float32
arr_u1 = np.array([1, 2, 3], dtype='u1')    # uint8

print(f"i4: {arr_i4.dtype}")                # i4: int32
print(f"f4: {arr_f4.dtype}")                # f4: float32
print(f"u1: {arr_u1.dtype}")                # u1: uint8

Numeric Type Ranges

Understanding type limits prevents overflow:

import numpy as np

print(f"int8:  {np.iinfo(np.int8).min} to {np.iinfo(np.int8).max}")

print(f"int16: {np.iinfo(np.int16).min} to {np.iinfo(np.int16).max}")

print(f"uint8: {np.iinfo(np.uint8).min} to {np.iinfo(np.uint8).max}")

print(f"float32 precision: {np.finfo(np.float32).precision} digits")

print(f"float64 precision: {np.finfo(np.float64).precision} digits")

Output:

int8:  -128 to 127
int16: -32768 to 32767
uint8: 0 to 255
float32 precision: 6 digits
float64 precision: 15 digits

String Types: S vs U

NumPy supports both byte strings and Unicode strings:

import numpy as np

byte_arr = np.array(['hello', 'world'], dtype='S')
print(byte_arr)             # [b'hello' b'world']
print(byte_arr.dtype)       # |S5 (5-byte string)

unicode_arr = np.array(['hello', 'world'], dtype='U')
print(unicode_arr)          # ['hello' 'world']
print(unicode_arr.dtype)    # <U5 (5-character Unicode)

fixed = np.array(['hi', 'hello', 'hey'], dtype='U10')
print(fixed.dtype)          # <U10 (max 10 chars)

Output:

[b'hello' b'world']
|S5
['hello' 'world']
<U5
<U10

String Length Inference

import numpy as np

names = np.array(['Al', 'Bob', 'Charlotte'])
print(names.dtype)      # <U9 (longest name is 9 chars)

print(repr(names[0]))   # 'Al' (but stored as 9 chars)

Output:

<U9
np.str_('Al')

Type Checking and Validation

Check If Specific Type

import numpy as np

arr = np.array([1.0, 2.0, 3.0])

print(arr.dtype == np.float64)                  # True

print(np.issubdtype(arr.dtype, np.floating))    # True
print(np.issubdtype(arr.dtype, np.integer))     # False
print(np.issubdtype(arr.dtype, np.number))      # True

Output:

True
True
False
True

Common Type Checks

import numpy as np

def describe_array(arr):
    """Describe array type characteristics."""
    dtype = arr.dtype
    
    info = {
        'dtype': dtype.name,
        'is_integer': np.issubdtype(dtype, np.integer),
        'is_float': np.issubdtype(dtype, np.floating),
        'is_numeric': np.issubdtype(dtype, np.number),
        'is_bool': np.issubdtype(dtype, np.bool_),
        'is_string': np.issubdtype(dtype, np.str_),
        'bytes_per_element': dtype.itemsize
    }
    return info

print(describe_array(np.array([1, 2, 3])))
print(describe_array(np.array([1.0, 2.0])))
print(describe_array(np.array(['a', 'b'])))

Output:

{'dtype': 'int64', 'is_integer': True, 'is_float': False, 'is_numeric': True, 'is_bool': False, 'is_string': False, 'bytes_per_element': 8}
{'dtype': 'float64', 'is_integer': False, 'is_float': True, 'is_numeric': True, 'is_bool': False, 'is_string': False, 'bytes_per_element': 8}
{'dtype': 'str32', 'is_integer': False, 'is_float': False, 'is_numeric': False, 'is_bool': False, 'is_string': True, 'bytes_per_element': 4}

Memory Optimization

Choose appropriate types to reduce memory usage:

import numpy as np

large_default = np.arange(1_000_000)
print(f"Default (int64): {large_default.nbytes / 1e6:.1f} MB")

large_int16 = np.arange(1_000_000, dtype='int16')
print(f"int16: {large_int16.nbytes / 1e6:.1f} MB")

large_int8 = np.arange(256, dtype='int8')  # Only 256 values fit
print(f"int8: {large_int8.nbytes / 1e3:.1f} KB")

Output:

Default (int64): 8.0 MB
int16: 2.0 MB
int8: 0.3 KB

Choosing the Right Type

import numpy as np

def suggest_int_type(values):
    """Suggest smallest integer type that fits values."""
    min_val, max_val = min(values), max(values)
    
    if min_val >= 0:  # Unsigned
        if max_val <= 255:
            return 'uint8'
        elif max_val <= 65535:
            return 'uint16'
        elif max_val <= 4294967295:
            return 'uint32'
        else:
            return 'uint64'
    else:  # Signed
        if -128 <= min_val and max_val <= 127:
            return 'int8'
        elif -32768 <= min_val and max_val <= 32767:
            return 'int16'
        elif -2147483648 <= min_val and max_val <= 2147483647:
            return 'int32'
        else:
            return 'int64'

ages = [25, 30, 45, 60, 22]
print(suggest_int_type(ages))           # 'uint8'

temperatures = [-10, 5, 20, 35, -5]
print(suggest_int_type(temperatures))   # 'int8'

Output:

uint8
int8

Structured Arrays (Multiple Types)

Define arrays with multiple named fields of different types:

import numpy as np

person_dtype = np.dtype([
    ('name', 'U20'),
    ('age', 'i4'),
    ('salary', 'f8')
])

people = np.array([
    ('Alice', 30, 75000.0),
    ('Bob', 25, 65000.0),
    ('Charlie', 35, 85000.0)
], dtype=person_dtype)

print(people['name'])    # ['Alice' 'Bob' 'Charlie']
print(people['age'])     # [30 25 35]
print(people[0])         # ('Alice', 30, 75000.)

Output:

['Alice' 'Bob' 'Charlie']
[30 25 35]
('Alice', 30, 75000.0)

Quick Reference

Task Code
Check type arr.dtype
Check type name arr.dtype.name
Bytes per element arr.dtype.itemsize
Total bytes arr.nbytes
Set type at creation np.array([...], dtype='f4')
Convert type arr.astype('int32')
Check if integer np.issubdtype(arr.dtype, np.integer)
Check if float np.issubdtype(arr.dtype, np.floating)
Get type limits np.iinfo(np.int8) / np.finfo(np.float32)

Summary

Always verify array types with .dtype when debugging numerical issues or unexpected behavior.

Specify dtype at creation for memory optimization-use float32 instead of float64 for ML workloads, and choose the smallest integer type that fits your data range.

Understanding type shortcuts like i4 and f8 makes code more concise while maintaining clarity.

Navigation