Python concatenate arrays based on information from a list - python

I have a list with about 500 elements in it. For illustration I have:
list3 = [ 'a', 'b', 'c', 'a' ]
Where 'a', 'b', 'c' is name of the arrays as:
a = np.random.normal( 0, 1, ( 500, 20 ) )
b = np.random.normal( 0, 1, ( 500, 30 ) )
c = np.random.normal( 0, 1, ( 500, 30 ) )
I want to concatenate the arrays in the list in the order present in the list.
So, for my example I want to obtain:
C = np.concatenate( ( a, b, c, a ), 1 )
I don't have an idea how to approach this other than to store the arrays in a dictionary and then do a string search and concatenation in a for loop. Is there an elegant way to do this ?

You can use the locals() dictionary to access the variables by name
d = locals()
np.concatenate([d[x] for x in list3], 1)

If you want to be compact:
np.concatenate([dict(a=a, b=b, c=c)[x] for x in list3], 1)
Or to avoid the redundant dictionary creation:
by_label = dict(a=a, b=b, c=c)
np.concatenate([by_label[x] for x in list3], 1)

You can use the globals object to get the arrays based on name.
globals()["a"] # array a
So can do
np.concatenate(tuple(globals()[x] for x in list3),1)

You can easily get such a dictionary of all local variables by calling the locals() function. For example, to look up a variable named 'a':
var = 'a'
locals()[var]
Since np.concatenate appears to take a tuple, you could use:
lc = locals()
C = np.concatenate(tuple(lc[var] for var in list3), 1)

