I have an array of integers/indices (i.e. arr_F_idx = np.arange(0,200)) and I would like to draw 100 pairs without repetition. I am using a masked array (I transform arr_F_idx after the first draw, as shown in the code below), but it seems that numpy.random.choice still draws the masked elements.

arr_F_idx = np.arange(0,200)
draw = np.random.choice(arr_F_idx,2,replace=False)
arr2_Drawn_pairs[0,0] = draw[0]
arr2_Drawn_pairs[0,1] = draw[1]
arr_F_idx_dum1 = np.array(arr_F_idx == draw[0])
arr_F_idx = np.ma.array(arr_F_idx, mask = arr_F_idx_dum1)
arr_F_idx_dum2 = np.array(arr_F_idx == draw[1])
arr_F_idx = np.ma.array(arr_F_idx, mask = arr_F_idx_dum2)

for i in range(1,100):
    draw = np.random.choice(arr_F_idx,2,replace=False)
    arr2_Drawn_pairs[i,0] = draw[0]
    arr2_Drawn_pairs[i,1] = draw[1]
    arr_F_idx_dum1 = np.array(arr_F_idx == draw[0])
    arr_F_idx = np.ma.array(arr_F_idx, mask = arr_F_idx_dum1)
    arr_F_idx_dum2 = np.array(arr_F_idx == draw[1])
    arr_F_idx = np.ma.array(arr_F_idx, mask = arr_F_idx_dum2)

The (sample) output that I get is

arr_F_idx

masked_array(data=[--, --, --, --, 4, 5, --, --, --, --, --, 11, 12, 13,
               --, --, 16, 17, --, 19, --, 21, 22, --, 24, --, --, --,
               28, --, --, --, 32, 33, 34, --, --, 37, 38, 39, --, 41,
               --, --, --, --, --, --, 48, --, --, --, --, --, --, --,
               --, 57, 58, --, 60, --, --, 63, 64, --, --, --, --, --,
               --, --, --, 73, --, --, 76, --, 78, --, --, --, --, --,
               --, --, --, --, 88, 89, --, --, 92, --, --, --, --, --,
               98, 99, --, 101, 102, --, --, --, 106, --, --, --, --,
               111, --, --, --, --, 116, --, --, --, --, 121, --, 123,
               124, 125, --, 127, --, --, --, 131, --, --, --, 135,
               --, --, --, --, --, 141, --, --, --, --, --, --, --,
               --, --, 151, --, --, --, 155, --, --, --, 159, --, 161,
               --, --, --, 165, --, 167, --, 169, --, 171, --, --, --,
               --, 176, --, 178, 179, --, --, --, 183, --, 185, --,
               --, --, 189, 190, --, --, 193, 194, --, 196, --, 198,
               199],
         mask=[ True,  True,  True,  True, False, False,  True,  True,
                True,  True,  True, False, False, False,  True,  True,
               False, False,  True, False,  True, False, False,  True,
               False,  True,  True,  True, False,  True,  True,  True,
               False, False, False,  True,  True, False, False, False,
                True, False,  True,  True,  True,  True,  True,  True,
               False,  True,  True,  True,  True,  True,  True,  True,
                True, False, False,  True, False,  True,  True, False,
               False,  True,  True,  True,  True,  True,  True,  True,
                True, False,  True,  True, False,  True, False,  True,
                True,  True,  True,  True,  True,  True,  True,  True,
               False, False,  True,  True, False,  True,  True,  True,
                True,  True, False, False,  True, False, False,  True,
                True,  True, False,  True,  True,  True,  True, False,
                True,  True,  True,  True, False,  True,  True,  True,
                True, False,  True, False, False, False,  True, False,
                True,  True,  True, False,  True,  True,  True, False,
                True,  True,  True,  True,  True, False,  True,  True,
                True,  True,  True,  True,  True,  True,  True, False,
                True,  True,  True, False,  True,  True,  True, False,
                True, False,  True,  True,  True, False,  True, False,
                True, False,  True, False,  True,  True,  True,  True,
               False,  True, False, False,  True,  True,  True, False,
                True, False,  True,  True,  True, False, False,  True,
                True, False, False,  True, False,  True, False, False],
   fill_value=999999)

For smaller ranges it happens as well; of course for very small ranges this is not a problem, but as I have mentioned, I want to exhaust the original array to the point that all its elements will be masked. The problem seems to be that the np.random.choice somehow still draws the masked elements, even though it is not supposed to (otherwise I do not see the point of the object called masked array). I may be doing something wrong. I will appreciate help on this issue, also if there is a simpler way to make the draws of pairs without repetition across and within pairs.

Edit: In fact, the numpy random.choice draws masked elements, as can be seen in the output (e.g. number 177 is drawn twice and 191 three times):

