0

Suppose I've got a set of background data measurements for different frequencies:

import numpy as np

C_bg_100kHz = 100*np.random.random(1000)
C_bg_200kHz = 200*np.random.random(1000)

Where C_bg_100kHz is some background noise for 100kHz measurements, and C_bg_200kHz is background noise for 200kHz measurements. I would like to create a function that subtracts the mean of these background data measurements from some array of measurement data, where I can specify as one of the function parameters which background data set I want to subtract. I have managed to make this fuction using eval():

def subtract(array_y,freq):
    bg = eval('C_bg_' + freq)
    return array_y - np.ones(len(array_y))*np.mean(bg)
>>> subtract([50,50,50],'100kHz')
array([-0.36224706, -0.36224706, -0.36224706])

>>> subtract([50,50,50],'200kHz')
array([-47.95860607, -47.95860607, -47.95860607])

Here, I can enter my data as array_y and subtract, for instance, the C_bg_100kHz dataset by passing '100kHz' as the freq input. Essentially, I want python to translate a string 'C_bg_100kHz' to the array C_bg_100kHz. However, this function uses eval(), which I've seen mentioned as something you don't want to do if you can avoid it. So, my question is whether I can avoid using eval() for this specific situation.

stan
  • 1
  • 5
    Yes, use a dictionary that maps string-names to the corresponding data. E.g. `data = {'C_bg_100kHz': ..., 'C_bg_200kHz': ...}`. Or just store the frequency as a numeric type and pass `freq` as a number, then access `data = {100: ..., 200: ...}`. – timgeb Jun 14 '22 at 10:25
  • don't use separated variables but keep as dictionary - `C_bg["100kHz"] = 100*np.random.random(1000)` – furas Jun 14 '22 at 10:26
  • 1
    There are a few other questions like this but the above is the first one I found. – Iguananaut Jun 14 '22 at 10:27

1 Answers1

0

I've found the below approach pretty flexible for me...

Put all those methods you want to dynamically call in a class, then call inspect to get the methods within that class - that'll return a list of tuple pairs that are callable, then push them into a dictionary...

import inspect

class testClass:

    def __init__(self):
        # Unfortunately, need to do this to make inspect work
        pass

    def foo1(self):
        print("Inside foo1")

    def foo2(self):
        print("Inside foo2")

myMethods = {row[0]:row[1] for row in inspect.getmembers(testClass(), predicate=inspect.ismethod)}
#Note, I am instantiating testClass for the above to work

# myMethods is now a dict of methods, so if you had a string coming out of your earlier work that was "foo1", 

str1 = "foo1"
myMethods[str1]()

>>> "Inside foo1"

Of course, you can pass parameters in, I'd suggest wrapping them up as kwargs for flexibility.

It is of course, just a fork off what has been suggested above by timgeb - but I don't like the idea of two points of truth within code. Here, when you update the class "testClass" or whatever you call it with a new method - or renamed method, then that automatically passes through to your calling algorithm, rather than having to manually update a dict.

Amiga500
  • 1,258
  • 1
  • 6
  • 11