How to calculate pairwise distances with SciPy

Geometric and feature-based workflows often need a numeric measure of how far observations lie from one another before clustering, matching, or thresholding can begin. SciPy performs these comparisons directly on rows of NumPy arrays through scipy.spatial.distance.

The pdist() function compares every row in one array and returns a condensed vector containing each unique pair. squareform() expands that vector into a symmetric matrix whose row and column positions match the original observation order.

Cross-set comparisons use cdist() to measure every query row against every reference row. Both arrays need the same number of feature columns, and structural assertions can check the resulting shapes, symmetry, and zero diagonal without relying on a fixed success message.

Steps to calculate pairwise distances with SciPy:

  1. Create pairwise_distance.py with the imports and reference-point array.
    pairwise_distance.py
    import numpy as np
    from scipy.spatial.distance import cdist, pdist, squareform
     
    np.set_printoptions(precision=3, suppress=True)
     
    reference_points = np.array([
        [0.0, 0.0],
        [3.0, 4.0],
        [6.0, 8.0],
    ])

    Each row is one observation, and each column is one feature used in the distance calculation.

  2. Add the within-set Euclidean calculations after the reference-point array.
    condensed_distances = pdist(reference_points, metric="euclidean")
    distance_matrix = squareform(condensed_distances)

    pdist() stores the three unique distances for three rows. squareform() restores their row-to-row positions in a 3 by 3 matrix.

  3. Add the query-point array and cross-set calculation after distance_matrix.
    query_points = np.array([
        [0.0, 4.0],
        [9.0, 12.0],
    ])
     
    query_distances = cdist(
        query_points,
        reference_points,
        metric="euclidean",
    )

    cdist() raises ValueError when the query and reference arrays have different feature-column counts.

  4. Append the distance displays after the cdist() call.
    print("Condensed reference distances:")
    print(condensed_distances)
    print("\nReference distance matrix:")
    print(distance_matrix)
    print("\nQuery-to-reference distances:")
    print(query_distances)
  5. Append the structural assertions after the result displays.
    np.testing.assert_allclose(distance_matrix, distance_matrix.T)
    np.testing.assert_allclose(np.diag(distance_matrix), 0.0)
    assert distance_matrix.shape == (3, 3)
    assert query_distances.shape == (2, 3)

    A wrong matrix shape, nonzero diagonal, or asymmetric within-set matrix stops the program with an assertion error.

  6. Run the completed script to produce both asserted distance matrices.
    $ python3 pairwise_distance.py
    Condensed reference distances:
    [ 5. 10.  5.]
    
    Reference distance matrix:
    [[ 0.  5. 10.]
     [ 5.  0.  5.]
     [10.  5.  0.]]
    
    Query-to-reference distances:
    [[ 4.     3.     7.211]
     [15.    10.     5.   ]]

    The command exits without an assertion error only when the two output shapes and the structural properties of the within-set matrix match the source arrays.