Sample matrix indices in proportion to the matrix element values

I have a 4 by 4 array which has a probability associated with each point.

{{0., 0., 0., 0.9},
{0., 0.05, 0., 0.},
{0., 0., 0., 0.},
{0., 0., 0.05, 0.}}


I want to sample the indices according to the probability/value at those indices in the matrix.

I should find point {1,4} many more times than I should {4,3} or {2,2}.

How can I sample the index where the matrix values correspond to the probability of finding that index?

2 Answers

You can use Position to find the non-zero probabilities:

m = {{0., 0., 0., 0.9}, {0., 0.05, 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0.05, 0.}};
Position[m, _?Positive]


{{1, 4}, {2, 2}, {4, 3}}

If you have large matrices, then you may be interested in using SparseArray to save memory and possibly also gain in performance:

sa = SparseArray[m];
sa["NonzeroPositions"]


{{1, 4}, {2, 2}, {4, 3}}

Here is how to sample the indices in proportion to the corresponding matrix elements:

pos = Position[m, _?Positive];
values = Extract[m, pos];
RandomChoice[values -> pos, 10]


{{1, 4}, {1, 4}, {4, 3}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}}

• Thanks, but I'm looking to find the value according to the probability in the matrix. Using your method I can find the non-zero values, but this doesn't help the problem. I'll edit the question to make it clearer. I think I might have an idea...(based from this uk.mathworks.com/matlabcentral/answers/…)
– Tomi
Dec 30, 2017 at 22:54
• @Tomi It was not clear from your original question, but now it is more clearly explained. I have updated my answer. Dec 30, 2017 at 22:56

WeightedData

m = {{0., 0., 0., 0.9}, {0., 0.05, 0., 0.}, {0., 0., 0., 0.}, {0., 0.,  0.05, 0.}};

{positions, values} = {#["NonzeroPositions"], #["NonzeroValues"]} & @ SparseArray[m];
wd = WeightedData[positions, values];


RandomVariate + EmpiricalDistribution

You can use EmpiricalDistribution + RandomVariate to generate a random sample of indices:

SeedRandom[777]
RandomVariate[EmpiricalDistribution[wd], 10]


{{1, 4}, {2, 2}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {4, 3}, {1, 4}, {1, 4}}

SeedRandom[1]
sampleproportions = {#, Round[#2/1000000., .01]} & @@@
Tally[RandomVariate[EmpiricalDistribution[wd], 1000000]]


{{{1, 4}, 0.9}, {{4, 3}, 0.05}, {{2, 2}, 0.05}}

which is, up to ordering, the same as

Transpose @ {positions, values}


{{{1, 4}, 0.9}, {{2, 2}, 0.05}, {{4, 3}, 0.05}}

RandomChoice + wd["EmpiricalPDF"]

Alternatively, you can use arg = values -> positions or arg = Rule @@ Reverse @ wd["EmpiricalPDF"] as the first argument of RandomChoice to get a list of random indices:

SeedRandom[777]
RandomChoice[arg, 10]


{{1, 4}, {2, 2}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {4, 3}, {1, 4}, {1, 4}}

MapIndexed

SeedRandom[777]
RandomChoice[Rule @@ Transpose[Join @@ MapIndexed[{##} &, m, {2}]], 10]


{{1, 4}, {2, 2}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {4, 3}, {1, 4}, {1, 4}}

wd2 = WeightedData @@ Transpose[Join @@ MapIndexed[{#2, #} &, m, {2}]];

SeedRandom[777]
RandomVariate[EmpiricalDistribution @ wd2, 10]


{{1, 4}, {2, 2}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {1, 4}, {4, 3}, {1, 4}, {1, 4}}