Tips for golfing with numpy, scipy, or pylab

10

There's already a comprehensive list of tips for python here, so what I'm asking for are tips that specifically apply to using the numpy, scipy or pylab libraries.

These can be either ways to shorten code already using numpy, or ways to shorten common python operations by using these libraries.

One tip per answer, please.

user2699

Posted 2018-02-23T21:54:05.457

Reputation: 538

Note that pylab is just matplotlib.pyplot + numpy in a deprecated common namespace. The numpy part of pylab is trivial in the sense that their imports have the same number of bytes, so only plotting stuff could additionaly come from pylab, but I suspect that's not what you had in mind with your question.

– Andras Deak – 2018-02-24T12:31:23.833

2@AndrasDeak, I'm aware that using pylab is considered bad practice, but very little in codegolf can be considered good practice. Pylab directly includes parts of many numpy packages. For example pylab.randint is valid where numpy would require numpy.random.randint. So for golfing pylab should provide shorter code. – user2699 – 2018-02-24T15:58:28.263

1I'm aware that deprecation is not a problem, my point was that it also doesn't give an advantage. I simply didn't realize that subpackages were also loaded into the pylab namespace like that! So sorry, you're perfectly right :) – Andras Deak – 2018-02-24T16:08:25.143

Answers

5

Make use of Numpy's broadcasting

Broadcasting means replicating a multidimensional array along some of its singleton dimensions to match the size of another array. This happens automatically for Numpy arrays when arithmetic operators are applied to them.

For example, to generate a 10×10 multiplication table you can use

import numpy
t=numpy.arange(1,11)
print(t*t[:,None]) # Or replace t[:,None] by [*zip(t)]

Try it online!

Here t is created as the Numpy array [1, 2, ..., 10]. This has shape (10,), which is equivalent to (1,10). The other operand array, t[:,None], has size (10,1). Multiplying the two arrays implicitly replicates them, so they behave as if they both had shape (10,10). The result, which also has shape (10,10), contains the products for all pairs of entries in the original arrays.

Luis Mendo

Posted 2018-02-23T21:54:05.457

Reputation: 87 464

That was a clever use of zip with the broadcasting, is that going to come up in it's own answer? – user2699 – 2018-02-23T22:42:03.670

@user2699 I don't think it's worth a separate answer, because [*zip(t)] has the same byte count as the more readable t[:,None]. But you are right, it may be worth noting, so I added it back here – Luis Mendo – 2018-02-23T22:48:45.170

Good point, I guess I didn't actually count the bytes. [*zip(t)] would be two bytes shorter if there were more dimensions. – user2699 – 2018-02-23T23:00:07.077

1Note that extended iterable unpacking in [*zip(t)] will only work on python 3. – Andras Deak – 2018-02-23T23:28:57.247

I viewed this page as I am interested in finding out what numpy has that Perl 6 doesn't. Anyway that would be written as my \t = 1..10; .fmt('%3d').put for t «*» t[*,Empty] or you could use zip(t) – Brad Gilbert b2gills – 2018-02-24T02:38:33.670

2

Use r_[...] instead of range(...)

Numpy provides matlab like syntax for array creation using r_[...]. Any slice notation in between the brackets is interpreted as an array with the range indicated. So, for instance

r_[:30:4]

is equivalent to

arange(0,30,4)

and for most uses can replace

range(0,30 4)

It can also handle more complex expressions. For example to get indices from 0 up to 10 and back down again,

r_[:10,10:-1:-1]

user2699

Posted 2018-02-23T21:54:05.457

Reputation: 538