Why don't you store directly the variables instead of their names ?
Like :
list3 = [a, b, c, a]
C = np.concatenate(list3, axis=1)
Or you can use eval() (which don't seems to be recommanded most of the time) :
list3 = ['a', 'b', 'c','a']
CC = np.concatenate([eval(i) for i in list3], axis=1)

Related

List comprehension using f-strings

I have three variables
a = 1
b = 2
c = 3
and I want to have a string like 'a=1, b=2, c=3'
so, I use f-string,
x = ''
for i in [a, b, c]:
x += f"{i=}"
but it gives,
x
'i=1, i=2, i=3, '
how do I make the i to be a, b, and c?
The list [a, b, c] is indistiguishable from the list [1, 2, 3] -- the variables themselves are not placed in the list, their values are, so there is no way to get the variable names out of the list after you've created it.
If you want the strings a, b, c, you need to iterate over those strings, not the results of evaluating those variables:
>>> ', '.join(f"i={i}" for i in "abc")
'i=a, i=b, i=c'
If you want to get the values held by the variables with those names, you can do this by looking them up in the globals() dict:
>>> a, b, c = 1, 2, 3
>>> ', '.join(f"{var}={globals()[var]}" for var in "abc")
'a=1, b=2, c=3'
but code that involves looking things up in globals() is going to be really annoying to debug the first time something goes wrong with it. Any time you have a collection of named values that you want to iterate over, it's better to just put those values in their own dict instead of making them individual variables:
>>> d = dict(a=1, b=2, c=3)
>>> ', '.join(f"{var}={val}" for var, val in d.items())
'a=1, b=2, c=3'
A list doesn't remember the names of variables assigned to it. For that, you need a dictionary.
x = ""
my_dict = {'a': a, 'b': b, 'c': c}
for k, v in my_dict.items():
x += f"{k}={v}, "
Using gloabls is not always a good idea if you want a solution that aovid using them you can inspect the variables that are declare using inspect module, there is thread regarding the getting the name of varibles here, from were the I took the function.
import inspect
def retrieve_var(var):
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
return [var_name for var_name, var_val in callers_local_vars if var_val is var]
and now you can use a loop as similar to that you where using
a, b, c = 1, 2, 3
x = ''
for i in [a, b, c]:
var = retrieve_var(i)
x += f"{var[0]}={i}, "

How to split a string into multiple variables & if not enough values, assign the variable as a particular value?

I have this code (Python 3.9):
vdam = {"mirror": 0, "door": 1, "windShield": 2}
vdam2 = list(vdam.items())
vdam3 = [a[0] for a in vdam2]
vdam4 = ' '.join([str(ele) for ele in vdam3])
a, b, c, d, e, f, g = vdam4.split()
I want to split the string into multiple variables but at the same time, if not enough values to be split, all other left variables get assigned as a particular value.
The above code generates the error like this:
ValueError: not enough values to unpack (expected 7, got 3)
Append default values to the list to make it contain the number of elements equal to the number of variables. Then assign them.
vdam5 = vdam4.split()
if len(vdam5) < 7:
vdam5 += [None] * (7 - len(vdam5))
a, b, c, d, e, f, g = vdam5
Here is a suggestion (updated based on a comment from Blckknght)
def fit(mystring, expected_length, replacement_value):
while len(mystring)<expected_length:
mystring +=replacement_value
return mystring
vdam = {"mirror": 0, "door": 1, "windShield": 2}
vdam2 = list(vdam.items())
vdam3 = [a[0] for a in vdam2]
vdam4 = ' '.join([str(ele) for ele in vdam3])
a, b, c, d, e, f, g = fit(vdam4.split(),7,'A')
mirror door windShield A A A A
vdam2-vdam4 are not needed, nor the .split(). List the keys and append enough defaults to handle 0-7 arguments. The final *_ captures the excess items:
>>> vdam = {"mirror": 0, "door": 1, "windShield": 2}
>>> a,b,c,d,e,f,g,*_ = list(vdam.keys()) + [None]*7
>>> a,b,c,d,e,f,g
('mirror', 'door', 'windShield', None, None, None, None)
If you have varying defaults, this works by expanding the keys as parameters to the function:
>>> vdam = {"mirror": 0, "door": 1, "windShield": 2}
>>> a,b,c,d,e,f,g,*_ = list(vdam.keys()) + [None]*7
>>> a,b,c,d,e,f,g
('mirror', 'door', 'windShield', None, None, None, None)
>>> def func(a=1,b=2,c=3,d=4,e=5,f=6,g=7):
... return a,b,c,d,e,f,g
...
>>> a,b,c,d,e,f,g = func(*vdam.keys())
>>> print(a,b,c,d,e,f,g)
mirror door windShield 4 5 6 7
Note you could also use func(*vdam4.split()) as well if you have a space-delimited string.

Finding the minimum value for different variables

If i am doing some math functions for different variables for example:
a = x - y
b = x**2 - y**2
c = (x-y)**2
d = x + y
How can i find the minimum value out of all the variables. For example:
a = 4
b = 7
c = 3
d = 10
So the minimum value is 3 for c. How can i let my program do this.
What have i thought so far:
make a list
append a,b,c,d in the list
sort the list
print list[0] as it will be the smallest value.
The problem is if i append a,b,c,d to a list i have to do something like:
lst.append((a,b,c,d))
This makes the list to be -
[(4,7,3,10)]
making all the values relating to one index only ( lst[0] )
If possible is there any substitute to do this or any way possible as to how can i find the minimum!
LNG - PYTHON
Thank you
You can find the index of the smallest item like this
>>> L = [4,7,3,10]
>>> min(range(len(L)), key=L.__getitem__)
2
Now you know the index, you can get the actual item too. eg: L[2]
Another way which finds the answer in the form(index, item)
>>> min(enumerate(L), key=lambda x:x[1])
(2, 3)
I think you may be going the wrong way to solving your problem, but it's possible to pull values of variable from the local namespace if you know their names. eg.
>>> a = 4
>>> b = 7
>>> c = 3
>>> d = 10
>>> min(enumerate(['a', 'b', 'c', 'd']), key=lambda x, ns=locals(): ns[x[1]])
(2, 'c')
a better way is to use a dict, so you are not filling your working namespace with these "junk" variables
>>> D = {}
>>> D['a'] = 4
>>> D['b'] = 7
>>> D['c'] = 3
>>> D['d'] = 10
>>> min(D, key=D.get)
'c'
>>> min(D.items(), key=lambda x:x[1])
('c', 3)
You can see that when the correct data structure is used, the amount of code required is much less.
If you store the numbers in an list you can use a reduce having a O(n) complexity due the list is not sorted.
numbers = [999, 1111, 222, -1111]
minimum = reduce(lambda mn, candidate: candidate if candidate < mn else mn, numbers[1:], numbers[0])
pack as dictionary, find min value and then find keys that have matching values (possibly more than one minimum)
D = dict(a = 4, b = 7, c = 3, d = 10)
min_val = min(D.values())
for k,v in D.items():
if v == min_val: print(k)
The buiit-in function min will do the trick. In your example, min(a,b,c,d) will yield 3.

How to get two arrays out of one csv file in Python?

I've a csv file containing lines like this:
A,x1
A,x2
A,x3
B,x4
B,x5
B,x6
The first part reflects the group (A or B) a value (x1, x2, ...) belongs to.
What I want to do now is importing that csv file in Python, so I have two lists in the end:
ListA = [x1, x2, x3]
ListB = [x4, x5, x6]
Can someone help me out with that?
Thanks in advance :)
import sys
file_path = "path_to_your_csv"
stream_in = open(file_path, 'rb')
A = [];
B = [];
for line in stream_in.readlines():
add_to_List = line.split(",")[1].strip()
if 'A' in line:
A.append(add_to_List);
if 'B' in line:
B.append(add_to_List)
stream_in.close()
print A
print B
after putting your data in a pandas Series object names ser, just type in ser.loc("A")
and ser.loc("B") to get the data slice you want.
Using preassigned names for your vectors lead to lots of duplicated logic, that gets more and more complicated if you add new vectors to your data description...
It's much better to use dictionaries
data=[['a', 12.3], ['a', 12.4], ['b', 0.4], ['c', 1.2]]
vectors = {} # an empty dictionary
for key, value in data:
vectors.setdefault(key,[]).append(value)
The relevant docs, from the python official documentation
setdefault(key[, default])
If key is in the dictionary, return its value.
If not, insert key with a value of default and return default.
default defaults to None.
append(x)
appends x to the end of the sequence (same as s[len(s):len(s)] = [x])
You could try:
In[1]: import pandas as pd
In[2]: df = pd.read_csv(file_name, header=None)
In[3]: print(df)
out[3]:
0 1
0 A x1
1 A x2
2 A x3
3 B x4
4 B x5
In[4]: ListA = df[0].tolist()
In[5]: print(ListB)
Out[5]: ['A', 'A', 'A', 'B', 'B', 'B']
In[6]: ListB = t_df[1].tolist()
In[7]: print(ListB)
Out[7]: ['x1', 'x2', 'x3', 'x4', 'x5', 'x6']

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