Home > Software engineering >  given the index of an item in a view of a numpy array, find its index in the base array
given the index of an item in a view of a numpy array, find its index in the base array

Time:01-02

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.

CodePudding user response:

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.

  • Related