# How to perform an argmax / argmin on a subset of a vector?

**URL:** <https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569>\
**Category:** New to Julia\
**Tags:** array\
**Created:** [October 28, 2021, 4:33pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569 "2021-10-28T16:33:36Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![erwanlecarpentier](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/erwanlecarpentier/32/23448_2.png) [@erwanlecarpentier](https://discourse.julialang.org/u/erwanlecarpentier)\
**Post date:** [October 28, 2021, 4:33pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/1 "2021-10-28T16:33:36Z")

</div>

Hi! Is there a way to perform an `argmax` on a subset of a vector determined by a bitarray. This bitarray tells whether the index should be considered or not.

**Example:** if my vector is `x = [0, 2, 1, 0]` and the bitarray is `b = [true, false, true, false]`, then the result should be `3` as only indexes `1` and `3` should be considered (as indicated by `b`).  
I cannot do `argmax(x[b])` as “filtering” with `b` modifies the indexing and thus return `2` instead of `3`.

Thanks!

---

<div class="post-metadata">

**Author:** ![aramirezreyes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aramirezreyes/32/42573_2.png) [@aramirezreyes](https://discourse.julialang.org/u/aramirezreyes)\
**Post date:** [October 28, 2021, 4:59pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/2 "2021-10-28T16:59:07Z")

</div>

> [@erwanlecarpentier](#):
>
> `b = [true, false, true, false]`

Probably someone will have a better idea, but one quick and dirty way for argmax could be:

```julia
julia> x = [0, 2, 1, 0]
4-element Vector{Int64}:
 0
 2
 1
 0

julia> b = [true, false, true, false]
4-element Vector{Bool}:
 1
 0
 1
 0

julia> argmax(x.*b)
3

```

All the elements multiplied by false will become zero and therefore will never be a max, provided you know your max is greater than zero.

You could easily adapt this method for argmin or a little bit more general by combining with Inf (see Gustaphe’s response below), but it will allocate an intermediate vector.

The other natural way would be with a loop. I think this is the best choice.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 28, 2021, 10:05pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/3 "2021-10-28T22:05:10Z")

</div>

```julia
argmax(x .- inf .* .!(b))

```

Doesn’t rely on positive `x`.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 28, 2021, 10:28pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/6 "2021-10-28T22:28:51Z")

</div>

In Julia 1.7 you can use (untested, on phone in bed):

```julia
_, ind = findmax((x,b) -> b ? x : -inf, zip(x, b))

```

Making a loop is probably more self documenting, but how cool is that?

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [October 28, 2021, 10:30pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/7 "2021-10-28T22:30:01Z")

</div>

in julia 1.6, this seems to work too:

```julia
julia> argmax(view(x,b))
2

```

---

<div class="post-metadata">

**Author:** ![aramirezreyes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aramirezreyes/32/42573_2.png) [@aramirezreyes](https://discourse.julialang.org/u/aramirezreyes)\
**Post date:** [October 28, 2021, 10:30pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/8 "2021-10-28T22:30:57Z")

</div>

This doesn’t do what is asked (the answer should be 3, not 2).

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [October 28, 2021, 10:31pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/9 "2021-10-28T22:31:18Z")

</div>

oh, sorry, you are right

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [October 28, 2021, 10:34pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/10 "2021-10-28T22:34:24Z")

</div>

How about

```julia
julia> findall(b)[argmax(@view x[b])]
3

```

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [October 28, 2021, 10:35pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/11 "2021-10-28T22:35:45Z")

</div>

this (now) also returns 3

```julia
julia> c = argmax(view(x,b))      
2 #index position of the nth true value in b
julia> findnext(identity,b,c)    
3

```

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 28, 2021, 10:36pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/12 "2021-10-28T22:36:17Z")

</div>

Dumber alternative:

```julia
eachindex(x)[b][argmax(x[b])]

```

---

<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:** [October 28, 2021, 10:48pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/13 "2021-10-28T22:48:58Z")

</div>

One more:

```julia
argmax(sortperm(x) .* b)

```

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 29, 2021, 5:18am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/14 "2021-10-29T05:18:47Z")

</div>

