# Reducing over Point objects

**URL:** <https://discourse.julialang.org/t/reducing-over-point-objects/133540>\
**Category:** General Usage\
**Tags:** statistics, reduction, vector-of-vectors\
**Created:** [October 30, 2025, 9:32am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540 "2025-10-30T09:32:55Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [October 30, 2025, 9:32am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/1 "2025-10-30T09:32:55Z")

</div>

Julia has many statistics-computing functions, typically a “reduce” kind of function, that can take either a simple vector, or go over an array and compute the output over row, columns, etc according to the `dims` parameter.

Is there a version for this that goes over a collection of vectors, tuples or `Point`s?

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 30, 2025, 10:41am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/2 "2025-10-30T10:41:31Z")

</div>

According to my understanding, the function `map` is way more useful than `reduce` in your statistics context. Because `reduce` is for 2-arg functions e.g. \* and +.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [October 30, 2025, 6:27pm UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/3 "2025-10-30T18:27:26Z")

</div>

Could you give some examples of what exactly you want to achieve? I would expect `reduce` to already work for a well-chosen function.

```julia-repl
julia> x = rand(Tuple{Float64, Float64}, 3)
3-element Vector{Tuple{Float64, Float64}}:
 (0.25205814867458654, 0.14284143829270268)
 (0.10081672872462866, 0.5909316913764835)
 (0.14787926347212343, 0.4637800398205445)

julia> reduce((a, b) -> max.(a, b), x)
(0.25205814867458654, 0.5909316913764835)

julia> reduce((a, b) -> a .+ b, x)
(0.5007541408713386, 1.1975531694897308)

```

---

<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:** [October 30, 2025, 6:30pm UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/4 "2025-10-30T18:30:48Z")

</div>

