2

The problem I want to solve in a preferably numpythonic way is this: I have a list A of 2d indices, for example:

A = [(0, 3), (2, 2), (3, 1)]

My goal is to now get an array

[[H H H 0],
 [H H H H],
 [H H 1 H],
 [H 2 H H]]

Where H would be some default value (for example -1) So the problem is generally about inverting an array in this fashion.

If A is injective (no value appears twice) I can state it rigorously:

Let A be an injective array of 2d-indices. Then, generate a 2d-array B such that B[i, j] = A.index((i, j))

Or for A not necessarily injective:

Let A be an injective array of 2d-indices. Then, generate a 2d-array B such that A[B[i, j]] = (i, j)

More specifically in the non injective case we could resolve the situation with an additional "decider" function. Say

A = [(0, 3), (2, 2), (3, 1), (0, 3)]

Then to resolve the conflict between (0, 3) being in position 0 and 3, I would like to apply some function to equivalent indices to find a definite value.

As an example: In my case, specifically, I have a second array C with the same length as A. If there are several candidates (2d-indices) in A for one "position" in the final 2d array, the chosen one should be the one whose 1d index in A minimizes the value in C.

I hope the problem is made clear by these examples. Thank you for any help.

Edit: more example:

    A = [(0, 3), (2, 2), (3, 1)]
    print(my_dream_func(A, default=7)
    >>> [[7 7 7 0],
         [7 7 7 7],
         [7 7 1 7],
         [7 2 7 7]]

    A = [(0, 3), (2, 2), (3, 1), (0, 3)]
    print(my_dream_func(A, default=7))
    >>> Err: an index appears twice

an alternative for this scenario:

    def resolveFunc(indices):
        c = [0.5, 2.0, 3.4, -1.9]
        return(np.argmin(c[indices]))

    A = [(0, 3), (2, 2), (3, 1), (0, 3)]

    print(my_dream_func(A, resolveFunc, default=7))
    #now resolveFunc is executed on 0 and 3
    #because 0.5 > -1.9, 3 is chosen as the value for (0, 3)
    >>> [[7 7 7 3],
         [7 7 7 7],
         [7 7 1 7],
         [7 2 7 7]]
feedMe
  • 3,431
  • 2
  • 36
  • 61

2 Answers2

1

I would do it as follows:

In [11]: A = np.array([(0, 3), (2, 2), (3, 1)])

In [12]: a = np.full((len(A), len(A)), 7)  # here H = 7

In [13]: a
Out[13]:
array([[7, 7, 7, 7],
       [7, 7, 7, 7],
       [7, 7, 7, 7],
       [7, 7, 7, 7]])

In [14]: a[A[:, 0], A[:, 1]] = np.arange(len(A))

In [15]: a
Out[15]:
array([[7, 7, 7, 0],
       [7, 7, 7, 7],
       [7, 7, 1, 7],
       [7, 2, 7, 7]])

The "decider" function is last wins.

If you want to chose a different decider function, you could specify/modify the tuple list (and enumeration) first, rather than trying to do something clever in numpy...

Andy Hayden
  • 359,921
  • 101
  • 625
  • 535
  • Your `(len(A), len(A))` looks more like `A.max(axis=0)+1` to me. – Paul Panzer Dec 22 '18 at 06:59
  • Also, [officially](https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#detailed-notes), "last wins" is not guaranteed. That said, the numpy docs [aren't always 100% accurate](https://stackoverflow.com/a/48940730/7207392). – Paul Panzer Dec 22 '18 at 07:12
1

Numpy supports the simultaneous assignment of multiple values to multiple indizes. Using this the most numpythonic way to write your function would thus be:

import numpy as np

def f(idx, shape, default):
    arr = np.full(shape, default)
    arr[idx] = np.arange(0, len(idx))
    return arr

shape=(4,4)
default=7
idx=[(1,2),(0,3)]

print(f(idx, shape, default))

In case of duplicate indizes in idx, the last index tuple overwrites any predecessors.

7Z0nE
  • 21
  • 3
  • This assigns to `ret[1, 0]` and `ret[2, 3]` - the OP is asking for `ret[1, 2]` and `ret[0, 3]` – Eric Dec 23 '18 at 00:55