Python : pass variable name as argument - python

I have a function f(x) in which many local variables are created. x is a string with the same name as one of these local variables and I would like to change this local variable by changing x. What is the clean way to do this? Currently I am using a lot of if/elif statements.
Some dummy code to represent my problem:
def f(x):
a = [1,2,3]
b = [2,3,4]
c = [3,4,5]
if x == "a":
a[0] = 10
elif x == "b":
b[0] = 10
elif x == "c":
c[0] = 10
return a,b,c
I would like for the right variable to change value but using all these if/elif statements feels a bit redundant.

Use a dict
Simply, use a dict:
def f(x):
values = {
"a": [1,2,3],
"b": [2,3,4],
"c": [3,4,5]
}
values[x][0] = 10
return values["a"], values["b"], values["c"]
If you really really want, use your original code and do locals()[x][0] = 10 but that's not really recommended because you could cause unwanted issues if the argument is the name of some other variable you don't want changed.

use dictionary like this:
def f(x):
d = {"a" :[1,2,3],"b" : [2,3,4],"c" : [3,4,5]}
d[x][0] = 10
return d

If your input is a string and you want to refer to a variable that matches that string you can use globals() like this:
globals()['x']
This way you can get the value and/or edit its contents.

You can use exec() and put your code in argument as a string
def f(x):
a = [1,2,3]
b = [2,3,4]
c = [3,4,5]
exec(x + "[0] = 10")
return a,b,c
print f("a")
# ([10, 2, 3], [2, 3, 4], [3, 4, 5])

Related

R's eval(parse(text=text)) equivalent in Python

I'm looking for a way to assign variables dynamically using loop & functions in Python. One way I can do so in R is by using eval(parse(text=text)). For example, assuming I have this code:
var <- c('First','Second','Third')
for (i in 1:length(var)){
text <- paste0("Variable_", var[i], " <- ", i)
eval(parse(text = text))
}
And my desired output is as follow:
> Variable_First
[1] 1
> Variable_Second
[1] 2
> Variable_Third
[1] 3
What is the equivalent way of doing such in Python?
Thanks in advance!
Well, if you really want to do this you can do it in pretty much the same way with exec. Its probably not the best thing to do though...
var = ["First", "Second", "Third"]
for i, j in enumerate(var, start=1):
exec(f"Variable_{j} = i")
Giving
>>> Variable_First
1
>>> Variable_Second
2
>>> Variable_Third
3
Use dictionaries to accomplish something similar: "variable" variables in a restricted namespace. For example, here a dictionary comprehension is used to assign to dictionary dct:
lst = ['First','Second','Third']
dct = {f'Variable_{s}': i for i, s in enumerate(lst)}
print(dct)
# {'Variable_First': 0, 'Variable_Second': 1, 'Variable_Third': 2}

Why a dictionary will be changed by a class instance, but an integer won't

I'm learning about python class. In a toy python script,
class test():
def __init__(self, a, b):
self.new = a
self.old = b
def another_one(self):
temp = self.new
for key in temp.keys():
temp[key] += 1
def old_one(self):
old = self.old
old += 1
a = {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5}
b = 5
test_pass = test(a, b)
test_pass.another_one(), test_pass.old_one()
a, b
I found that by running the method another_one of instance test_pass, dictionary a will be changed. However, integer b will not be altered by running the method old_one.
Why a dictionary will be changed while an integer will not?
Python integers are immutable, you can't change them in-place.
Take a look at the following snippet:
x = 2
print(id(x))
x += 1
print(id(x))
x = [2]
print(id(x))
x += [1]
print(id(x))
You can see that the first part, where the integer is being modified, the unique id() of the object changes. Afterwards x is a completely different object.
When modifying the list, its 'id()' does not change, the list is changes in-place.
The integer is immutable, they can't be magically turn into something different. Lists can.

I want to convert the categorical variable to numerical in Python

I have a dataframe having categorical variables. I want to convert them to the numerical using the following logic:
I have 2 lists one contains the distinct categorical values in the column and the second list contains the values for each category. Now i need to map these values in place of those categorical values.
For Eg:
List_A = ['A','B','C','D','E']
List_B = [3,2,1,1,2]
I need to replace A with 3, B with 2, C and D with 1 and E with 2.
Is there any way to do this in Python.
I can do this by applying multiple for loops but I am looking for some easier way or some direct function if there is any.
Any help is very much appreciated, Thanks in Advance.
Create a mapping dict
List_A = ['A','B','C','D','E',]
List_B = [3,2,1,1,2]
d=dict(zip(List_A, List_B))
new_list=['A','B','C','D','E','A','B']
new_mapped_list=[d[v] for v in new_list if v in d]
new_mapped_list
Or define a function and use map
List_A = ['A','B','C','D','E',]
List_B = [3,2,1,1,2]
d=dict(zip(List_A, List_B))
def mapper(value):
if value in d:
return d[value]
return None
new_list=['A','B','C','D','E','A','B']
map(mapper,new_list)
Suppose df is your data frame and "Category" is the name of the column holding your categories:
df[df.Category == "A"] = 3,2, 1, 1, 2
df[(df.Category == "B") | (df.Category == "E") ] = 2
df[(df.Category == "C") | (df.Category == "D") ] = 1
If you only need to replace values in one list with the values of other and the structure is like the one you say. Two list, same lenght and same position, then you only need this:
list_a = []
list_a = list_b
A more convoluted solution would be like this, with a function that will create a dictionary that you can use on other lists:
# we make a function
def convert_list(ls_a,ls_b):
dic_new = {}
for letter,number in zip(ls_a,ls_b):
dic_new[letter] = number
return dic_new
This will make a dictionary with the combinations you need. You pass the two list, then you can use that dictionary on other list:
List_A = ['A','B','C','D','E']
List_B = [3,2,1,1,2]
dic_new = convert_list(ls_a, ls_b)
other_list = ['a','b','c','d']
for _ in other_list:
print(dic_new[_.upper()])
# prints
3
2
1
1
cheers
You could use a solution from machine learning scikit-learn module.
OneHotEncoder
LabelEncoder
http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html
The pandas "hard" way:
https://stackoverflow.com/a/29330853/9799449

