I have an array x
and I would like to repeat each entry of x
a number of times specified by the corresponding entries of another array y
, of the same length of x
.
x = [1, 2, 3, 4, 5] # Array to be repeated
y = [3, 2, 1, 2, 3] # Repetitions for each element of x
# result should be [1, 1, 1, 2, 2, 3, 4, 4, 5, 5, 5]
Is there a way to do this in Julia?
The repeat () is an inbuilt function in julia which is used to construct an array by repeating the specified array elements with the specified number of times. repeat (A::AbstractArray, counts::Integer…) A::AbstractArray: Specified array. counts::Integer: Specified number of times each element get repeated.
For Julia, Vectors are just a special kind of Matrix, namely with just one row (row matrix) or just one column (column matrix): Julia Vectors can come in two forms: Column Matrices (one column, N rows) and Row Matrices (one row, N columns) Very useful operation. Takes any collection and returns a representation containing n rows and m columns:
Pass every element of the collection through a function and return the new collection. In this example, divide every element of a vector by the sum of all elements, in other words, normalize it: Technically, there's still another way to create Vectors in Julia but they are not as widely used and will probably cause some confusion.
Elements can be added or removed from the front or back of the vector. A Vector in Julia can be created with the use of a pre-defined keyword Vector () or by simply writing Vector elements within square brackets ( []). There are different ways of creating Vector.
Your x
and y
vectors constitute what is called a run-length encoding of the vector [1, 1, 1, 2, 2, 3, 4, 4, 5, 5, 5]
. So if you take the inverse of the run-length encoding, you will get the vector you are looking for. The StatsBase.jl package contains the rle
and inverse_rle
functions. We can use inverse_rle
like this:
julia> using StatsBase
julia> x = [1, 2, 3, 4, 5];
julia> y = [3, 2, 1, 2, 3];
julia> inverse_rle(x, y)
11-element Vector{Int64}:
1
1
1
2
2
3
4
4
5
5
5
You've given what I would have suggested as the answer already in your comment:
vcat(fill.(x, y)...)
How does this work? Start with fill
:
help?> fill
fill(x, dims::Tuple)
fill(x, dims...)
Create an array filled with the value x. For example, fill(1.0, (5,5)) returns a 5×5 array of floats, with each element initialized to 1.0.
This is a bit more complicated than it needs to be for our case (where we only have one dimension to fill into), so let's look at a simple example:
julia> fill(1, 3)
3-element Vector{Int64}:
1
1
1
so fill(1, 3)
just means "take the number one, and put this number into a one-dimensional array 3 times."
This of course is exactly what we want to do here: for every element in x
, we want an array that holds this element multiple times, with the multiple given by the corresponding element in y
. We could therefore loop over x
and y
and do something like:
julia> for (xᵢ, yᵢ) ∈ zip(x, y)
fill(xᵢ, yᵢ)
end
Now this loop doesn't return anything, so we'd have to preallocate some storage and assign to that within the loop. A more concise way of writing this while automatically returning an object would be a comprehension:
julia> [fill(xᵢ, yᵢ) for (xᵢ, yᵢ) ∈ zip(x, y)]
5-element Vector{Vector{Int64}}:
[1, 1, 1]
[2, 2]
[3]
[4, 4]
[5, 5, 5]
and even more concisely, we can just use broadcasting:
julia> fill.(x, y)
5-element Vector{Vector{Int64}}:
[1, 1, 1]
[2, 2]
[3]
[4, 4]
[5, 5, 5]
so from the comprehension or the broadcast we are getting a vector of vectors, each vector being an element of x
repeated y
times. Now all that remains is to put these together into a single vector by concatenating them vertically:
julia> vcat(fill.(x, y)...)
11-element Vector{Int64}:
1
1
1
2
2
3
4
4
5
5
5
Here we are using splatting to essentially do:
z = fill.(x, y)
vcat(z[1], z[2], z[3], z[4], z[5])
Note that splatting can have suboptimal performance for arrays of variable length, so a better way is to use reduce
which is special cased for this and will give the same result:
reduce(vcat, fill.(x, y))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With