parsing a conditional from a string

Viewed 82

I have two numpy arrays a, b and a string of the form s = "1.5 * b if 2*a > 7 else a + b"

I would like to create an array c, which will evaluate the string on the arrays in an efficient manner.

Example of a desired behavior:

a = np.array([1, 4])
b = np.array([3, 1])
s = "1.5 * b if 2*a > 7 else a + b"
print(my_eval(a, b, s))

[4, 1.5]

Was thinking of something like f = np.vectorize(eval(s)); map(a, b, f)

What's the best way to do it? The number of arrays in the expression can be larger (but bounded by something reasonable.

1 Answers

I can't speak for the efficiency, but you could use the given string expression s in a function definition template string, execute it into a local dictionary with exec, vectorize it for non-ufunc expressions, and then call it within the proposed my_eval function:

import numpy as np


def my_eval(s, a, b):

    locals_dict = {}

    # Generates source code to define a new function from the given string.
    source = f"def f(a, b): return {s}"

    # Executes the function definition script into locals_dict.
    exec(source, globals(), locals_dict)

    # Defines a vectorized version of the newly defined function.
    f = np.vectorize(locals_dict["f"])

    # Applies the function.
    return f(a, b)


s = "1.5 * b if 2 * a > 7 else a + b"
a = np.array([1, 4]).astype(float)
b = np.array([3, 1]).astype(float)
c = my_eval(s, a, b)

print(c)

This can modified to handle variable numbers of input arguments. For example, something like the following could handle up to 26 different input arrays, one for each letter of the alphabet:

import numpy as np
from string import ascii_lowercase


def my_eval(s, *args):

    locals_dict = {}

    # Generates source code to define a new function from the given string.
    params = ", ".join(list(ascii_lowercase[0:len(args)]))
    source = f"def f(*args): {params} = args; return {s}"

    # Executes the function definition script into locals_dict.
    exec(source, globals(), locals_dict)

    # Defines a vectorized version of the newly defined function.
    f = np.vectorize(locals_dict["f"])

    # Applies the function.
    return f(*args)
Related