Search code examples
pythonnumpymatplotlibscatter-plotintersection

How to scatter plot the intersecting values from arrays with different shapes


I have two arrays and I need to use them in a scatter plot with the consideration of their membership. For example, the first row of B is located at the second row of A, from column 2 to 3.

#A
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])

#B
array([[ 5,  6],
       [12, 13],
       [16, 17]])

I made the code below :

import numpy as np
import matplotlib.pyplot as plt

A = np.arange(20).reshape(5, 4)
B = np.array([[5, 6], [12, 13], [16, 17]])
x, y = np.meshgrid(range(A.shape[0]), range(A.shape[1]))

fig, ax = plt.subplots()
ax.scatter(x, y, facecolor='none', edgecolor='k', s=70, marker='s')

for ix, iy, a in zip(x.ravel(), y.ravel(), A.ravel()):
    plt.annotate(a, (ix,iy), textcoords='offset points', xytext=(0,7), ha='center', fontsize=14)

plt.axis("off")
ax.invert_yaxis()

plt.show()

Now, I can check if B is in A with np.isin(A, B) but I have two problems:

  1. The grid that doesn't reflect the shape of A (there is like an extra column at the right)
  2. The True values must be a filled x 'X' with black edge and same size and width as reds

enter image description here

Do you have any ideas on how to do that?


Solution

  • As per the comment by @chrslg, x, y = np.meshgrid(range(A.shape[1]), range(A.shape[0])) instead of x, y = np.meshgrid(range(A.shape[0]), range(A.shape[1])).

    np.isin(A, B) creates a Boolean array, which can be used to index x and y to insert an 'x' marker inside the 's' marker, for overlapping values.

    np.isin(A, B)
    array([[False, False, False, False],
           [False,  True,  True, False],
           [False, False, False, False],
           [ True,  True, False, False],
           [ True,  True, False, False]])
    
    import numpy as np
    import matplotlib.pyplot as plt
    
    A = np.arange(20).reshape(5, 4)
    B = np.array([[5, 6], [12, 13], [16, 17]])
    
    # reversed 1 and 0 on this line
    x, y = np.meshgrid(range(A.shape[1]), range(A.shape[0]))
    
    # create a Boolean of overlapping values
    idx_bool = np.isin(A, B)
    
    fig, ax = plt.subplots()
    ax.scatter(x, y, facecolor='r', edgecolor='k', s=70, marker='s')
    
    # use idx_bool to on x and y
    ax.scatter(x[idx_bool], y[idx_bool], facecolor='k', s=70, marker='x')
    
    for ix, iy, a in zip(x.ravel(), y.ravel(), A.ravel()):
        plt.annotate(a, (ix,iy), textcoords='offset points', xytext=(0,7), ha='center', fontsize=14)
    
    plt.axis("off")
    ax.invert_yaxis()
    
    plt.show()
    

    enter image description here

    Use the inverse of idx_bool to selectively add facecolor

    fig, ax = plt.subplots()
    
    # selectively plot red squares
    ax.scatter(x[~idx_bool], y[~idx_bool], facecolor='r', edgecolor='k', s=70, marker='s')
    
    # use idx_bool on x and y
    ax.scatter(x[idx_bool], y[idx_bool], facecolor='none', edgecolor='k', s=70, marker='s')  # remove this line if you don't want any squares on the True values
    ax.scatter(x[idx_bool], y[idx_bool], facecolor='k', s=70, marker='x')
    
    for ix, iy, a in zip(x.ravel(), y.ravel(), A.ravel()):
        plt.annotate(a, (ix,iy), textcoords='offset points', xytext=(0,7), ha='center', fontsize=14)
    
    plt.axis("off")
    ax.invert_yaxis()
    
    plt.show()
    

    enter image description here

    With ax.scatter(x[idx_bool], y[idx_bool], facecolor='none', edgecolor='k', s=70, marker='s') removed.

    enter image description here