Skip to content

Commit 39eead1

Browse files
Plankton555Nucs
authored andcommitted
Added basic np.random.choice functionality (Fixed #310)
* Stub files for np.random.choice * Simplest random.choice functionality * Simple random.choice with weighted probabilities * Unit test stubs and updated documentation (np.random.choice) * Added Ignore markers to tests for unimplemented features * random.choice handles shape=null * Improved unit tests for np.random.choice * Added documentation to np.searchsorted
1 parent 1499e02 commit 39eead1

3 files changed

Lines changed: 249 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Text;
5+
6+
namespace NumSharp
7+
{
8+
public partial class NumPyRandom
9+
{
10+
/// <summary>
11+
/// Generates a random sample from a given 1-D array
12+
/// </summary>
13+
/// <param name="arr">If an ndarray, a random sample is generated from its elements. If an int, the random sample is generated as if a were np.arange(a)</param>
14+
/// <param name="shape">Output shape. If the given shape is, e.g., (m, n, k), then m * n * k samples are drawn. Default is None, in which case a single value is returned.</param>
15+
/// <param name="replace">Whether the sample is with or without replacement</param>
16+
/// <param name="probabilities">The probabilities associated with each entry in a. If not given the sample assumes a uniform distribution over all entries in a.</param>
17+
/// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html</remarks>
18+
public NDArray choice(NDArray arr, Shape shape = null, bool replace = true, double[] probabilities = null)
19+
{
20+
int arrSize = arr.len;
21+
NDArray idx = np.random.choice(arrSize, shape, probabilities: probabilities);
22+
return arr[idx];
23+
}
24+
25+
/// <summary>
26+
/// Generates a random sample from a given 1-D array
27+
/// </summary>
28+
/// <param name="a">If an ndarray, a random sample is generated from its elements. If an int, the random sample is generated as if a were np.arange(a)</param>
29+
/// <param name="shape">Output shape. If the given shape is, e.g., (m, n, k), then m * n * k samples are drawn. Default is None, in which case a single value is returned.</param>
30+
/// <param name="replace">Whether the sample is with or without replacement</param>
31+
/// <param name="probabilities">The probabilities associated with each entry in a. If not given the sample assumes a uniform distribution over all entries in a.</param>
32+
/// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html</remarks>
33+
public NDArray choice(int a, Shape shape = null, bool replace = true, double[] probabilities = null)
34+
{
35+
if (shape is null)
36+
{
37+
shape = 1;
38+
}
39+
NDArray arr = np.arange(a);
40+
NDArray idx = null;
41+
//Debug.WriteLine($"arr: {arr}");
42+
if (probabilities is null)
43+
{
44+
idx = np.random.randint(0, arr.len, shape);
45+
}
46+
else
47+
{
48+
NDArray cdf = np.cumsum(probabilities);
49+
cdf /= cdf[cdf.len - 1];
50+
NDArray uniformSamples = np.random.uniform(0, 1, shape);
51+
idx = np.searchsorted(cdf, uniformSamples);
52+
}
53+
return idx;
54+
}
55+
}
56+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace NumSharp
6+
{
7+
public static partial class np
8+
{
9+
/// <summary>
10+
/// Find indices where elements should be inserted to maintain order.
11+
///
12+
/// Find the indices into a sorted array a such that, if the corresponding elements in v were inserted before the indices, the order of a would be preserved.
13+
/// </summary>
14+
/// <param name="a">Input array. Must be sorted in ascending order.</param>
15+
/// <param name="v">Values to insert into a.</param>
16+
/// <returns>Array of insertion points with the same shape as v.</returns>
17+
/// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.searchsorted.html</remarks>
18+
public static NDArray searchsorted(NDArray a, NDArray v)
19+
{
20+
// TODO currently no support for multidimensional a
21+
NDArray output = new int[v.shape[0]];
22+
for (int i = 0; i < v.size; i++)
23+
{
24+
double target = v[i];
25+
int idx = binarySearchRightmost(a, target);
26+
output[i] = idx;
27+
}
28+
return output;
29+
}
30+
31+
/// <summary>
32+
/// Find the (right-most) position of a target value within a sorted array.
33+
/// </summary>
34+
/// <param name="arr">Sorted array.</param>
35+
/// <param name="target">Target value to find position of.</param>
36+
/// <returns>Would-be index of target value within the array.</returns>
37+
/// <remarks>https://en.wikipedia.org/wiki/Binary_search_algorithm</remarks>
38+
private static int binarySearchRightmost(NDArray arr, double target)
39+
{
40+
// TODO should probably not work on NDArray? Does it make sense to do binary search on multidimensional arrays?
41+
int L = 0;
42+
int R = arr.size;
43+
int m;
44+
double val;
45+
while (L < R)
46+
{
47+
m = (L + R) / 2;
48+
val = arr[m];
49+
if (val < target)
50+
{
51+
L = m + 1;
52+
}
53+
else
54+
{
55+
R = m;
56+
}
57+
}
58+
return L;
59+
}
60+
}
61+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Text;
6+
7+
namespace NumSharp.UnitTest.RandomSampling
8+
{
9+
[TestClass]
10+
public class NpRandomChoiceTests : TestClass
11+
{
12+
[TestMethod]
13+
public void UniformOneSample()
14+
{
15+
// Generate a uniform random sample from np.arange(5) of size 1:
16+
// This is equivalent to np.random.randint(0,5,1)
17+
int low = 0;
18+
int high = 5;
19+
int nrSamples = 1;
20+
21+
NDArray actual = np.random.choice(high); // Not specifying size means 1 single value is wanted
22+
23+
Assert.AreEqual(actual.len, nrSamples, "Unexpected number of elements");
24+
25+
// Verify that all elements in output are within the range
26+
for (int i = 0; i < actual.len; i++)
27+
{
28+
Assert.IsTrue(actual[i] >= low, "Element was less than expected");
29+
Assert.IsTrue(actual[i] < high, "Element was greater than expected");
30+
}
31+
}
32+
[TestMethod]
33+
public void UniformMultipleSample()
34+
{
35+
// Generate a uniform random sample from np.arange(5) of size 3:
36+
// This is equivalent to np.random.randint(0,5,3)
37+
int low = 0;
38+
int high = 5;
39+
int nrSamples = 3;
40+
41+
NDArray actual = np.random.choice(high, nrSamples);
42+
43+
Assert.AreEqual(actual.len, nrSamples, "Unexpected number of elements");
44+
45+
// Verify that all elements in output are within the range
46+
for (int i = 0; i < actual.len; i++)
47+
{
48+
Assert.IsTrue(actual[i] >= low, "Element was less than expected");
49+
Assert.IsTrue(actual[i] < high, "Element was greater than expected");
50+
}
51+
}
52+
53+
[TestMethod]
54+
public void NonUniformSample()
55+
{
56+
// Generate a non-uniform random sample from np.arange(5) of size 3:
57+
int low = 0;
58+
int high = 5;
59+
int nrSamples = 3;
60+
double[] probabilities = new double[] {0.1, 0, 0.3, 0.6, 0};
61+
62+
NDArray actual = np.random.choice(5, nrSamples, probabilities: probabilities);
63+
64+
Assert.AreEqual(actual.len, nrSamples, "Unexpected number of elements");
65+
66+
// Verify that all elements in output are within the range
67+
for (int i = 0; i < actual.len; i++)
68+
{
69+
Assert.IsTrue(actual[i] >= low, "Element was less than expected");
70+
Assert.IsTrue(actual[i] < high, "Element was greater than expected");
71+
Assert.IsTrue(actual[i] != 1, "Sampled zero-probability element");
72+
Assert.IsTrue(actual[i] != 4, "Sampled zero-probability element");
73+
}
74+
}
75+
76+
[TestMethod]
77+
[Ignore("Choice without replacement not implemented yet")]
78+
public void UniformSampleWithoutReplace()
79+
{
80+
NDArray actual = np.random.choice(5, 3, replace: false);
81+
Assert.Fail("Not implemented");
82+
}
83+
84+
[TestMethod]
85+
[Ignore("Choice without replacement not implemented yet")]
86+
public void NonUniformSampleWithoutReplace()
87+
{
88+
double[] probabilities = new double[] { 0.1, 0, 0.3, 0.6, 0 };
89+
NDArray actual = np.random.choice(5, 3, replace: false, probabilities: probabilities);
90+
Assert.Fail("Not implemented");
91+
}
92+
93+
[TestMethod]
94+
[Ignore("Choice with string arrays not implemented yet")]
95+
public void StringArraySample1()
96+
{
97+
int nrSamples = 5;
98+
99+
NDArray aa_milne_arr = new string[] { "pooh", "rabbit", "piglet", "Christopher" };
100+
double[] probabilities = new double[] { 0.5, 0.1, 0.0, 0.3 };
101+
102+
NDArray actual = np.random.choice(aa_milne_arr, nrSamples, probabilities: probabilities);
103+
104+
Assert.AreEqual(actual.len, nrSamples, "Unexpected number of elements");
105+
106+
// Verify that all elements in output are within the possibilities
107+
for (int i = 0; i < actual.len; i++)
108+
{
109+
Assert.IsTrue((string)actual[i] != (string)aa_milne_arr[2], "Sampled zero-probability element");
110+
}
111+
}
112+
113+
[TestMethod]
114+
public void IntegerArraySample()
115+
{
116+
int nrSamples = 5;
117+
118+
NDArray int_arr = new int[] { 42, 96, 3, 101 };
119+
double[] probabilities = new double[] { 0.5, 0.1, 0.0, 0.3 };
120+
121+
NDArray actual = np.random.choice(int_arr, nrSamples, probabilities: probabilities);
122+
123+
Assert.AreEqual(actual.len, nrSamples, "Unexpected number of elements");
124+
125+
// Verify that all elements in output are within the possibilities
126+
for (int i = 0; i < actual.len; i++)
127+
{
128+
Assert.IsTrue((int)actual[i] != (int)int_arr[2], "Sampled zero-probability element");
129+
}
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)