5

I have a = array([1, 2, 3, 4, 5]) and b = array([10, 20, 30, 40, 50]). I want:

array([[ -9, -19, -29, -39, -49],
       [ -8, -18, -28, -38, -48],
       [ -7, -17, -27, -37, -47],
       [ -6, -16, -26, -36, -46],
       [ -5, -15, -25, -35, -45]])

What's the most efficient way to do this? I have

np.transpose([a]) - np.tile(b, (len(a), 1))

However I wonder if there's a more efficient way than this if a is very large, which wouldn't require copying b so much (or vice versa).

Claudiu
  • 224,032
  • 165
  • 485
  • 680

2 Answers2

10

Some NumPy functions, like np.subtract, np.add, np.multiply, np.divide, np.logical_and, np.bitwise_and, etc. have an "outer" method which can be used to create the equivalent of "multiplication tables":

In [76]: np.subtract.outer(a, b)
Out[76]: 
array([[ -9, -19, -29, -39, -49],
       [ -8, -18, -28, -38, -48],
       [ -7, -17, -27, -37, -47],
       [ -6, -16, -26, -36, -46],
       [ -5, -15, -25, -35, -45]])

or, using broadcasting:

In [96]: a[:, None] - b
Out[96]: 
array([[ -9, -19, -29, -39, -49],
       [ -8, -18, -28, -38, -48],
       [ -7, -17, -27, -37, -47],
       [ -6, -16, -26, -36, -46],
       [ -5, -15, -25, -35, -45]])

The performance of the two is about the same:

In [98]: a = np.tile(a, 1000)

In [99]: b = np.tile(b, 1000)

In [100]: %timeit a[:, None] - b
10 loops, best of 3: 88.3 ms per loop

In [101]: %timeit np.subtract.outer(a, b)
10 loops, best of 3: 87.8 ms per loop

In [102]: %timeit np.transpose([a]) - np.tile(b, (len(a), 1))
10 loops, best of 3: 120 ms per loop
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • That second one gives a different result, as you can see – Claudiu Feb 17 '14 at 22:39
  • @unutbu You should add the timing of OP's method for comparison. – M4rtini Feb 17 '14 at 22:45
  • oh looks like `a[:, None]` is the same as `np.transpose([a])`.. so this is the same as mine except I don't have to do the `.tile` explicitly – Claudiu Feb 17 '14 at 22:46
  • @Claudiu: Yes; I think the difference is mainly due to broadcasting. – unutbu Feb 17 '14 at 22:50
  • @unutbu: Gotcha, makes sense thanks! I asked this question to help me answer [this one](http://stackoverflow.com/a/21839396/15055) - could you perchance take a look and let me know why the non-loop version isn't any faster than the loop version, even after applying the answer from here? – Claudiu Feb 17 '14 at 22:52
  • @Claudiu: To make it an apples-to-apples comparison, we need to make `func` and `optfunc` return numpy arrays rather than lists. [If you do that](https://gist.github.com/unutbu/9063262), then the relative timings of the loop vs non-loop versions becomes on my machine 2.977s vs. 1.959s. Unfortunately, building a Python list can be far faster than building a NumPy array. Even though NumPy operations are quicker, you have to overcome the penalty of instantiating the array(s) before using NumPy pays off. – unutbu Feb 18 '14 at 02:26
  • @Claudiu: If in optfunc you only loop 1000 times, then optfun (the looping version) is faster than optfun2 (the vectorized version). However, [if you increase the number of iterations to say 10000](https://gist.github.com/unutbu/9063494), then optfun2 becomes faster than optfun. 2.339s (looping) vs 0.913s (vectorize). – unutbu Feb 18 '14 at 02:27
  • 1
    @Claudiu: I added an `optfunc3` which is a tiny bit faster than `optfunc2`. It replaces 5 calls to `np.transpose` with 1 call. – unutbu Feb 18 '14 at 02:28
  • @unutbu: gotcha, that's what I figured about building the result. as to `optfunc3`, nice - i figured there was a way to optimize that but didn't put time into figuring it out – Claudiu Feb 18 '14 at 17:52
0

You can also use np.vstack for broadcasting but there isn't any new idea in that after other answers:

np.vstack(a)-b

However I like this one because it is very short.

Furthermore, it also works if a and b are basic Python lists (unlike the very handy a[:, None] - b in a comment above).

Thomas Baruchel
  • 7,236
  • 2
  • 27
  • 46