Can I shorten the process of adding multiple variables in Python?

I have a list and from that list every variable uses one index for a value. Example:
val = [2, 4, 8, 6]
var1 = val[0]
var2 = val[1]
var3 = val[2]
var4 = val[3]
Can I put this into a loop somehow? Because I have 20 values so it is long to write 20 variables.
P.S of course, the values from added variables must be usable. And the format I'm using those variables looks like this:
D = {u'label1': var1, u'label2: var2...}
For your specific issue you could use your dict directly from the list
D = {u'label0' : var[0], u'label1' : val[1],...}
and create the dict as
D = dict(("var{}".format(i),v) for i,v in enumerate(val))
Then, you refer to it as values["var1"] for example, where you can put as key the name you like, label_ for instance.
Try it,
label_dict = {}
for i in range(len(val)):
label_dict['label' + str(i+1)] = val[i]

Python higher-order sequence assignment?

Is there a way to group names together in python, to repeatedly assign to them en masse?
While we can do:
a,b,c = (1,2,3)
I would like to be able to do something like:
names = a,b,c
*names = (3,2,1) # this syntax doesn't work
a,b,c == (3,2,1) #=> True
Is there a built-in syntax for this? If not, I assume it would be possible with an object that overloads its assignment operator. In that case, is there an existing implementation, and would this concept have any unexpected failure modes?
The point is not to use the names as data, but rather to be able to use the actual names as variables that each refer to their own individual item, and to be able to use the list as a list, and to avoid code like:
a = 1
b = 2
c = 3
sequence = (a,b,c)
You should go one level up in your data abstraction. You are not trying to access the entries by their individual names -- you rather use names to denote the whole collection of values, so a simple list might be what you want.
If you want both, a name for the collection and names for the individual items, then a dictionary might be the way to go:
names = "a b c".split()
d = dict(zip(names, (1, 2, 3)))
d.update(zip(names, (3, 2, 1)))
If you need something like this repeatedly, you might want to define a class with the names as attributes:
class X(object):
def __init__(self, a, b, c):
self.update(a, b, c)
def update(self, a, b, c)
self.a, self.b, self.c = a, b, c
x = X(1, 2, 3)
x.update(3, 2, 1)
print x.a, x.b. x.c
This reflects that you want to block a, b and c to some common structure, but keep the option to access them individually by name.
This?
>>> from collections import namedtuple
>>> names = namedtuple( 'names', ['a','b','c'] )
>>> thing= names(3,2,1)
>>> thing.a
3
>>> thing.b
2
>>> thing.c
1
You should use a dict:
>>> d = {"a": 1, "b": 2, "c": 3}
>>> d.update({"a": 8})
>>> print(d)
{"a": 8, "c": 3, "b": 2}
I've realised that "exotic" syntax is probably unnecessary. Instead the following achieves what I wanted: (1) to avoid repeating the names and (2) to capture them as a sequence:
sequence = (a,b,c) = (1,2,3)
Of course, this won't allow:
*names = (3,2,1) # this syntax doesn't work
a,b,c == (3,2,1) #=> True
So, it won't facilitate repeated assignment to the same group of names without writing out those names repeatedly (except in a loop).
Well, you shouldn't do this, since it's potentially unsafe, but you can use the exec statement
>>> names = "a, b, c"
>>> tup = 1,2,3
>>> exec names + "=" + repr(tup)
>>> a, b, c
(1, 2, 3)
Python has such an elegant namespace system:
#!/usr/bin/env python
class GenericContainer(object):
def __init__(self, *args, **kwargs):
self._names = []
self._names.extend(args)
self.set(**kwargs)
def set(self, *args, **kwargs):
for i, value in enumerate(args):
self.__dict__[self._names[i]] = value
for name, value in kwargs.items():
if name not in self._names:
self._names.append(name)
self.__dict__[name] = value
def zip(self, names, values):
self.set(**dict(zip(names, values)))
def main():
x = GenericContainer('a', 'b', 'c')
x.set(1, 2, 3, d=4)
x.a = 10
print (x.a, x.b, x.c, x.d,)
y = GenericContainer(a=1, b=2, c=3)
y.set(3, 2, 1)
print (y.a, y.b, y.c,)
y.set(**dict(zip(('a', 'b', 'c'), (1, 2, 3))))
print (y.a, y.b, y.c,)
names = 'x', 'y', 'z'
y.zip(names, (4, 5, 6))
print (y.x, y.y, y.z,)
if __name__ == '__main__':
main()
Each instance of GenericContainer is an isolated namespace. IMHO it is better than messing with the local namespace even if you are programming under a pure procedural paradigm.
Not sure whether this is what you want...
>>> a,b,c = (1,2,3)
>>> names = (a,b,c)
>>> names
(1, 2, 3)
>>> (a,b,c) == names
True
>>> (a,b,c) == (1,2,3)
True

Categories