Forming an array from items in list of lists - python

I am trying to create an array from data in a list of lists.
ac_name = 'ac'
dat = [['ab=55', 'ac=25', 'db =57', 'dc =44'],
['ab=75','ac =12', 'cg =11', 'pt =95'],
['ab=17', 'ac=62'],
['ab=97', 'aa=501', 'dc=12', 'dd=19']]
So I want to get a list that looks like this
ac = ['ac=25','ac=12','ac=62','']
and from this get
ac_values = [25,12,62,'']
All in all I want to convert dat into one large array.
I know this doesnt work because it is going through every item, so the output is however many elements there are in dat.
ac = []
for d in dat:
for c in d:
if ac_name in c:
ac.append(c)
else:
ac.append('')

As I mentioned in comment, your else block is inside the nested loop which means that for all the items in each list if the condition is not executed you'll have an empty string. You can use a flag to see whether the if block is executed in nested loop and append an empty string to the final result.
In [6]: ac = []
...: for d in dat:
...: flag = True
...: for c in d:
...: if ac_name in c:
...: ac.append(c)
...: flag = False
...: if flag:
...: ac.append('')
...:
In [7]: ac
Out[7]: ['ac=25', 'ac =12', 'ac=62', '']
But since this is not a much Pythonic way for dealing with problem, instead you can use generator expressions and next() function as following to create a dictionary out of expected result. In this case you can easily access keys or values as well.
In [19]: result = dict((ind, next((i for i in d if i.startswith(ac_name)), '=').split('=')[1]) for ind, d in enumerate(dat))
In [20]: result
Out[20]: {0: '25', 1: '12', 2: '62', 3: ''}
In [21]: result.keys() # shows number of sub-lists in your original list
Out[21]: dict_keys([0, 1, 2, 3])
In [22]: result.values()
Out[22]: dict_values(['25', '12', '62', ''])

ac_name = 'ac'
datas = [['ab=55', 'ac=25', 'db =57', 'dc =44'],
['ab=75','ac =12', 'cg =11', 'pt =95'],
['ab=17', 'ac=62'],
['ab=97', 'aa=501', 'dc=12', 'dd=19'],
['ab=55', 'ac=25', 'db =57', 'dc =44'],
['ab=75','ac =12', 'cg =11', 'pt =95'],
['ab=17', 'ac=62'],
['ab=97', 'aa=501', 'dc=12', 'dd=19']]
lst = []
for i,data in enumerate(datas):
for d in data:
if ac_name in d:
lst.append(d.split('=')[-1])
if i == len(lst):
lst.append('')
print(lst)
Output
['25', '12', '62', '', '25', '12', '62', '']

You can use itertools.chain to flatten your list of lists. Then use a list comprehension to filter and split elements as required.
from itertools import chain
res = [int(i.split('=')[-1]) for i in chain.from_iterable(dat) \
if i.startswith('ac')]
print(res)
[25, 12, 62]

There are many ways to do this as folks have shown. Here is one way using list comprehension and higher order functions:
In [14]: ["" if not kv else kv[0].split('=')[-1].strip() for kv in [filter(lambda x: x.startswith(ac_name), xs) for xs in datas]]
Out[14]: ['25', '12', '62', '']
If an exact key "ac" is desired, can use regular expressions too:
import re
p = re.compile(ac_name + '\s*')
["" if not kv else kv[0].split('=')[-1].strip() for kv in [filter(lambda x: p.match(x), xs) for xs in datas]]

After some puzzling, I found a possible solution
Process each element in each sublist individually: if it contains 'ac', then strip the 'ac=' part. If not, just return an empty string ''.
Then concatenate all elements in each sublist using string.join(). This will return a list of strings with either the number string, e.g. '25', or an empty string.
Finally, conditionally convert each string to integer if possible. Else just return the (empty) string.
ac = [int(cell_string) if cell_string.isdigit() else cell_string for cell_string in
[''.join([cell.split('=')[1] if ac_name in cell else '' for cell in row]) for row in data]]
Output:
[25, 12, 62, '']
edit:
If you want to extend it to multiple column names, e.g.:
col_name = ['ac', 'dc']
Then just extend this:
cols = [[int(cell_string) if cell_string.isdigit() else cell_string for cell_string in
[''.join([cell.split('=')[1] if name in cell else '' for cell in row]) for row in data]] for name in col_name]
Output:
[[25, 12, 62, ''], [44, '', '', 12]]