I ran a benchmark. Code below.

Note logarithmic `y` scale. Lower is better, and the lowest point is the most important. The numbers vary a bit with different runs, but the order is constant as far as I’ve seen. These are the functions I could make pass the test. My conclusion: `findall` or `findnext` wins (unless we count writing a loop to do it, which is a bit sad)

![benchmark](https://global.discourse-cdn.com/julialang/original/3X/6/b/6ba51acad21f8e27ab8a0b61bbfcefa903e99aa9.png)

> **Code**
>
> ```julia
> #!/bin/julia
> 
> using BenchmarkTools
> using BenchmarkPlots
> using StatsPlots
> using Test
> using Random: bitrand
> 
> BenchmarkTools.DEFAULT_PARAMETERS.seconds = 20
> N = 10_000
> 
> preservingargmax = Dict{Symbol,Function}()
> 
> preservingargmax[:Inf] = (x, b) -> argmax(x .- Inf .* .!(b))
> preservingargmax[:findall] = (x, b) -> findall(b)[argmax(x[b])]
> preservingargmax[:findall_views] = (x, b) -> first(@view findall(b)[argmax(@view x[b])])
> preservingargmax[:findnext] = (x, b) -> begin
> c = argmax(x[b])
> i0 = findfirst(identity, b)
> for _ in 1:c-1
> i0 = findnext(identity, b, i0 + 1)
> end
> return i0
> end
> preservingargmax[:findnext_views] = (x, b) -> begin
> c = argmax(view(x, b))
> i0 = findfirst(identity, b)
> for _ in 1:c-1
> i0 = findnext(identity, b, i0 + 1)
> end
> return i0
> end
> preservingargmax[:eachindex] = (x, b) -> eachindex(x)[b][argmax(x[b])]
> preservingargmax[:eachindex_views] = (x, b) -> first(view(view(eachindex(x),b),argmax(view(x,b))))
> preservingargmax[:sortperm] = (x, b) -> argmax(sortperm(x) .* b)
> preservingargmax[:argmax] = (x, b) -> argmax(i -> b[i] ? x[i] : -Inf, eachindex(x))
> preservingargmax[:enumerate] = (x, b) -> first(argmax(t -> t[2][1] ? t[2][2] : -Inf, enumerate(zip(b, x))))
> preservingargmax[:zip] = (x, b) -> first(argmax(t -> t[2] ? t[3] : -Inf, zip(eachindex(x), b, x)))
> preservingargmax[:loop] = (x, b) -> begin
> maxx = -Inf
> maxi = 0
> for (xi, bi, i) in zip(x, b, eachindex(x))
> (bi && xi > maxx) || continue
> maxx = xi
> maxi = i
> end
> return maxi
> end
> preservingargmax[:branchless] = (x, b) -> begin
> maxx = -Inf
> maxi = 0
> for (xi, bi, i) in zip(x, b, eachindex(x))
> maxi = (bi && xi > maxx)*i + !(bi && xi > maxx)*maxi
> maxx = (bi && xi > maxx)*xi + !(bi && xi > maxx)*maxx
> end
> return maxi
> end
> preservingargmax[:to_index] = (x, b) -> (i=Base.to_index(b); Vector{Int}(i)[argmax(x[i])])
> preservingargmax[:typemin] = (x, b) -> (y = copy(x); y[.!b] .= typemin(eltype(x)); argmax(y))
> preservingargmax[:parentindices] = (x, b) -> (v = view(x, b); first(parentindices(v))[argmax(v)])
> 
> labels = [
> :Inf,
> :findall,
> :findall_views,
> :findnext,
> :findnext_views,
> :eachindex,
> :eachindex_views,
> :sortperm,
> :argmax,
> :enumerate,
> :zip,
> :loop,
> :branchless,
> :to_index,
> :typemin,
> :parentindices,
> ]
> 
> bg = BenchmarkGroup()
> 
> x_1 = [0, 2, 1, -3, 0]
> b_1 = [true, false, true, true, false]
> x_2 = collect(1:100)
> b_2 = falses(100); b_2[[1, 3, 10, 100]] .= true
> @testset "correctness, $k" for (k, f) in preservingargmax
> @test f(x_1, b_1) == 3
> @test f(x_2, b_2) == 100
> bg[k] = @benchmarkable $f(x, b) setup = (x = randn($N); b = bitrand($N))
> end
> 
> res = run(bg)
> 
> plot(
> res,
> labels,
> ;
> yscale=:log10,
> xrotation=30,
> )
> 
> savefig("benchmark.png")
> 
> ```

---

<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:** [October 29, 2021, 6:31am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/15 "2021-10-29T06:31:31Z")

</div>

@gustaphe, thanks for sharing this brilliant benchmarking code.  
For large arrays, @longemen3000’s `findnext` solution seems to be the fastest.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [October 29, 2021, 6:33am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/16 "2021-10-29T06:33:37Z")

</div>

Another one with Julia 1.7:

```julia
argmax(i -> b[i] ? x[i] : -Inf, eachindex(x))

```

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 29, 2021, 6:54am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/17 "2021-10-29T06:54:43Z")

