Search code examples
pythonvectordirection

Quiver vector from high value toward low value


What I would like to plot is to make vector from high values to low values. If code would start from:

a = [[1, 8, 9, 10],[2, 15, 3, -1],[3,1,6,11],[13,15,5,-2]]
X,Y = np.meshgrid(np.arange(4), np.arange(4))
U = ?
V = ?

From this point, I should set U and V components of the vector. The magnitude of each point would be a[x][y]. I don't have much idea of how I can set U and V to make arrow from high to low value at each grid point.


Solution

  • Here's a solution (doesn't require numpy):

    import itertools as it
    
    a = [[1, 8, 9, 10],[2, 15, 3, -1],[3,1,6,11],[13,15,5,-2]]
    rowSize = len(a[0])
    
    maxVal = a[0][0]
    maxIndex = 0
    minVal = a[0][0]
    minIndex = 0
    
    for k, v in enumerate(it.chain(*a)):  # Loop through a flattened list of the values in the array, and locate the indices of the max and min values.
        if v > maxVal:
            maxVal = v
            maxIndex = k    
        if v < minVal:
            minVal = v
            minIndex = k
    
    U = (minIndex % rowSize) - (maxIndex % rowSize)
    V = (minIndex / rowSize) - (maxIndex / rowSize)
    
    print U, ",", V
    

    OUTPUT

    2 , 2
    

    Note that you haven't defined what behavior you want when there are two equal maximum values, as there are in your example. The code above uses the "first" (upper-leftmost) one as the true maximum, and ignores all others.

    Explanation:

    I flattened the list (which means I read the values like you would the words on a book - first the first row, then the second row, then the third row). Each value got a single index, like so:

    0  1  2  3
    4  5  6  7
    8  9  10 11
    12 13 14 15
    

    For example, a value in the second row and the third column would get an index of 6, since it's the 7th value if you read the array like a book.

    At the end, when we've found the index of the max or min value, we need to get 2D coordinates from the 1D index. So, we can use the mod operator (%) to get the x-value.

    For example, 6 % 4 = 2, so X = 2 (the 3rd column)

    To get the Y value, we use the integer division operator (/).

    For example, 6 / 4 = 1, so Y = 1 (the second row)

    The formulas for U and V are simply taking the X and Y values for the max and min and subtracting them to get the vector coordinates, like so:

    U = xMin - xMax
    V = yMin - yMax
    

    If you're wondering, "why the heck didn't he just use the meshgrid solution I started with", there are two reasons: One, using a non-standard library like numpy is generally undesirable if there is an easy way to solve the problem without non-standard libraries, and two, if you ever need to deal with large arrays, generating a large meshgrid could become time/memory expensive.

    Solution that picks shortest vector:

    import itertools as it
    
    a = [[1, 8, 9, 10],[2, 15, 3, -1],[3,1,6,11],[13,15,5,-2]]
    rowSize = len(a[0])
    
    values = sorted(enumerate(it.chain(*a)), key=lambda x:x[1])  # Pair each value with its 1D index, then sort the list.
    
    minVal = values[0][1]
    maxVal = values[-1][1]
    
    maxIndices = map(lambda x:x[0], filter(lambda x:x[1]==maxVal, values))  # Get a list of all the indices that match the maximum value
    minIndices = map(lambda x:x[0], filter(lambda x:x[1]==minVal, values))  # Get a list of all the indices that match the minimum value
    
    def getVector(index1, index2, rowSize):  # A function that translates a pair of 1D index values to a "quiver vector"
        return ((index1 % rowSize) - (index2 % rowSize), (index1 / rowSize) - (index2 / rowSize))
    
    vectors = [getVector(k2, k1, rowSize) for k1, k2 in it.product(maxIndices, minIndices)]  # produce a list of the vectors formed by all possible combinations of the 1D indices for maximum and minimum values
    
    U, V = sorted(vectors, key=lambda x:(x[0]*x[0] + x[1]*x[1])**0.5)[0]
    
    print U, ",", V
    

    OUTPUT

    2 , 0