4
$\begingroup$

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?

$\endgroup$

2 Answers 2

8
$\begingroup$

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}}

$\endgroup$
2
  • $\begingroup$ 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/…) $\endgroup$
    – Tomi
    Dec 30, 2017 at 22:54
  • $\begingroup$ @Tomi It was not clear from your original question, but now it is more clearly explained. I have updated my answer. $\endgroup$
    – C. E.
    Dec 30, 2017 at 22:56
4
$\begingroup$

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}}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.