7
\$\begingroup\$

I’ve got a matrix of data and I want to take the average of the positive and negative parts of the values. Is there a more Pythonic way of doing it?

from numpy import *

…

exposures = matrix([0]*numPaths*numExposures)
exposures.shape = numPaths, numExposures

… # fill matrix

@vectorize
def plusv(x): return max(x,0)

@vectorize
def minv(x): return min(x,0)

ee  = transpose(exposures.mean(axis=0))
epe = transpose(plusv(exposures).mean(axis=0))
ene = transpose(minv(exposures).mean(axis=0))
\$\endgroup\$
3
\$\begingroup\$

There are a few things which we could improve:

When importing a module, you would not usually import all symbols from that module. You can however rename the module to make it more convenient, e.g.:

import numpy as np
# now we can do np.matrix(…), np.transpose(…), etc.

Instead of initializing a matrix and then reshaping it, create it directly in the shape you need:

exposures = np.zeros((numPaths, numExposures))

When defining a function put the function body on a new line, no matter how short it might be:

def plusv(x):
    return max(x, 0)

Instead of stuffing this normalization into a custom function, you could just use the builtin clip. It takes two arguments: a minimum and maximum bound, both inclusive. So plusv(a) would be equivalent to a.clip(0, np.inf) for floats and a.clip(0, np.iinfo(np.int).max) for integers.

The numpy functions can either be called as functions or as methods. Using them as methods is usually easier to read for people used to reading left-to-right.

ee  = exposures.mean(axis=0).transpose()
epe = exposures.clip(0, np.inf).mean(axis=0).transpose()
ene = exposures.clip(-np.inf, 0).mean(axis=0).transpose()

Oh dear, we are repeating code! Well, let's just use a list comprehension/generator expression instead:

positive_exposures = exposures.clip(0, np.inf)
negative_exposures = exposures.clip(-np.inf, 0)
ee, epe, ene = (xs.mean(axis=0).transpose() for xs in
    (exposures, positive_exposures, negative_exposures))

In the above snippet, the temporary variables positive_exposure and negative_exposure are entirely optional, but they make the code more self-documenting.

Note that I use snake_case for my variables. This is the convention in Python for normal variables or method names. The lowerCamelCase scheme is not generally used. While I recommend you follow the general Python convention of words separated by underscores, the only important thing is to be consistent. Whatever your naming scheme, names like ee and epe are not acceptable. Prefer full words over abbreviations, unless the abbreviations are common enough (i for an index is ok, and everyone understands str means “string“).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.