3
\$\begingroup\$

I have a library function that returns a pair of two numbers like this

def library_function(x0, x1):
    return x0**2 + x1**2, 10*x0*x1 + 1

(Actually, it is a function that returns predicted probabilities of two classes for some object, but it doesn't matter, so I created an artificial one.) I want to draw contours where the difference between two numbers returned by library_function is zero. To do so, I have to create a two-dimensional list (or array) with the differences.

I have the following possibilities:

X0 = np.linspace(-10, 10, 200)
X1 = np.linspace(-10, 10, 200)

results = [[(lambda u: u[0]-u[1])(library_function(x0, x1)) 
            for x0 in X0] for x1 in X1]
plt.contour(X0, X1, results, [0])

This approach is concise but I have to introduce lambda which seem to be considered not so Pythonic.

Another way:

values = [[library_function(x0, x1) for x0 in X0] for x1 in X1]
results = [[a-b for a, b in row] for row in values]

I have to introduce temporary two-dimensional list and this approach is longer.

Are there any ideas how to do it better (more concise and readable)?

\$\endgroup\$
2
  • \$\begingroup\$ Why must you have a single library_function that returns a pair? Perhaps it would be better as two functions? I think I would like to see what your real library_function is. \$\endgroup\$ Commented Nov 24, 2016 at 23:57
  • \$\begingroup\$ It's pybrain's .activate() method. It returns a predicted vector. I use it as two-class classifier and my output space is two-dimensional, and I'd like to draw a border between two classes, so I have to calculate the difference between the plausibilities returned by the function activate() and compare it with 0. (Oh, I just discovered that I can just use the first component and compare it with 1/2, which will work if my outputs are probabilites…) \$\endgroup\$ Commented Nov 25, 2016 at 0:03

2 Answers 2

3
\$\begingroup\$

Since you are using NumPy, you should deal with grids the NumPy way instead of performing list comprehensions.

def probability_diff(x0, x1):
    probs = library_function(x0, x1)
    return probs[0] - probs[1]

results = np.vectorize(probability_diff)(X0[:, np.newaxis], X1)

I wouldn't try to get too clever with the lambdas.

Also, since the grid happens to be a square, you could use X0 again instead of defining X1.

\$\endgroup\$
1
  • \$\begingroup\$ Good point about np.vectorize, didn't know about it, and a good reference to SO answer. \$\endgroup\$ Commented Nov 26, 2016 at 14:48
1
\$\begingroup\$

I think using list comprehensions is generally considered Pythonic. As an improvement, you can opt to use generators for your values variable.

values = ((library_function(x0, x1) for x0 in X0) for x1 in X1)
results = [[a-b for a, b in row] for row in values]

By using a generator, you're using a constant amount of space for the values variable. I also think that using more descriptive variable names would be helpful.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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