> [@eldee](#):
>
> `reduce((a, b) -> a .+ b, x)`

You can also just do `reduce(.+, x)`.

> [@xor0110](#):
>
> Is there a version for this that goes over a collection of vectors, tuples or `Point`s?

If your type supports a `+` function, like a vector, you can just use a function like `sum` and it will work.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 31, 2025, 12:28am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/5 "2025-10-31T00:28:15Z")

</div>

Admittedly, `reduce` is in some cases fast. But I still don’t think it is proper to be used in statistics, e.g.

```julia-auto
julia> v = [1,2,3,4]; # a vector of samples

julia> maximum(abs2, v)
16

julia> maximum(abs2, reverse(v))
16

julia> reduce((a, b) -> max(abs2(a), abs2(b)), v)
256

julia> reduce((a, b) -> max(abs2(a), abs2(b)), reverse(v))
65536

```

As the doc suggests, `reduce` is almost exclusively only for `+, *, max, min`.

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [October 31, 2025, 7:36am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/6 "2025-10-31T07:36:39Z")

</div>

Well, these are just two different things that are being computed. Not sure if that makes `reduce` more or less suitable for statistics than `map`…

But we don’t even have to choose between `map` and `reduce`, we can just use `mapreduce` 😁

```julia-repl
julia> v = [1,2,3,4];

julia> mapreduce(abs2, max, v)
16

julia> mapreduce(abs2, max, reverse(v))
16

```

> [@WalterMadelim](#):
>
> As the doc suggests, `reduce` is almost exclusively only for `+, *, max, min`.

I’m not sure about this either, but the docs do mention explicitly that `maximum`, `sum`, etc. should be preferred over writing out `reduce(max, ...)`, `reduce(+, ...)` etc. if possible. So in that example, I agree that `maximum(abs2, v)` is more intuitive.

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [November 20, 2025, 6:59pm UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/7 "2025-11-20T18:59:29Z")

</div>

Apologies that my first message was not too clear. These examples should better illustrate what happens.

```julia-auto

julia> data = randn(Point3f,111);

julia> mean(data)
3-element Point{3, Float32} with indices SOneTo(3):
 0.09946571
 0.043787897
 0.029109783

julia> median(data)
-0.20733127f0

julia> maximum(data)
3-element Point{3, Float32} with indices SOneTo(3):
  2.6677918
  1.3926599
 -0.09724049

```

The challenge is that `Point` has specific semantics associated with each of these operations I’m interested in. For `mean` or `sum`, it works as I want. For the other operations, that’s not what I want. I want to apply that computation across each different dimension. This would take some kind of specialized functor for Points that does that, it would be the same as `stack`ing the vector of points, then doing `maximum(xx, dims=2)`, and then returning that as a `Point`. The whole _point_ of this is that I’m trying to use `Point` more often instead of vectors, so I’m trying to make operations that I commonly use with vectors and arrays easier to perform when my data is represented as a collection of Points.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [November 20, 2025, 7:52pm UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/8 "2025-11-20T19:52:30Z")

</div>

> [@xor0110](#):
>
> For `mean` or `sum`, it works as I want. For the other operations, that’s not what I want. I want to apply that computation across each different dimension.

Addition and scaling of `Point3f`s indeed behaves as you expect, i.e. componentwise, and therefore `sum` and `mean` work for you without issue. But `Point3f`s are sorted lexicographically, so `maximum(data)`, which should return the largest `Point3f` in `data` according to this order, typically simply yields the one with largest first component.

I’m not completely sure what the rationale is for `median`, but we get the `middle` of the midpoint according to the order: `mean(extrema(sort(data)[56]))`. In fact, as `median(rand(Point3f, 2))` throws, this is probably not intended. Personally, I would expect to get a `Point3f` back: the (mean of the) middle (two) `Point3f`(s), where we again use the default lexicographic order.

In any case, my point is that `Vector{Point3f}`s are not the type to use if you want e.g. componentwise maximum. But you can always just ‘convert’ it to a `Matrix` for free using `reinterpet` and then use the `maximum(..., dims=2)` you mentioned:

```julia-repl
julia> data = randn(Point3f, 3)
3-element Vector{Point{3, Float32}}:
 [1.0567589, 0.5325625, -0.87686133]
 [1.8618298, 0.081127204, 0.8937693]
 [1.2074091, 0.44339427, -0.09087456]

julia> maximum(data)
3-element Point{3, Float32} with indices SOneTo(3):
 1.8618298
 0.081127204
 0.8937693

julia> maximum(reinterpret(reshape, Float32, data), dims=2)
3×1 Matrix{Float32}:
 1.8618298
 0.5325625
 0.893769

```

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [November 21, 2025, 8:47am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/9 "2025-11-21T08:47:51Z")

</div>

Correct! The point is, I would like a functor that does this job generically. Not sure how to call it and what would be the best implementation… `pointreduce(F, mypoints) = F(reinterpret(mypoints ,...), dims=2)` something like this…

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [November 22, 2025, 10:27am UTC](https://discourse.julialang.org/t/reducing-over-point-objects/133540/10 "2025-11-22T10:27:25Z")

</div>

You could use something like

```julia
componentwise(pts::AbstractVector{Point3f}, f, args...; kwargs...) = Point3f(ntuple(i -> f(args..., view(reinterpret(reshape, Float32, pts), i, :); kwargs...), Val(3)))

```

```julia-repl
julia> data = randn(Point3f, 3)
3-element Vector{Point{3, Float32}}:
 [1.5850062, 0.63555634, -2.3332644]
 [-0.85313797, 0.6584275, -0.274162]
 [-0.47674105, -0.7140366, -1.309917]

julia> componentwise(data, median)
3-element Point{3, Float32} with indices SOneTo(3):
 -0.47674105
  0.63555634
 -1.30991

julia> componentwise(data, reduce, max; init=1.f0)
3-element Point{3, Float32} with indices SOneTo(3):
 1.5850062
 1.0
 1.0

```

I’m not sure if this is better for e.g. `maximum(reinterpret(reshape, Float32, data), dims=2)`, but note that in `componentwise(data, maximum)` we are certainly not accessing the entries in memory-order. For `Point3f` this is probably not too much of an issue though. Also, for some reason (related to the splatting and maybe the anonymous function capture) we have two allocations even though `componentwise` feels like it should be allocation-free if `f` is so.

If you want to squeeze out every last bit of performance, this approach is then not fully optimal, but if not, it could be convenient.