arr2_Drawn_pairs
Out[53]: 
array([[ 20.,  49.],
   [ 35., 114.],
   [ 44.,  42.],
   [ 52., 140.],
   [191.,  59.],    191 - the first time
   [147., 144.],
   [ 74., 143.],
   [ 23.,  43.],
   [130.,   1.],
   [146., 166.],
   [ 62.,  80.],
   [ 26., 138.],
   [152.,  71.],
   [ 50.,  87.],
   [ 69.,   9.],
   [ 20.,  65.],
   [  3., 162.],
   [ 30., 104.],
   [168., 145.],
   [154.,  54.],
   [129.,   2.],
   [ 79., 170.],
   [ 14., 188.],
   [107.,  30.],
   [119., 188.],
   [139.,  94.],
   [132., 158.],
   [  0.,  69.],
   [ 47.,  27.],
   [192.,  72.],
   [181., 160.],
   [ 95., 162.],
   [ 40.,  25.],
   [107.,   8.],
   [128.,  10.],
   [  7.,  83.],
   [ 91., 173.],
   [174.,  10.],
   [134.,  82.],
   [ 67.,  52.],
   [195., 172.],
   [197.,  96.],
   [ 15., 188.],
   [184., 164.],
   [ 18., 180.],
   [ 45.,  27.],
   [ 86.,  84.],
   [ 97., 128.],
   [149.,   6.],
   [109.,  85.],
   [182.,  62.],
   [ 53.,  68.],
   [157.,  81.],
   [188.,  25.],
   [107.,  45.],
   [117.,  86.],
   [195.,  47.],
   [105., 103.],
   [ 51., 162.],
   [187., 162.],
   [ 70.,  97.],
   [ 29., 156.],
   [175., 177.],
   [  0.,  10.],
   [ 87.,  46.],
   [  1., 119.],
   [ 93.,  90.],
   [174.,  53.],
   [ 77.,  84.],
   [ 84.,  66.],
   [ 91., 186.],
   [ 83.,  59.],
   [137., 140.],
   [136., 186.],
   [100., 195.],
   [173.,  81.],
   [120., 115.],
   [ 36.,  46.],
   [112., 148.],
   [118., 103.],
   [  8., 128.],
   [ 56.,  65.],
   [158., 145.],
   [180., 122.],
   [142., 126.],
   [133.,  45.],
   [ 59., 173.],
   [110., 119.],
   [177.,  31.],   177 - the first time!
   [ 82., 158.],
   [ 53., 113.],
   [ 85., 150.],
   [126.,  94.],
   [ 61., 152.],
   [ 93.,  40.],
   [  1.,  55.],
   [ 96., 162.],
   [153., 108.],
   [163.,   9.],
   [ 75.,  50.],
   [101.,  47.],
   [178., 148.],
   [188., 183.],
   [ 69., 177.],    177 - the second time!
   [141.,  16.],
   [ 31.,  28.],
   [106., 147.],
   [ 66., 176.],
   [156.,  96.],
   [  9.,  21.],
   [139.,  57.],
   [106.,  11.],
   [ 25.,   2.],
   [152.,  69.],
   [ 34., 169.],
   [148., 191.],    191 - the second time!
   [105.,  32.],
   [187., 156.],
   [105., 191.],    191 - the third time!
   [ 53., 128.],
   [ 56.,  30.],
   [176.,   7.],
   [168., 150.],
   [ 48., 101.],
   [105., 167.]])

Edit 2: A brute-force method for obtaining my desired result is perhaps creating a new array to sample from after every draw,

arr_F_idx_iter = np.append(arr_F_idx[0:draw[0]],arr_F_idx[draw[0]+1:199])

but I think the question of whether this can be done efficiently with masked arrays is still legitimate, as it would be quicker, maybe also points out a flaw in how masked arrays work.

1

There are 1 best solutions below

0
hpaulj On

A simple 1d masked array:

In [28]: m = np.ma.masked_array(np.arange(10), mask=np.random.randint(0,2,10))    
In [29]: m
Out[29]: 
masked_array(data=[--, --, 2, 3, --, --, 6, 7, --, --],
             mask=[ True,  True, False, False,  True,  True, False, False,
                    True,  True],
       fill_value=999999)

choice, without special ma knowledge, draws from the data attribute:

In [31]: np.random.choice(m,3, replace=False)
Out[31]: array([4, 3, 8])

In [32]: np.random.choice(m.data,3, replace=False)
Out[32]: array([1, 8, 5])

If you want it to draw from the unmasked elements, you need to give it such an array, compressed:

In [33]: np.random.choice(m.compressed(),3, replace=False)
Out[33]: array([2, 6, 3])

In general np functions don't work correctly on masked arrays. That's why there's a large set of np.ma functions (and ma methods). Often those functions use the compressed to get the unmasked values. Or they replace masked values with some "innocent" fill.

In [35]: m.data
Out[35]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [36]: m.compressed()
Out[36]: array([2, 3, 6, 7])

In [37]: m.filled()
Out[37]: 
array([999999, 999999,      2,      3, 999999, 999999,      6,      7,
       999999, 999999])

In [38]: m.filled(0)
Out[38]: array([0, 0, 2, 3, 0, 0, 6, 7, 0, 0])