Random sampling without replacement when more needs to be sampled than there are samples
<p>I need to generate samples from a list of numbers in a scenario where I might have the situation that I need to sample more numbers than I have. More explicitly, this is what I need to do: </p>

<ul>
<li><p>Let the total number of elements in my list be N.</p></li>
<li><p>I need to sample randomly without replacement from this list M samples.</p></li>
<li><p>If M &lt;= N, then simply use Numpy's random.choice without replacement. </p></li>
<li><p>If M > N, then the samples must consist X times all the N numbers in the list, where X is the number of times N fully divides M, i.e. X = floor(M/N) and then sample additional M-(X*N) remainder samples from the list without replacement. </p></li>
</ul>

<p>For example, let my list be the following: </p>

<blockquote>
  <p>L = [1, 2, 3, 4, 5]</p>
</blockquote>

<p>and I need to sample 8 samples. Then firstly, I sample the full list once and additional 3 elements randomly without replacement, e.g. my samples could then be: </p>

<blockquote>
  <p>Sampled_list = [1, 2, 3, 4, 5, 3, 5, 1]</p>
</blockquote>

<p>How can I implement such a code as efficiently as possible in terms of computation time in Python? Can this be done without for-loops? </p>

<p>At the moment I'm implementing this using for-loops but this is too inefficient for my purposes. I have also tried Numpy's random.choice without replacement but then I need to have M &lt;= N. </p>

<p>Thank you for any help! </p>

<p>Use <code>divmod()</code> to get the number of repetitions of the list and the remainder/shortfall. The shortfall can then be randomly selected from the list using <code>numpy.random.choice()</code>.</p>

<pre><code>import numpy as np

def get_sample(l, n):
    samples, shortfall = divmod(n, len(l))
    return np.concatenate((np.repeat(l, samples), np.random.choice(l, shortfall, False)))


&gt;&gt;&gt; get_sample(range(100), 10)
array([91, 95, 73, 96, 18, 37, 32, 97,  4, 41])
&gt;&gt;&gt; get_sample(range(10), 100)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
   2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
   4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
   6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9,
   9, 9, 9, 9, 9, 9, 9, 9])
&gt;&gt;&gt; get_sample([1,2,3,4], 0)
array([], dtype=int64)
&gt;&gt;&gt; get_sample([1,2,3,4], 4)
array([1, 2, 3, 4])
&gt;&gt;&gt; get_sample([1,2,3,4], 6)
array([1, 2, 3, 4, 4, 3])
&gt;&gt;&gt; get_sample([1,2,3,4], 6)
array([1, 2, 3, 4, 3, 2])

&gt;&gt;&gt; get_sample(list('test string'), 6)
array(['n', 's', 'g', 's', 't', ' '], 
  dtype='|S1')
&gt;&gt;&gt; get_sample(np.array(list('test string')), 4)
array(['r', 't', 's', 'g'], 
  dtype='|S1')
</code></pre>

python numpy sampling