Search code examples
arraysnumpyview

given the index of an item in a view of a numpy array, find its index in the base array


Say a is a numpy array of shape (N,) and b = a[k:l]. I know that x = b[i], is there a way to find j such that x = a[j] without knowing k and l and without searching a for x?

For instance a = np.array([2,4,3,1,7]) and b = a[1:4]. I only have access to b, but would like to know what the index of 3 is in a, knowing that its index in b is 1.

Of course I could access a by using b.base, and then search a for the item 3, but I wondered wether there is a method attached to views which returns the index of an item in the base array.


Solution

  • As @hpaulj already has stated in the comments, there is no built in functionality to do so. But you can still compute the base's index from the size of the dtype and the byte offset between the base and the view. You can obtain the byte offset from the attribute ndarray.__array_interface__['data'][0]

    import numpy as np
    import unittest
    
    def baseIndex(array: np.ndarray, index: int) -> int:
        base = array.base
        if base is None:
            return index
        size = array.dtype.itemsize
        stride = array.strides[0] // size
        offset = (array.__array_interface__['data'][0] - base.__array_interface__['data'][0]) // size
        return offset + index * stride
    
    a = np.array([0,1,2,3,4,5,6])
    b = a
    class Test(unittest.TestCase):
    
        def test_1_simple(self):
            """b = a"""
            b = a
            i = 1
            j = baseIndex(b, i)
            self.assertEqual(a[j], b[i])
        
        def test_2_offset(self):
            """b = a[3:]"""
            b = a[3:]
            i = 1
            j = baseIndex(b, i)
            self.assertEqual(a[j], b[i])
        
        def test_3_strided(self):
            """b = a[1::2]"""
            b = a[1::2]
            i = 1
            j = baseIndex(b, i)
            self.assertEqual(a[j], b[i])
        
        def test_4_reverse_strided(self):
            """b = a[4::-2]"""
            b = a[4::-2]
            i = 1
            j = baseIndex(b, i)
            self.assertEqual(a[j], b[i])
    
    
    unittest.main(verbosity=2)
    

    Output:

    test_1_simple (__main__.Test)
    b = a ... ok
    test_2_offset (__main__.Test)
    b = a[3:] ... ok
    test_3_strided (__main__.Test)
    b = a[1::2] ... ok
    test_4_reverse_strided (__main__.Test)
    b = a[4::-2] ... ok
    
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
    
    OK
    

    EDIT: I have now updated the function to handle cases where b is non-contiguous and/or reverse, thanks @Jérôme Richard for spotting that. Also, as @mozway states, ndarray.__array_interface__ is an internal numpy detail that can change without notice, but as of now I don't see any other way to do so.