# Strange behaviors in python and numpy

**URL:** <https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325>\
**Category:** Offtopic\
**Created:** [October 23, 2023, 5:56pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325 "2023-10-23T17:56:35Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [October 23, 2023, 5:56pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/1 "2023-10-23T17:56:35Z")

</div>

> [@Did Julia community do something to improve its correctness?](https://discourse.julialang.org/t/did-julia-community-do-something-to-improve-its-correctness/102515/141):
>
> can someone explain this?
> 
> ```julia
> In [1]: import numpy as np
> 
> In [2]: a = np.array([1,2,3])
> 
> In [3]: sum(a,a)
> Out[3]: array([7, 8, 9])
> 
> ```
> 
> Seems that `sum` is not intended to sum two arrays (one thing is the array being summed, the other I didn’t understand from the documentation. Fair enough, but I think it makes my point: I do not trust my intuition about what a function should do… not here, not with any other language.

Numpy just assumes that you want to do broadcasting here, so it just starts adding stuff to your numpy array.

```ipython
In [1]: import numpy as np

In [2]: a = np.array([1,2,3])

In [3]: sum(a,a)
Out[3]: array([7, 8, 9])

In [4]: ?sum
Signature: sum(iterable, /, start=0)
Docstring:
Return the sum of a 'start' value (default: 0) plus an iterable of numbers

When the iterable is empty, return the start value.
This function is intended specifically for use with numeric values and may
reject non-numeric types.
Type: builtin_function_or_method

In [5]: a + 1 + 2 + 3
Out[5]: array([7, 8, 9])

In [6]: a + sum(a)
Out[6]: array([7, 8, 9])

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 23, 2023, 7:03pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/2 "2023-10-23T19:03:59Z")

</div>

can we start a sub-thread about Numpy?

> <https://gist.github.com/Moelf/59d6312c51c250ba251125e54bea7282>

Nobody ever said Numpy is too buggy

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [October 26, 2023, 12:22pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/3 "2023-10-26T12:22:50Z")

</div>

Are you saying numpy might have correctness issues?

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [October 26, 2023, 12:29pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/4 "2023-10-26T12:29:07Z")

</div>

That’s not correctness issues. In `a in b` in Numpy means what `any(a .== b)` means in Julia. It’s a bad definition to be sure, but not really a correctness problem.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 26, 2023, 1:33pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/5 "2023-10-26T13:33:53Z")

</div>

> [@jakobnissen](#):
>
> `a in b` in Numpy means what `any(a .== b)`

except it also has this behavior

```julia
In [2]: np.array([1,2,3]) == np.array([1,2])
ValueError: operands could not be broadcast together with shapes (3,) (2,)

In [3]: np.array([1,2,3]) == np.array([2])
Out[3]: array([False, True, False])

In [12]: np.array([1,2,3]) in np.array([2])
Out[12]: True

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [October 26, 2023, 1:55pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/6 "2023-10-26T13:55:05Z")

</div>

No idea whether this is correct or incorrect but it did catch me by surprise when I was porting some Python code to Julia:

```julia
>>> import numpy as np
>>> x = np.float32(0)
>>> (x ** 2).dtype
dtype('float64')

```

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [October 26, 2023, 2:49pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/7 "2023-10-26T14:49:53Z")

</div>

@jling That behaviour is completely consistent. Remember that in NumPy, `a == b` is the same as Julia’s `a .== b`, and `a in b` is the same as Julia’s `any(a .== b)`.

So, we get the exact same behaviour in Julia:

```julia
julia> [1,2,3] .== [1, 2]
ERROR: DimensionMismatch: arrays could not be broadcast to a common size; got a dimension with lengths 3 and 2
[...]

julia> [1,2,3] .== [2]
3-element BitVector:
 0
 1
 0

julia> any([1,2,3] .== [2])
true

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 26, 2023, 2:51pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/8 "2023-10-26T14:51:29Z")

</div>

> [@jakobnissen](#):
>
> `julia> [1,2,3] .== [2]`

oh wow I actually hate this.

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [October 26, 2023, 2:54pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/9 "2023-10-26T14:54:14Z")

</div>

Like NumPy, we broadcast arrays with length 1 across a given dimension. This is useful, because we can do stuff like

```julia
julia> x = rand(4, 3);

julia> x_normed = x ./ sum(x; dims=2)
4×3 Matrix{Float64}:
 0.0099922 0.907046 0.0829616
 0.628005 0.0108374 0.361158
 0.568802 0.249942 0.181256
 0.361719 0.220452 0.417829

```

Which works, even though `sum` returns a `4 x 1` matrix.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 26, 2023, 3:26pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/10 "2023-10-26T15:26:29Z")

</div>

> [@jling](#):
>
> oh wow I actually hate this.

Isn’t this basically the whole point of broadcasting?

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 26, 2023, 3:32pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/11 "2023-10-26T15:32:15Z")

</div>

I would have thought in general

```julia
x != [x] != Ref(x)

```

for broadcasting, but idk, maybe I just don’t have a basic understanding of the point of broadcasting

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 26, 2023, 3:56pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/12 "2023-10-26T15:56:21Z")

</div>

Yeah, this is what I call “implicit” broadcasting — dimensions that _happen_ to have length one also “extrude out” (or repeat) to match other arguments exactly like missing dimensions do. I like [this visualization](https://yuri.is/julia-guide/#!/broadcasting).

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 26, 2023, 5:17pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/13 "2023-10-26T17:17:06Z")

</div>

> [@jling](#):
>
> I would have thought in general
> 
> ```julia
> x != [x] != Ref(x)
> 
> ```
> 
> for broadcasting

It would be a pity if we couldn’t do `matrix .* vec`, but had resort to padding `vec` with `repeat`, like Matlab in the bad old days (Matlab too broadcasts singleton dimensions these days, and I think numpy as well. In my mind, this is what ‘broadcasting’ means.)

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [October 26, 2023, 6:10pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/14 "2023-10-26T18:10:28Z")

</div>

> [@jling](#):
>
> > [@jakobnissen](#):
> >
> > `julia> [1,2,3] .== [2]`
> 
> oh wow I actually hate this.

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

> [@DNF](#):
>
> Isn’t this basically the whole point of broadcasting?

In my opinion, broadcasting should apply scalars to each element of a container but should not conflate singleton containers with scalars.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 26, 2023, 6:17pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/15 "2023-10-26T18:17:45Z")

</div>

How would you do broadcast multiply between a matrix and a vector, then? Use `repeat`?

Anyway, what are the drawbacks of this conflation? I can see only advantages.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [October 26, 2023, 7:39pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/16 "2023-10-26T19:39:58Z")

</div>

> [@DNF](#):
>
> what are the drawbacks of this conflation?

Mixing containers with their values complicates reasoning and hides errors. This could be an error, for example:

```julia
julia> [10] .* [2, 3]
2-element Vector{Int64}:
 20
 30

```

> [@DNF](#):
>
> How would you do broadcast multiply between a matrix and a vector, then?

I think you mean

```julia
julia> m = collect(reshape(1:6, 3,2))
3×2 Matrix{Int64}:
 1 4
 2 5
 3 6

julia> v = [1, 10, 100]
3-element Vector{Int64}:
   1
  10
 100

julia> m .* v
3×2 Matrix{Int64}:
   1 4
  20 50
 300 600

```

We can retain that without inconsistency. I like to think of matching up each dim from each argument until one runs out; semantically,

```julia
julia> mapslices(col -> map(*, col, v), m; dims=1)
3×2 Matrix{Int64}:
   1 4
  20 50
 300 600

```

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [October 26, 2023, 8:05pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/17 "2023-10-26T20:05:01Z")

</div>

I think part of this confusion stems from the fact that actually _scalars_ don’t fit the view of “extruding dimensions of size 1”. In that sense I have no problem accepting that `[1,2,3] .+ [1]` works and more the fact that `[1,2,3] .+ 1` _also_ works.  
However noone wants to wrap scalars in brackets and I think this problem is actually at least part of the reason that numbers are iterable in Julia because that means `5` also behaves as a container `5[1] == 5` and then broadcasting stays logically consistent.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 26, 2023, 8:15pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/18 "2023-10-26T20:15:47Z")

</div>

In my view it’s the other way around: Numbers, Refs and 0-dimensional arrays are the ones that are behaving completely unobjectionably. They all have `ndims(x) == 0` and `axes(x) = ()`. The “proper” way to index them is with `x[]`; `x[1]` is [a convenience](https://docs.julialang.org/en/v1/manual/arrays/#Omitted-and-extra-indices). They fit the view of “expanding [missing dimensions](https://docs.julialang.org/en/v1/base/arrays/#Base.Broadcast.broadcast)”. Data that has missing dimensions (or dimensions that are statically-enforced to be 1, like a transposed vector’s first dimension) will never unexpectedly change how it broadcasts. You might get caught out, however, if your matrix suddenly only has one row on some nth iteration.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [October 26, 2023, 9:54pm UTC](https://discourse.julialang.org/t/strange-behaviors-in-python-and-numpy/105325/19 "2023-10-26T21:54:45Z")

</div>

There are several ways to do broadcasting or extending operations across arrays:

1. Like Numpy or Julia with the following rules:

2. Like APL or J with the following rules:

3. Always using explicit and possible multiple mappings when applying scalar operations across arrays. Haskell with nested lists and `map . map` comes to mind or something like  
`mapslices(v -> let s = sum(v); map(x -> x / s, v) end, X; dims = 2)`.  
This can get quite verbose quickly and I will not expand on this here …

All models are perfectly consistent with the first one being less general than the second one regarding composed operations applying at different sub-dimensions (ranks). It is probably easier to understand though and a good compromise to combat the verbosity of the third.
