This seems to be different from the OP example, as it produces indexes: 1:3:88
, and not variable step (= 3 or 4) indexes.
From the proposed solutions I seemed to understand that this was the expected result.
A=rand(100)
A[range(begin, step=end Γ· len, length=len)]
It is not so?
I just wanted to write one of the solutions a little differently
Letβs say I want an integer difference between integers, and I care that that each step
between each number is an integer. Julia has a convenient syntax for this: begin:step:end
.
My criterion may be distinct from yours.
- I want my indices to be exactly spaced apart by the same
step
. - I do not care if the last index is included.
julia> function helper(A; step)
B = A[begin:step:end]
@info "B = A[begin:step:end]" B[1] B[2] B[end] length(B)
return B
end
helper (generic function with 1 method)
julia> A = 1:100
1:100
julia> helper(A, step = 1)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 2
β B[end] = 100
β length(B) = 100
1:1:100
julia> helper(A, step = 2)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 3
β B[end] = 99
β length(B) = 50
1:2:99
julia> helper(A, step = 3)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 4
β B[end] = 100
β length(B) = 34
1:3:100
julia> helper(A, step = 4)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 5
β B[end] = 97
β length(B) = 25
1:4:97
julia> helper(A, step = 5)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 6
β B[end] = 96
β length(B) = 20
1:5:96
julia> helper(A, step = 6)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 7
β B[end] = 97
β length(B) = 17
1:6:97
julia> helper(A, step = 7)
β Info: B = A[begin:step:end]
β B[1] = 1
β B[2] = 8
β B[end] = 99
β length(B) = 15
1:7:99
For many arrays, 1:step:end
might do, but not all Julia arrays start at index 1
.
Next we need to determine step
. It could be length(A) Γ· desired_length
. Note that Γ·
is an alias for div
, which will do integer division, rounding towards zero. Γ·
is similar to \\
in Python.
Simply setting step
as above would create an array that is longer than desired. Thus we have to further truncate the result to the desired length.
Below I will use the idiom end-begin+1
to stand in for length
.
julia> function helper2(A; length)
B = A[begin:(end-begin+1)Γ·length:end]
B = B[begin:begin+length-1]
@info "B:" B[1] B[2] B[end] Base.length(B)
return B
end
helper2 (generic function with 1 method)
julia> helper2(A, length = 100)
β Info: B:
β B[1] = 1
β B[2] = 2
β B[end] = 100
β Base.length(B) = 100
1:1:100
julia> helper2(A, length = 95)
β Info: B:
β B[1] = 1
β B[2] = 2
β B[end] = 95
β Base.length(B) = 95
1:1:95
julia> helper2(A, length = 50)
β Info: B:
β B[1] = 1
β B[2] = 3
β B[end] = 99
β Base.length(B) = 50
1:2:99
julia> helper2(A, length = 45)
β Info: B:
β B[1] = 1
β B[2] = 3
β B[end] = 89
β Base.length(B) = 45
1:2:89
julia> helper2(A, length = 32)
β Info: B:
β B[1] = 1
β B[2] = 4
β B[end] = 94
β Base.length(B) = 32
1:3:94
julia> helper2(A, length = 16)
β Info: B:
β B[1] = 1
β B[2] = 7
β B[end] = 91
β Base.length(B) = 16
In summary, if you are flexible on the last index but want an integer step, there is an easy syntax if you can specify step
.
Great idea
Thank you for your help! I like you and @rocco_sprmnt21 using the div
function, though I havenβt quite made it work.
I tried
helper2(1:41, 23)
and got back 1:1:23
. This would downsample the long array but instead give only the first section of it.
This is very similar to @mbaumanβs solution - which is equally good. I slightly prefer using div
rather than round
(and rather than Γ·
which I canβt find on my keyboard).
So in the original notation, from now on Iβll use
sampleindices = range(start=1, step=div(length(longarray), numsamples), length=numsamples)
downsampled = longarray[sampleindices]
The reason for making sampleindices
is that other parallel arrays need indexing my typical use cases.
Thank you everyone!
In one case (round) the range 1:100 is sampled with variable step (3 or 4) but does cover the full interval from 1 to 100, while in the simpler/trivial case, a constant step of 3 is used to produce 30 indexes: 1:3:88, leaving a big gap in the tail (from 89 to 100).
help?> Γ·
"Γ·" can be typed by \div<tab>
These are important nuances to keep in mind for each use case! Iβd mark both as solutions if I could.
I wrote a more general function for similar usecases:
julia> using DataManipulation
julia> discreterange(identity, 1, 100; length=30)
30-element Vector{Int64}:
1
4
8
...
90
93
97
100
Its main target are transformed ranges, like logarithmically-spaced discreterange(log, 1, 1000; length=30)
. There, you cannot just create a float range and round it to integers.
But discreterange
works for regular linear ranges as well, as shown in the example.
I noticed that the initial indexes donβt really follow a logarithmic proportion.
In this specific example, the output is equivalent to:
[1:4; round.(Int, exp.(range(log(5), log(1000), length=30-4)))]
Of course, because that would be impossible (:
In some applications, it makes sense to allow repeated indexes at the beginning:
round.(Int, exp.(range(log(1), log(1000), length=30)))
Sure, the whole point of discreterange()
is to give distinct integers. So both variants are available, either for regular linear ranges of for mapped ones:
julia> round.(Int, maprange(log, 1, 100; length=20))
[1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 14, 18, 23, 30, 38, 48, 62, 78, 100]
julia> discreterange(log, 1, 100; length=20)
[1, 2, 3, 4, 5, 6, 7, 9, 11, 14, 17, 20, 25, 30, 37, 45, 55, 67, 82, 100]
Any particular reason for the name discreterange
, as opposed to something like integerrange
? I suppose βdiscreteβ is meant to imply integers, in the sense that floats are (nearly) continuous. But discrete mathematics can refer to finite countable objects, not necessarily one-to-one with all integers (or Int64).
Any particular reason for the name
discreterange
, as opposed to something likeintegerrange
?
Not really, just the first name that came to mind when I needed such a function (:
uniqueintegerrange
would be the most descriptive, even though a bit on the longer sideβ¦