# Randomly select x% of elements in a array/matrix

**URL:** https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369
**Category:** New to Julia
**Tags:** arrays, random
**Created:** [July 17, 2022, 8:51pm UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369 "2022-07-17T20:51:53Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)
#### Post date: [July 17, 2022, 8:51pm UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/1 "2022-07-17T20:51:53Z")

</div>

Let’s say I have a matrix like:

```julia
m = rand(50,100)

```

And now I want to select, say, 10% of the data in this matrix (a view will work, I don’t need a new array).

```julia
import Random
m[:, Random.randperm(100)[1:10]]

```

Is there a better way than this?

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [July 17, 2022, 8:59pm UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/2 "2022-07-17T20:59:30Z")

</div>

> [@vshesh](#):
>
> And now I want to select, say, 10% of the data in this matrix

```julia
Random.randsubseq(m, 0.1)

```

The [StatsBase.jl package](https://juliastats.org/StatsBase.jl/v0.22/sampling.html) has other possible sampling methods, e.g. `StatsBase.seqsample_a!`, depending on what precisely you want to do.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [July 17, 2022, 10:01pm UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/3 "2022-07-17T22:01:51Z")

</div>

Here is a simple way `using Random`’s most common function `rand()` - hope it is correct too:

```julia
M = rand(50,100)
p = 0.10
n = round(Int, p*length(M))
rand(M, n)

```

_NB: this allows repetitions, not what is required._

---

<div class="post-metadata">

### Author: ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)
#### Post date: [July 18, 2022, 12:50am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/4 "2022-07-18T00:50:23Z")

</div>

This samples with replacement, but I think the OP wants without.

Edit: The [source code](https://github.com/JuliaLang/julia/blob/742b9abb4dd4621b667ec5bb3434b8b3602f96fd/stdlib/Random/src/misc.jl#L86) for `Random.randsubseq!` is quite an interesting algorithm:

```julia
## randsubseq & randsubseq!

# Fill S (resized as needed) with a random subsequence of A, where
# each element of A is included in S with independent probability p.
# (Note that this is different from the problem of finding a random
# size-m subset of A where m is fixed!)
function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real)
    require_one_based_indexing(S, A)
    0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]"))
    n = length(A)
    p == 1 && return copyto!(resize!(S, n), A)
    empty!(S)
    p == 0 && return S
    nexpected = p * length(A)
    sizehint!(S, round(Int,nexpected + 5*sqrt(nexpected)))
    if p > 0.15 # empirical threshold for trivial O(n) algorithm to be better
        for i = 1:n
            rand(r) <= p && push!(S, A[i])
        end
    else
        # Skip through A, in order, from each element i to the next element i+s
        # included in S. The probability that the next included element is
        # s==k (k > 0) is (1-p)^(k-1) * p, and hence the probability (CDF) that
        # s is in {1,...,k} is 1-(1-p)^k = F(k). Thus, we can draw the skip s
        # from this probability distribution via the discrete inverse-transform
        # method: s = ceil(F^{-1}(u)) where u = rand(), which is simply
        # s = ceil(log(rand()) / log1p(-p)).
        # -log(rand()) is an exponential variate, so can use randexp().
        L = -1 / log1p(-p) # L > 0
        i = 0
        while true
            s = randexp(r) * L
            s >= n - i && return S # compare before ceil to avoid overflow
            push!(S, A[i += ceil(Int,s)])
        end
        # [This algorithm is similar in spirit to, but much simpler than,
        # the one by Vitter for a related problem in "Faster methods for
        # random sampling," Comm. ACM Magazine 7, 703-718 (1984).]
    end
    return S
end

```

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [July 18, 2022, 4:33am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/5 "2022-07-18T04:33:37Z")

</div>

Yes, you are right, repetitions are allowed in the `rand()` example.

For distinct 10% values we could use `sample()`:

```julia
using StatsBase
M = rand(50,100)
p = 0.10
n = round(Int, p*length(M))
sample(M, n; replace=false)

```

But the dedicated `randsubseq()` function suggested by Steve has simpler syntax.

---

<div class="post-metadata">

### Author: ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)
#### Post date: [July 18, 2022, 4:57am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/6 "2022-07-18T04:57:15Z")

</div>

`sample` (with `replace=false`), `randsubseq`, and `rand` all appear to do different things:

- `rand(A, n)` returns _exactly_ `n` elements sampled from `A` with replacement.
- `sample(A, n, replace=false)` returns _exactly_ `n` elements sampled from `A` without replacement.
- `randsubseq(A, p)` returns a subset of `A` where each element is included with probability `p`. (Thus, the number of elements in the output is a binomial random variable with probability `p`: its length can be different every time.)

```julia
julia> using Random

julia> A = 1:10
1:10

julia> randsubseq(A, 0.1)
1-element Vector{Int64}:
 10

julia> randsubseq(A, 0.1)
Int64[]

julia> randsubseq(A, 0.1)
1-element Vector{Int64}:
 5

julia> randsubseq(A, 0.1)
2-element Vector{Int64}:
 7
 8

julia> randsubseq(A, 0.1)
1-element Vector{Int64}:
 3

```

I think `sample(A, n, replace=false)` is what the OP is after.

---

<div class="post-metadata">

### Author: ![vshesh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vshesh/32/20362_2.png) [@vshesh](https://discourse.julialang.org/u/vshesh)
#### Post date: [July 19, 2022, 12:26am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/7 "2022-07-19T00:26:45Z")

</div>

Yup, `sample` is what I was looking for. Thanks!  
Only one thing - how do I get columns of the matrix instead of just values from the matrix?

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [July 19, 2022, 6:25am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/8 "2022-07-19T06:25:42Z")

</div>

> [@vshesh](#):
>
> how do I get columns of the matrix instead of just values from the matrix

You could use again `sample` together with `view`:

```julia
view(m, :, sample(1:100,10,replace=false))

```

---

<div class="post-metadata">

### Author: ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)
#### Post date: [July 19, 2022, 6:41am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/9 "2022-07-19T06:41:43Z")

</div>

Huh, why does this result in fewer allocations than `sample` alone? I expected that the call to `sample` inside of `view` would require allocating (defeating the purpose of the `view`), but `@allocated` reveals that this is not the case.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [July 19, 2022, 6:56am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/10 "2022-07-19T06:56:55Z")

</div>

Not an explanation but an observation: the random columns selected are complete, so we may take views over them.

---

<div class="post-metadata">

### Author: ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)
#### Post date: [July 19, 2022, 8:08am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/11 "2022-07-19T08:08:07Z")

</div>

In my experiment, the difference was stark even for array input:

```julia
julia> using StatsBase

julia> a = rand(1000);

julia> @allocated sample(a, 50)
22087420

julia> @allocated view(a, sample(1:1000, 50; replace=false))
2263532

```

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [July 19, 2022, 8:15am UTC](https://discourse.julialang.org/t/randomly-select-x-of-elements-in-a-array-matrix/84369/12 "2022-07-19T08:15:44Z")

</div>

There might be some problem with your experiment. I get different results, confirmed also by using `@btime` and interpolating the variables:

```julia
a = rand(1000);
@btime sample($a, 50) # 298 ns (1 alloc: 496 bytes)
@btime view($a, sample(1:1000, 50; replace=false)) # 880 ns (2 allocs: 8.4 KiB)

```
