简体   繁体   中英

How to initialize locations of numpy array using dictionary keys and values?

I have the following numpy array which is basically a 3 channel image:

arr = np.zeros((6, 4, 3), dtype=np.float32)

# dictionary of values, key is array location
values_of_channel_0 = {
        (0, 2) : 1,  
        (1, 0) : 1,  
        (1, 3) : 5,  
        (2, 1) : 2,  
        (2, 2) : 3,  
        (2, 3) : 1,  
        (3, 0) : 1,  
        (3, 2) : 2,  
        (4, 0) : 2,  
        (4, 2) : 20,  
        (5, 0) : 1,  
        (5, 2) : 10, 
        (5, 3) : 1 
}

I am trying to find the most elegant way to set all the values of the 3rd channel according to the dictionary. Here is what I tried:

locations = list(values_of_channel_0.keys())
values = list(values_of_channel_0.values())
arr[lc,0] = values # trying to set the 3rd channel

But this fails.

Is there a way in which this can be done without looping over keys and values?

What's wrong with a simple loop? Something will have to iterate over the key/value-pairs you provide in your dictionary in any case?

import numpy as np

arr = np.zeros((6, 4, 3), dtype=np.float32)

# dictionary of values, key is array location
values_of_channel_0 = {
        (0, 2) : 1,
        (1, 0) : 1,
        (1, 3) : 5,
        (2, 1) : 2,
        (2, 2) : 3,
        (2, 3) : 1,
        (3, 0) : 1,
        (3, 2) : 2,
        (4, 0) : 2,
        (4, 2) : 20,
        (5, 0) : 1,
        (5, 2) : 10,
        (5, 3) : 1
}

for (a, b), v in values_of_channel_0.items():
    arr[a, b, 0] = v

print(arr)

Result:

[[[ 0.  0.  0.]
  [ 0.  0.  0.]
  [ 1.  0.  0.]
  [ 0.  0.  0.]]

 [[ 1.  0.  0.]
  [ 0.  0.  0.]
  [ 0.  0.  0.]
  [ 5.  0.  0.]]

 [[ 0.  0.  0.]
  [ 2.  0.  0.]
  [ 3.  0.  0.]
  [ 1.  0.  0.]]

 [[ 1.  0.  0.]
  [ 0.  0.  0.]
  [ 2.  0.  0.]
  [ 0.  0.  0.]]

 [[ 2.  0.  0.]
  [ 0.  0.  0.]
  [20.  0.  0.]
  [ 0.  0.  0.]]

 [[ 1.  0.  0.]
  [ 0.  0.  0.]
  [10.  0.  0.]
  [ 1.  0.  0.]]]

If you insist on not looping for the assignment, you can construct a data structure that can be assigned at once:

channel_0 = [[values_of_channel_0[b, a] if (b, a) in values_of_channel_0 else 0 for a in range(4)] for b in range(6)]

arr[..., 0] = channel_0

But this is clearly rather pointless and not even more efficient. If you have some control over how values_of_channel_0 is constructed, you could consider constructing it as a nested list or array of the right dimensions immediately, to allow for this type of assignment.

Users @mechanicpig and @michaelszczesny offer a very clean alternative (which will be more efficient since it relies on the efficient implementation of zip() ):

arr[(*zip(*values_of_channel_0), 0)] = list(values_of_channel_0.values())

Edit: you asked for an explanation of the lefthand side.

This hinges on the unpacking operator * . *values_of_channel_0 spreads all the keys of the dictionary values_of_channel_0 into a call to zip() . Since these keys are all 2- tuple s of int , zip will yield two tuples, one with all the first coordinates (0, 1, 1, ...) and the second with the second coordinates (2, 0, 3, ...) .

Since the call to zip() is also preceded by * , these two values will be spread to index arr[] , together with a final coordinate 0 . So this:

arr[(*zip(*values_of_channel_0), 0)] = ...

Is essentially the same as:

arr[((0, 1, 1, ...), (2, 0, 3, ...), 0)] = ...

That's a slice of arr with exactly the same number of elements as the dictionary, including all the elements with the needed coordinates. And so assigning list(values_of_channel_0.values()) to it works and has the desired effect of assigning the matching values to the correct coordinates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM