NumPy - Create Array of Constants (np.full)

The `np.full()` function creates an array of specified shape filled with a constant value. The basic signature is `numpy.full(shape, fill_value, dtype=None, order='C')`.

Key Insights

  • np.full() creates arrays filled with a constant value in a single operation, offering better performance and cleaner syntax than alternatives like np.ones() * value
  • The function supports custom data types, multi-dimensional shapes, and memory-efficient array creation for large datasets
  • Understanding np.full() versus np.full_like(), np.zeros(), and np.ones() helps you choose the right tool for array initialization patterns

Basic Syntax and Usage

The np.full() function creates an array of specified shape filled with a constant value. The basic signature is numpy.full(shape, fill_value, dtype=None, order='C').

import numpy as np

# Create a 1D array of five 7s
arr = np.full(5, 7)
print(arr)  # [7 7 7 7 7]

# Create a 2D array filled with 3.14
matrix = np.full((3, 4), 3.14)
print(matrix)
# [[3.14 3.14 3.14 3.14]
#  [3.14 3.14 3.14 3.14]
#  [3.14 3.14 3.14 3.14]]

# Create a 3D array filled with -1
cube = np.full((2, 3, 4), -1)
print(cube.shape)  # (2, 3, 4)

The shape parameter accepts either an integer for 1D arrays or a tuple for multi-dimensional arrays. The fill_value can be any scalar compatible with the desired data type.

Data Type Control

Explicit data type specification prevents unwanted type conversions and controls memory usage. NumPy infers the dtype from the fill_value by default, but explicit control is often necessary.

import numpy as np

# Default inference: float64
arr_default = np.full(5, 3.14)
print(arr_default.dtype)  # float64

# Explicit integer type
arr_int = np.full(5, 3.14, dtype=np.int32)
print(arr_int)  # [3 3 3 3 3]

# Boolean array
arr_bool = np.full(10, True, dtype=bool)
print(arr_bool)  # [True True True ... True]

# Complex numbers
arr_complex = np.full((2, 2), 1+2j, dtype=complex)
print(arr_complex)
# [[1.+2.j 1.+2.j]
#  [1.+2.j 1.+2.j]]

# String arrays
arr_str = np.full(3, "default", dtype='U10')
print(arr_str)  # ['default' 'default' 'default']

Memory-efficient types matter for large arrays. Using np.float32 instead of np.float64 halves memory consumption:

import numpy as np

# Compare memory usage
large_f64 = np.full((1000, 1000), 1.0, dtype=np.float64)
large_f32 = np.full((1000, 1000), 1.0, dtype=np.float32)

print(f"float64: {large_f64.nbytes / 1024 / 1024:.2f} MB")  # 7.63 MB
print(f"float32: {large_f32.nbytes / 1024 / 1024:.2f} MB")  # 3.81 MB

Performance Comparison

np.full() outperforms alternative approaches for creating constant arrays. Here’s a practical comparison:

import numpy as np
import time

size = (1000, 1000)
value = 42

# Method 1: np.full()
start = time.perf_counter()
arr1 = np.full(size, value)
time1 = time.perf_counter() - start

# Method 2: np.ones() * value
start = time.perf_counter()
arr2 = np.ones(size) * value
time2 = time.perf_counter() - start

# Method 3: np.empty() + assignment
start = time.perf_counter()
arr3 = np.empty(size)
arr3[:] = value
time3 = time.perf_counter() - start

print(f"np.full():        {time1*1000:.3f} ms")
print(f"np.ones() * val:  {time2*1000:.3f} ms")
print(f"np.empty() + =:   {time3*1000:.3f} ms")

# Typical output:
# np.full():        2.145 ms
# np.ones() * val:  3.892 ms
# np.empty() + =:   2.234 ms

np.full() combines initialization and assignment in a single optimized operation, avoiding the multiplication overhead of the np.ones() approach.

Using np.full_like()

np.full_like() creates an array with the same shape and dtype as an existing array, filled with a constant value. This is essential when maintaining consistency across related arrays.

import numpy as np

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

# Create matching array filled with zeros
matched = np.full_like(reference, 0)
print(matched)
# [[0 0 0]
#  [0 0 0]]
print(matched.dtype)  # int32

