diff --git a/Numpy/Array Basics/Random Shuffle/__init__.py b/Numpy/Array Basics/Random Shuffle/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Numpy/Array Basics/Random Shuffle/task-info.yaml b/Numpy/Array Basics/Random Shuffle/task-info.yaml new file mode 100644 index 0000000..f4f527b --- /dev/null +++ b/Numpy/Array Basics/Random Shuffle/task-info.yaml @@ -0,0 +1,20 @@ +type: edu +files: +- name: task.py + visible: true + placeholders: + - offset: 87 + length: 16 + placeholder_text: '# TODO: shuffle rows of `arr` in place' + - offset: 118 + length: 28 + placeholder_text: '# TODO: now permute the elements within rows of `arr`' + - offset: 162 + length: 45 + placeholder_text: '# TODO: fully randomize the array' +- name: tests/test_task.py + visible: false +- name: __init__.py + visible: false +- name: tests/__init__.py + visible: false diff --git a/Numpy/Array Basics/Random Shuffle/task.md b/Numpy/Array Basics/Random Shuffle/task.md new file mode 100644 index 0000000..e646f15 --- /dev/null +++ b/Numpy/Array Basics/Random Shuffle/task.md @@ -0,0 +1,92 @@ +## Random Shuffle + +Sometimes you need to shuffle the contents of an array. For instance, in machine learning tasks +it is common to shuffle data and normalize it. + +NumPy offers two methods that will provide you with randomly shuffled (or **permuted**) data: +- [`random.Generator.permutation`](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.permutation.html#numpy.random.Generator.permutation): randomly permutes a sequence, or return a permuted range. +- [`random.Generator.shuffle`](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.shuffle.html#numpy.random.Generator.shuffle): modifies an array or sequence **in-place** by shuffling its contents. + +The key difference between these two methods is that `permutation` returns an array, while +`shuffle` modifies it in-place. Additionally, `permutation` allows you to create a permuted sequence from +range. For arrays with more than one dimension, the order of sub-arrays is changed but their contents remains the same. +Please check out the examples below. + +1. Shuffle / permute a sequence +```python +rng = np.random.default_rng() +print(rng.permutation(10)) # permutation returns a permuted range +``` +Output (will be different every time): +```text +[6 5 0 4 9 1 8 2 3 7] +``` + +```python +arr = np.arange(10) +rng.shuffle(arr) # Shuffle sequence in place +print(arr) +``` +Output: +```text +[0 3 5 7 8 6 9 1 2 4] +``` +2. Shuffle / permute a 2D array +```python +arr = np.arange(9).reshape((3, 3)) +print(rng.permutation(arr)) # permutation returns a permuted array, order within rows is maintained +``` +Output: +```text +[[3 4 5] + [6 7 8] + [0 1 2]] +``` +```python +arr = np.arange(9).reshape((3, 3)) +rng.shuffle(arr) # Shuffle array in place, order within rows is maintained +print(arr) +``` +Output: +```text +[[6 7 8] + [3 4 5] + [0 1 2]] +``` +2. Shuffle / permute a 2D array along another axis +```python +arr = np.arange(9).reshape((3, 3)) +print(rng.permutation(arr, axis=1)) # permutation returns an array permuted within rows +``` +Output: +```text +[[2 0 1] + [5 3 4] + [8 6 7]] +``` +```python +arr = np.arange(9).reshape((3, 3)) +rng.shuffle(arr, axis=1) # Shuffle array in place within rows +print(arr) +``` +Output: +```text +[[0 2 1] + [3 5 4] + [6 8 7]] +``` + +### Task + +First, `shuffle` the rows of the array `arr` (0th axis is the default axis). +Then, randomly permute the numbers within rows in `arr` and assign the result to +the variable `permuted_2d`. +Still, your array is not fully random, since all the elements remain in the same rows +where they started. Create a `fully_random` array. Use some of the methods we +learned earlier. + +
Use the examples in task description to shuffle and permute the array.
+ +
To fully randomize it, you could first, flatten the array, apply the permutation +method to it, and reshape back to the original shape. All these things can be done in one +line of code.
diff --git a/Numpy/Array Basics/Random Shuffle/task.py b/Numpy/Array Basics/Random Shuffle/task.py new file mode 100644 index 0000000..0f7ecaa --- /dev/null +++ b/Numpy/Array Basics/Random Shuffle/task.py @@ -0,0 +1,17 @@ +import numpy as np + +rng = np.random.default_rng() + +arr = np.arange(100).reshape(5, 20) +rng.shuffle(arr) +permuted_2d = rng.permutation(arr, axis=1) +fully_random = rng.permutation(arr.flatten()).reshape(5, 20) + + +if __name__ == '__main__': + print(arr) + for i in arr: + print(i[0], min(i)) + print(i[-1], max(i)) + print(permuted_2d) + print(fully_random) diff --git a/Numpy/Array Basics/Random Shuffle/tests/__init__.py b/Numpy/Array Basics/Random Shuffle/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Numpy/Array Basics/Random Shuffle/tests/test_task.py b/Numpy/Array Basics/Random Shuffle/tests/test_task.py new file mode 100644 index 0000000..07d4333 --- /dev/null +++ b/Numpy/Array Basics/Random Shuffle/tests/test_task.py @@ -0,0 +1,28 @@ +import unittest +import numpy as np + +from task import arr, permuted_2d, fully_random + + +class TestCase(unittest.TestCase): + def test_shape(self): + self.assertEqual(arr.shape, (5, 20), msg="wrong shape of the array 'arr'") + self.assertEqual(permuted_2d.shape, (5, 20), msg="wrong shape of the array 'permuted_2d'") + self.assertEqual(fully_random.shape, (5, 20), msg="wrong shape of the array 'fully_random'") + + def test_arr(self): + for i in arr: + # This test checks if in each row the minimum element goes first and maximum - last. + self.assertTrue(i[0] == min(i) and i[-1] == max(i), msg="'arr' should be shuffled along the 0th axis") + + def test_two_d(self): + for i in permuted_2d: + # This test checks that differences between all neighboring elements in rows of the array + # are not equal to 1 (in non-shuffled rows they would be). + self.assertFalse(all([(x - i[i.tolist().index(x) - 1]) == 1 for x in i if i.tolist().index(x) > 0]), + msg="'permuted_2d' should be shuffled along the 1st axis") + + def test_random(self): + # This test checks if elements were also randomized between the rows. + for i in fully_random: + self.assertTrue(max(i) - min(i) > 19, "'fully_random' needs to be fully shuffled!") diff --git a/Numpy/Array Basics/lesson-info.yaml b/Numpy/Array Basics/lesson-info.yaml index 044e9e8..7516abc 100644 --- a/Numpy/Array Basics/lesson-info.yaml +++ b/Numpy/Array Basics/lesson-info.yaml @@ -5,3 +5,4 @@ content: - Create an Array from Range - Reshape - Random Sampling +- Random Shuffle