How to pythonically select a random index from a 2D list such that the corresponding element matches a value?

╄→尐↘猪︶ㄣ 提交于 2021-02-19 23:41:08

问题


I have a 2D list of booleans. I want to select a random index from the the list where the value is False. For example, given the following list:

[[True, False, False],
 [True, True, True],
 [False, True, True]]

The valid choices would be: [0, 1], [0, 2], and [2, 0].

I could keep a list of valid indices and then use random.choice to select from it, but it seems unpythonic to keep a variable and update it every time the underlying list changes for only this one purpose.

Bonus points if your answer runs quickly.


回答1:


We can use a oneliner like:

import numpy as np
from random import choice

choice(np.argwhere(~a))

With a the array of booleans.

This works as follows: by using ~a, we negate the elements of the array. Next we use np.argwhere to construct a k×2-array: an array where every row has two elements: for every dimension the value such that the corresponding value has as value False.

By choice(..) we thus select a random row. We can however not use this directly to access the element. We can use the tuple(..) constructor to cast it to a tuple:

>>> tuple(choice(np.argwhere(~a)))
(2, 0)

You can thus fetch the element then with:

t = tuple(choice(np.argwhere(~a)))
a[t]

But of course, it is not a surprise that:

>>> t = tuple(choice(np.argwhere(~a)))
>>> a[t]
False



回答2:


My non-numpy version:

result = random.choice([
    (i,j)
    for i in range(len(a)) 
    for j in range(len(a[i])) 
    if not a[i][j]])

Like Willem's np version, this generates a list of valid tuples and invokes random.choice() to pick one.

Alternatively, if you hate seeing range(len(...)) as much as I do, here is an enumerate() version:

result = random.choice([
    (i, j)
    for i, row in enumerate(a)
    for j, cell in enumerate(row)
    if not cell])



回答3:


Assuming you don't want to use numpy.

matrix = [[True, False, False],
          [True, True, True],
          [False, True, True]]

valid_choices = [(i,j) for i, x in enumerate(matrix) for j, y in enumerate(x) if not y]
random.choice(valid_choices)

With list comprehensions, you can change the if condition (if not y) to suit your needs. This will return the coordinate that is randomly selected, but optionally you could change the value part of the list comprehension (i,j) in this case to: y and it'd return false, though thats a bit redundant in this case.



来源:https://stackoverflow.com/questions/48631962/how-to-pythonically-select-a-random-index-from-a-2d-list-such-that-the-correspon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!