# Override dtype if needed
matched_float = np.full_like(reference, 0, dtype=np.float64)
print(matched_float.dtype)  # float64

Practical use case in data processing pipelines:

import numpy as np

def process_with_mask(data, threshold):
    """Apply threshold and create validity mask."""
    # Create mask with same shape as data
    valid_mask = np.full_like(data, False, dtype=bool)
    
    # Mark valid entries
    valid_mask[data > threshold] = True
    
    # Create result array initialized to NaN
    result = np.full_like(data, np.nan, dtype=float)
    result[valid_mask] = data[valid_mask] * 2
    
    return result, valid_mask

data = np.array([1.5, 2.3, 0.8, 3.1, 1.2])
processed, mask = process_with_mask(data, 1.0)
print(processed)  # [3.  4.6 nan 6.2 2.4]
print(mask)       # [True True False True True]

Practical Applications

Matrix Initialization for Algorithms

Many algorithms require matrices initialized to specific values:

import numpy as np

def initialize_weights(layers, init_value=0.01):
    """Initialize neural network weights."""
    weights = []
    for i in range(len(layers) - 1):
        # Small random values, but show structure with full
        w = np.full((layers[i], layers[i+1]), init_value)
        weights.append(w)
    return weights

# Create network: 784 inputs, 128 hidden, 10 outputs
network_shape = [784, 128, 10]
weights = initialize_weights(network_shape, 0.01)
print(f"Layer 1 shape: {weights[0].shape}")  # (784, 128)
print(f"Layer 2 shape: {weights[1].shape}")  # (128, 10)

Image Processing Masks

Creating masks for image manipulation:

import numpy as np

def create_border_mask(image_shape, border_width, border_value=0):
    """Create mask with border region marked."""
    mask = np.full(image_shape, 255, dtype=np.uint8)
    
    # Set border to border_value
    mask[:border_width, :] = border_value
    mask[-border_width:, :] = border_value
    mask[:, :border_width] = border_value
    mask[:, -border_width:] = border_value
    
    return mask

# Create 100x100 image mask with 10-pixel border
mask = create_border_mask((100, 100), 10, border_value=0)
print(f"Center value: {mask[50, 50]}")   # 255
print(f"Border value: {mask[5, 50]}")    # 0
print(f"Unique values: {np.unique(mask)}")  # [0 255]

Default Parameter Arrays

Creating default configuration arrays:

import numpy as np

class SimulationConfig:
    def __init__(self, grid_size, default_temperature=273.15):
        # Initialize temperature grid
        self.temperature = np.full(grid_size, default_temperature)
        
        # Initialize pressure grid (1 atm in Pa)
        self.pressure = np.full(grid_size, 101325.0)
        
        # Active cell mask
        self.active = np.full(grid_size, True, dtype=bool)
    
    def set_boundary_conditions(self, temp, pressure):
        """Set boundary to specific conditions."""
        self.temperature[0, :] = temp
        self.temperature[-1, :] = temp
        self.pressure[0, :] = pressure
        self.pressure[-1, :] = pressure

# Create 50x50 simulation grid
config = SimulationConfig((50, 50), default_temperature=298.15)
config.set_boundary_conditions(temp=273.15, pressure=100000.0)
print(f"Grid shape: {config.temperature.shape}")
print(f"Default temp: {config.temperature[25, 25]:.2f} K")
print(f"Boundary temp: {config.temperature[0, 25]:.2f} K")

Common Pitfalls

Watch for mutable fill values with object arrays:

import numpy as np

# Dangerous: all elements reference same list
bad_array = np.full(3, [], dtype=object)
bad_array[0].append(1)
print(bad_array)  # [list([1]) list([1]) list([1])]

# Correct: create independent lists
good_array = np.array([[] for _ in range(3)], dtype=object)
good_array[0].append(1)
print(good_array)  # [list([1]) list([]) list([])]

The np.full() function provides the most direct, performant way to create constant-value arrays in NumPy. Use it as your default choice for initialization, reserving np.zeros() and np.ones() only when semantic clarity matters or when working with code that specifically expects those patterns.

Liked this? There's more.

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