</div>

Added to the benchmark. I tried to make something similar to that before, it’s really annoying there is no `indmax` function.

I’m also trying

```julia
first(argmax(t -> t[2][1] ? t[2][2] : -Inf, enumerate(zip(b, x))))

```

for which I had high hopes. Sadly it’s the second worst suggestion yet ☹

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 29, 2021, 7:07am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/18 "2021-10-29T07:07:48Z")

</div>

The current leader is simply writing a loop. I wish it weren’t so, but sometimes you have to do things yourself.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [October 29, 2021, 11:53am UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/19 "2021-10-29T11:53:47Z")

</div>

I feel like there is a package somewhere that does views that have offset indexes to match the original positions.  
But it isn’t in [OffsetArrays](https://github.com/JuliaArrays/OffsetArrays.jl) nor [PaddedViews.jl](https://github.com/JuliaArrays/PaddedViews.jl)

---

<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:** [October 29, 2021, 8:51pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/20 "2021-10-29T20:51:16Z")

</div>

This thread is addictive…

One more lamp to add to Gustaphe’s fantastic lamp store:

```julia
function argmax_typemin(x,b)
    y = copy(x)
    y[.!b] .= typemin(eltype(x))
    argmax(y)
end

```

For N=10K, the store’s window display is (_updated based on posts further down_):

![benchmark_conditional_argmax_10K_r1](https://global.discourse-cdn.com/julialang/original/3X/8/7/87df9e8d2145649c2a4ff48949713583fdbe5f5d.png)

If one runs again the benchmark for N=10K, as the input is random, some lamps change shape and relative position.

![benchmark_conditional_argmax_10K_r2](https://global.discourse-cdn.com/julialang/original/3X/8/4/8468da5257d363d96bdcfb532d9cc7fecd1c8b43.png)

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 29, 2021, 9:09pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/21 "2021-10-29T21:09:43Z")

</div>

One day I want to (have the time to) implement a group wide `setup` for BenchmarkTools so you can have a common rand seed for every test. Sorely lacking functionality.

---

<div class="post-metadata">

**Author:** ![StevenWhitaker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevenwhitaker/32/9749_2.png) [@StevenWhitaker](https://discourse.julialang.org/u/StevenWhitaker)\
**Post date:** [October 29, 2021, 9:13pm UTC](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569/22 "2021-10-29T21:13:26Z")

</div>

> [@longemen3000](#):
>
> ```julia
> julia> c = argmax(view(x,b))      
> 2 #index position of the nth true value in b
> julia> findnext(identity,b,c)    
> 3
> 
> ```

I might be missing something, but the above code doesn’t seem to work in general:

```julia
julia> x = 1:100
1:100 # Maximum value is 100 at index 100

julia> b = falses(100); b[[1, 3, 10, 100]] .= true; # Compare only these indexes

julia> c = argmax(view(x, b))
4 # Index of maximal value in x[b]

julia> findnext(identity, b, c)
10 # Should be 100

```

[Next page](https://discourse.julialang.org/t/how-to-perform-an-argmax-argmin-on-a-subset-of-a-vector/70569.md?page=2)
