Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia - Repeat entries of a vector by another vector (inner)

Tags:

julia

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?

like image 439
Physics_Student Avatar asked Nov 16 '21 13:11

Physics_Student


People also ask

How to use repeat () function 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.

What are vectors in Julia?

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:

How to normalize a vector in Julia?

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.

How do you add elements to a vector in Julia?

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.


2 Answers

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
like image 114
Cameron Bieganek Avatar answered Sep 29 '22 08:09

Cameron Bieganek


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))
like image 43
Nils Gudat Avatar answered Sep 29 '22 08:09

Nils Gudat