Try this:
ac_name = 'ac'
ac = []
ac_values = []
for value in dat:
found = False
for item in value:
if ac_name in item:
ac.append(item)
ac_values.append(item.split('=')[-1])
found = True
if not found:
ac.append(' ')
ac_values.append(' ')
print(ac)
print(ac_values)
Output:
['ac= 25', 'ac = 12', 'ac=62', ' ']
[' 25', ' 12', '62', ' ']

This will work for any length of ac_name:
ac_name = 'ac'
ac = []
ac_values=[]
for i in dat:
found=False
for j in i:
if j[:2]==ac_name:
ac.append(j)
ac_values.append(int(j[len(ac_name)+2:]))
found=True
if not found:
ac.append("")
ac_values.append("")
print(ac)
print(ac_values)

Related

Find matching strings between dictionary and list and replace matches with string

I have the following dictionary and list. My dictionary contains values that are lists.
d={'Ro06_244736': ['A/A/A/A', 'R37', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', '35', 'A/A/A/A']}
l=['R37', '35']
I am trying to find matching values between my list and my dictionary list's values, and if they match replace my dictionary list's values with 'N/A'. Matches can be at any element. I am wanting the following output.
d2={'Ro06_244736': ['A/A/A/A', 'N/A', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', 'N/A', 'A/A/A/A']}
I have tried the following, but it didn't work :
for a,b in d.items():
#print(a,b)
for n, i in enumerate(b):
#print(n,i)
for j in l:
#print(j)
if i == j:
print(i,j)
b[n] = "N/A"
print(b)
What is the best way to do this?
The following code replaces anywhere in the list
In [9]: d={'Ro06_244736': ['A/A/A/A', 'R37', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', '35', 'A/A/A/A']}
...:
...: l=['R37', '35']
...:
In [10]: for k,v in d.items():
...: for index in range(len(v)):
...: if v[index] in l: d[k][index] = "N/A"
...:
In [11]: d
Out[11]:
{'Ro06_244736': ['A/A/A/A', 'N/A', 'C/C/C/C'],
'Ro06_244742': ['G/G/G/G', 'N/A', 'A/A/A/A']}
I would like to do an easy list comprehension,use a set(would be faster,the original list is also okay) to save the element in the list:
Assume:
d = {'Ro06_244736': ['A/A/A/A', 'R37', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', '35', 'A/A/A/A']}
l = {'R37', '35'}
One line:
d2 = {key: [item if item not in l else 'N/A' for item in values] for key, values in d.items()}
Result:
{'Ro06_244736': ['A/A/A/A', 'N/A', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', 'N/A', 'A/A/A/A']}
I believe it's because you're not actually editing the original list, rather you're editing the copy produced by d.items().
Try assigning copy back into the dictionary after you have edited it.
for a,b in d.items():
...
b[n] = "N/A"
...
d[a] = b
d={'Ro06_244736': ['A/A/A/A', 'R37', 'C/C/C/C'], 'Ro06_244742': ['G/G/G/G', '35', 'A/A/A/A']}
l=['R37', '35']
for x,y in d.items():
for i in y:
if(i in l):
y[(y.index(i))]="N/A"
print(d)

Replacing elements in list with values from dictionary

I want to replace strings in a list with values from dictionary. However, for some logical reason which is not logical to me (obviously), length of list changes after replacement.
genre_list = ['action, drama, thriller', 'crime, romance, adventure']
list_new = []
categories_ids = {'action': '18',
'drama': '13',
'thriller': '11',
'romance': '1',
'adventure': '8',
'crime': '3'
}
print(len(genre_list)) # length before
for z in genre_list:
for a, b in categories_ids.items():
if a in z:
list_temp = z.replace(z, b)
list_new.append(list_temp)
print(len(list_new)) # length after
What am I missing here? Thanks in advance.
You append to list_new each element from categories that appears in each element of genre_list - the first 3 keys appears in the first element of genre_list and the 3 other keys appears in the second element of genre_list - so in list_new will be 6 elements in total.
Try instead:
genre_list = ['action, drama, thriller', 'crime, romance, adventure']
list_new = []
categories_ids = {'action': '18',
'drama': '13',
'thriller': '11',
'romance': '1',
'adventure': '8',
'crime': '3'
}
for z in genre_list:
for a, b in categories_ids.items():
z = z.replace(a, b)
list_new.append(z) # here is the difference - one append per element in genre_list
print(list_new) # output:['18, 13, 11', '3, 1, 8']
Use:
def func(s):
return ", ".join(categories_ids[w] for w in s.split(", "))
list_new = list(map(func, genre_list))
print(list_new)
This prints:
['18, 13, 11', '3, 1, 8']
you are adding new elements to your new_list if a key from your dict is in one string from genre_list but the genre_list has in one string multiple keys from your dict so you end to have multiple strings/elements in your new_list
you can use a regular expression with list comprehension:
import re
genre_list = ['action, drama, thriller', 'crime, romance, adventure']
pattern = '|'.join(categories_ids)
def replace(gr):
return categories_ids[gr.group()]
list_new = [re.sub(pattern, replace, t) for t in genre_list]
# ['18, 13, 11', '3, 1, 8']
Each list element contains more than 1 keys which is reason you end up with more elements in the new list. This can be handled as given the code below.
for z in genre_list:
key_words=''
for key in z.split(','):
if key.strip() in categories_ids:
key_words += categories_ids[key.strip()] +','
list_new.append(key_words[:-1])
Now both the lists will have same length as given below.
2 ['action, drama, thriller', 'crime, romance, adventure']
2 ['18,13,11', '3,1,8']

How filter a list for elements that can be converted from strings to floats without raising ValueError?

Say I have the following list
w = ['Elapsed', 'time:', '0', 'days', '8', 'hours', '22', 'minutes', '15.9', 'seconds.']
I want to filter through this list and only return the elements that can be converted to floats without raising ValueError. I could do
l = []
for el in w:
try:
l.append(float(el))
except ValueError:
continue
But I was wondering if there is a robust way of using the filter and lambda functions to achieve the same thing in one line? Or perhpas there's another way?
filter way
l = list(map(float, filter(lambda x: x.replace('.', '', 1).lstrip('+-').isdecimal(), w)))
list comprehension way
l = [float(x) for x in w if x.replace('.', '', 1).lstrip('+-').isdecimal()]
isfloat function
def isfloat(x: str) -> bool:
x = x.replace('.', '', 1) # strip first '.', so we can use isdecimal
x = x.lstrip('+-') # strip sign(prosible)
return x.isdecimal()
the different between str.isdigit, isnumeric and isdecimal

display a list values as a sequence of string

I have a list like
L=[[w,['0','0']],[r,['1']]]
I want to print it as
w = 00
r = 1
for getting this I tried like
for k in range(1,len(tab[0]))
print L[0][k][0],"=",(L[0][k][1])
but it prints like
w = ['0','0']
r = ['1']
can anyone help me? thanx in advance
In [5]: L=[['w',['0','0']],['r',['1']]]
In [6]: L1 = [item for item in L]
In [7]: L1
Out[7]: [['w', ['0', '0']], ['r', ['1']]]
In [8]: ['%s=%s' % (item[0], ''.join(item[1])) for item in L1]
Out[8]: ['w=00', 'r=1']
If the question is how do I convert ['0', '0'] to 00 then you may use join
for k in range(1,len(tab[0]))
print L[0][k][0],"=", ''.join((L[0][k][1]))
I don't fully understand the for loop (because I don't know what tab is) but to get string representation of a list, use join
Your list is wrong. Unless w and r are predefined variables, it should read as:
L=[['w',['0','0']],['r',['1']]]
In that case, the following will achieve the result:
for item in L:
print(item[0], ''.join(item[1]))
Just use a simple for-loop with string formatting:
L = [['w', ['0','0']], ['r',['1']]]
for item in L:
print '{} = {}'.format(item[0], ''.join(item[1]))
What ''.join() does it joins every item in the list separated by the '' (i.e nothing). So:
['0', '0'] --> '00'
Go with this..
L=[[w,['0','0']],[r,['1']]]
for item in L:
print item[0], '=', ''.join(item[1])
Ans:
w = 00
r = 1

How to convert list of intable strings to int

In Python, I want to convert a list of strings:
l = ['sam','1','dad','21']
and convert the integers to integer types like this:
t = ['sam',1,'dad',21]
I tried:
t = [map(int, x) for x in l]
but is showing an error.
How could I convert all intable strings in a list to int, leaving other elements as strings?
My list might be multi-dimensional. A method which works for a generic list would be preferable:
l=[['aa','2'],['bb','3']]
I'd use a custom function:
def try_int(x):
try:
return int(x)
except ValueError:
return x
Example:
>>> [try_int(x) for x in ['sam', '1', 'dad', '21']]
['sam', 1, 'dad', 21]
Edit: If you need to apply the above to a list of lists, why didn't you converted those strings to int while building the nested list?
Anyway, if you need to, it's just a matter of choice on how to iterate over such nested list and apply the method above.
One way for doing that, might be:
>>> list_of_lists = [['aa', '2'], ['bb', '3']]
>>> [[try_int(x) for x in lst] for lst in list_of_lists]
[['aa', 2], ['bb', 3]]
You can obviusly reassign that to list_of_lists:
>>> list_of_lists = [[try_int(x) for x in lst] for lst in list_of_lists]
How about using map and lambda
>>> map(lambda x:int(x) if x.isdigit() else x,['sam','1','dad','21'])
['sam', 1, 'dad', 21]
or with List comprehension
>>> [int(x) if x.isdigit() else x for x in ['sam','1','dad','21']]
['sam', 1, 'dad', 21]
>>>
As mentioned in the comment, as isdigit may not capture negative numbers, here is a refined condition to handle it notable a string is a number if its alphanumeric and not a alphabet :-)
>>> [int(x) if x.isalnum() and not x.isalpha() else x for x in ['sam','1','dad','21']]
['sam', 1, 'dad', 21]
I would create a generator to do it:
def intify(lst):
for i in lst:
try:
i = int(i)
except ValueError:
pass
yield i
lst = ['sam','1','dad','21']
intified_list = list(intify(lst))
# or if you want to modify an existing list
# lst[:] = intify(lst)
If you want this to work on a list of lists, just:
new_list_of_lists = map(list, map(intify, list_of_lists))
For multidimenson lists, use recursive technique may help.
from collections import Iterable
def intify(maybeLst):
try:
return int(maybeLst)
except:
if isinstance(maybeLst, Iterable) and not isinstance(lst, str):
return [intify(i) for i in maybeLst] # here we call intify itself!
else:
return maybeLst
maybeLst = [[['sam', 2],'1'],['dad','21']]
print intify(maybeLst)
Use isdigit() to check each character in the string to see if it is a digit.
Example:
mylist = ['foo', '3', 'bar', '9']
t = [ int(item) if item.isdigit() else item for item in mylist ]
print(t)
Use a list comprehension to validate the numeracy of each list item.
str.isnumeric won't pass a negative sign
Use str.lstrip to remove the -, check .isnumeric, and convert to int if it is.
Alternatively, use str.isdigit in place of .isnumeric.
Keep all values in the list
l = ['sam', '1', 'dad', '21', '-10']
t = [int(v) if v.lstrip('-').isnumeric() else v for v in l]
print(t)
>>> ['sam', 1, 'dad', 21, -10]
Remove non-numeric values
l = ['sam', '1', 'dad', '21', '-10']
t = [int(v) for v in t if v.lstrip('-').isnumeric()]
print(t)
>>> [1, 21, -10]
Nested list
l = [['aa', '2'], ['bb', '3'], ['sam', '1', 'dad', '21', '-10']]
t = [[int(v) if v.lstrip('-').isnumeric() else v for v in x] for x in l]
print(t)
>>> [['aa', 2], ['bb', 3], ['sam', 1, 'dad', 21, -10]]

Categories