python list with fixed number of elements - python

I want to create a fixed size list of size 6 of tuples in Python. Note that in the code below i am always re-initializing the values in the outer for loop in order to reset the previously created list which was already added to globalList. Here is a snippet:
for i,j in dictCaseId.iteritems():
listItems=[None]*6
for x,y in j:
if x=='cond':
tuppo = y
listItems.insert(0,tuppo)
if x=='act':
tuppo = y
listItems.insert(1,tuppo)
if x=='correc':
tuppo = y
listItems.insert(2,tuppo)
...
...
globalList.append(listItems)
But when I try to run the above (snippet only shown above) it increases the list size. I mean, stuff gets added but I also see the list contains more number of elements. I dont want my list size to increase and my list is a list of 6 tuples.
For example:
Initially: [None,None,None,None,None,None]
What I desire: [Mark,None,Jon,None,None,None]
What I get: [Mark,None,Jon,None,None,None,None,None]

Instead of inserting you should assign those values. list.insert inserts a new element at the index passed to it, so length of list increases by 1 after each insert operation.
On the other hand assignment modifies the value at a particular index, so length remains constant.
for i,j in dictCaseId.iteritems():
listItems=[None]*6
for x,y in j:
if x=='cond':
tuppo = y
listItems[0]=tuppo
if x=='act':
tuppo = y
listItems[1]=tuppo
if x=='correc':
tuppo = y
listItems[2]=tuppo
Example:
>>> lis = [None]*6
>>> lis[1] = "foo" #change the 2nd element
>>> lis[4] = "bar" #change the fifth element
>>> lis
[None, 'foo', None, None, 'bar', None]
Update:
>>> lis = [[] for _ in xrange(6)] # don't use [[]]*6
>>> lis[1].append("foo")
>>> lis[4].append("bar")
>>> lis[1].append("py")
>>> lis
[[], ['foo', 'py'], [], [], ['bar'], []]

Ashwini fixes your main issue, however I will make a suggestion here.
Because (from what you have shown us) you are simply assigning an element to a specific index based on a condition, it would be better to do something like this:
for i, j in dictCaseId.iteritems():
listItems = [None] * 6
lst = ['cond', 'act', 'correc', ...]
for x, y in j:
idx = lst.index(x)
listItems[idx] = y
globalList.append(listItems)
Or with a list of lists:
for i, j in dictCaseId.iteritems():
listItems = [[] for _ in xrange(6)]
lst = ['cond', 'act', 'correc', ...]
for x, y in j:
idx = lst.index(x)
listItems[idx].append(y)
globalList.append(listItems)
This allows each of the conditions to be dealt with in one go, and it condenses your code significantly.

Instead of listItems.insert(0, tuppo), do listItems[0] = tuppo etc.

thats becauce you insert the names indestead of changing the value.
do like this indstead
for i,j in dictCaseId.iteritems():
listItems=[None]*6
for x,y in j:
if x=='cond':
tuppo = y
listItems[0] = tuppo
if x=='act':
tuppo = y
listItems[1] = tuppo
if x=='correc':
tuppo = y
listItems[2] = tuppo ...
...
globalList.append(listItems)

Related

Differences between declaring a list & appending and list()

This below appends the s to the list l
s = pd.Series([1], name='foo')
l = []
l.append(s)
This only appends 1 to l
s = pd.Series([1], name='foo')
l = list(s)
How to implement the first script the best way without declaring a list and then appending?
[x] makes a list with x as an element.
list(x) makes a list produced by iterating over x. x has to be iterable, otherwise you'll get an error.
It is, in effect, [i for i in x], or
alist = []
for i in x:
alist.append(i)

Delete first two characters of elements in a list of lists

Let's say I have the following list of lists:
final_list = [[1,'pppizza',3,4],[1,'mmmonkey',9,10],[1,'dddoublerainbow',8,2]]
Now I need to remove the first 2 characters of the second element of every list, so the result will be:
final_list = [[1,'pizza',3,4],[1,'monkey',9,10],[1,'doublerainbow',8,2]]
No need to rebuild the entire list from scratch just to do it in a one-line comprehension while unnecessarily iterating the inner lists. Just modify the elements that need modifying:
for lst in final_list:
lst[1] = lst[1][2:]
use double list comprehensions:
final_list = [[x if hasattr(x, '__len__') and i == 1 else x[2:] for i, x in enumerate(y)] for y in my_list]
this will trim the first 2 elements of the second element, even if the element is not a string
If you want it for strings only then the statement becomes:
final_list = [[x if type(x) == str and i == 1 else x[2:] for i, x in enumerate(y)] for y in my_list]
final_list = [[x[2:] if i==1 else x for i, x in enumerate(y)] for y in my_list]
Full answer:
final_list = []
for y in my_list:
l = []
for i, x in enumerate(my_list):
if i==1: #2nd element in the list
l.append(x[2:]) # Append a string starting from the 3rd letter to the end
else:
l.append(x) . # Just append the element at x
my_list.append(l) # Append the list l to my_list
print(my_list)

Finding indices of items from a list in another list even if they repeat

This answer works very well for finding indices of items from a list in another list, but the problem with it is, it only gives them once. However, I would like my list of indices to have the same length as the searched for list.
Here is an example:
thelist = ['A','B','C','D','E'] # the list whose indices I want
Mylist = ['B','C','B','E'] # my list of values that I am searching in the other list
ilist = [i for i, x in enumerate(thelist) if any(thing in x for thing in Mylist)]
With this solution, ilist = [1,2,4] but what I want is ilist = [1,2,1,4] so that len(ilist) = len(Mylist). It leaves out the index that has already been found, but if my items repeat in the list, it will not give me the duplicates.
thelist = ['A','B','C','D','E']
Mylist = ['B','C','B','E']
ilist = [thelist.index(x) for x in Mylist]
print(ilist) # [1, 2, 1, 4]
Basically, "for each element of Mylist, get its position in thelist."
This assumes that every element in Mylist exists in thelist. If the element occurs in thelist more than once, it takes the first location.
UPDATE
For substrings:
thelist = ['A','boB','C','D','E']
Mylist = ['B','C','B','E']
ilist = [next(i for i, y in enumerate(thelist) if x in y) for x in Mylist]
print(ilist) # [1, 2, 1, 4]
UPDATE 2
Here's a version that does substrings in the other direction using the example in the comments below:
thelist = ['A','B','C','D','E']
Mylist = ['Boo','Cup','Bee','Eerr','Cool','Aah']
ilist = [next(i for i, y in enumerate(thelist) if y in x) for x in Mylist]
print(ilist) # [1, 2, 1, 4, 2, 0]
Below code would work
ilist = [ theList.index(i) for i in MyList ]
Make a reverse lookup from strings to indices:
string_indices = {c: i for i, c in enumerate(thelist)}
ilist = [string_indices[c] for c in Mylist]
This avoids the quadratic behaviour of repeated .index() lookups.
If you data can be implicitly converted to ndarray, as your example implies, you could use numpy_indexed (disclaimer: I am its author), to perform this kind of operation in an efficient (fully vectorized and NlogN) manner.
import numpy_indexed as npi
ilist = npi.indices(thelist, Mylist)
npi.indices is essentially the array-generalization of list.index. Also, it has a kwarg to give you control over how to deal with missing values and such.

Selection elements of a list based on another 'True'/'False' list

I have two lists of the same length.
The first one contains strings. The second one - strings that can be either 'True' or 'False'.
If the nth element of the second list is 'True', I want to append the nth element of the first list to another list.
So if I have:
List1:
('sth1','sth2','sth3','sth4')
List2:
('True','False','True','False')
The outcome should be List3:
('sth1','sth3').
How can I intersect two list in that way?
Use zip:
result = [x for x, y in zip(xs, ys) if y == 'True']
Example:
xs = ('sth1','sth2','sth3','sth4')
ys = ('True','False','True','False')
result = [x for x, y in zip(xs, ys) if y == 'True']
result
['sth1', 'sth3']
Or use numpy:
import numpy as np
filtered = np.array(List1)[np.array(List2)]
Btw this only works if the elements inside List2 are True/False, not if they are "True"/"False".
If you didn't know about zip :
l1 = ('sth1','sth2','sth3','sth4')
l2 = ('True','False','True','False')
l = [x for i,x in enumerate(l1) if l2[i]=='True']
print l
#=> ['sth1', 'sth3']
It would be shorter with a tuple of booleans :
l1 = ('sth1','sth2','sth3','sth4')
l2 = (True,False,True,False)
l = [x for i,x in enumerate(l1) if l2[i]]
print l
Simplest way is to use itertools.compress method as follows.
import itertools
list1 = ('sth1','sth2','sth3','sth4')
list2 = ('True','False','True','False')
list2 = map(lambda x: x == 'True', list2)
result = list(itertools.compress(list1, list2))
compress method returns an iterator, so you that is why you need to wrap the iterator object in list
I hope it helps.

Removing elements corresponding to 'None' in lists

I have two lists
x = [ None , None , "foo" , "bar" ]
y = [ "bar" , "foo" , "foo" ,"bar"]
len(x) == len(y)
I want to check if there exists an element in list x that is None then remove that element and remove corresponding element in y too .
like remove x[0] == None , so remove x[0] and y[0] from x and y
the result should be:
x = ["foo","bar"]
y = ["foo","bar"]
i tried a quite non-pythonic way which gave me a "list index out of range " error:
for i in range(0,len(x)):
if(x[i] == None):
x.remove(x[i])
y.remove(y[i])
Using the zip method you can do this very nicely:
x, y = zip(*[(e_x, e_y) for e_x, e_y in zip(x, y) if e_x is not None])
Here you iterate over both lists at once, creating a new list with tuples containing the elements of x and y. These tuples are only added if the element from x e_x is not None.
The outer zip converts the list of tuples back in two separate lists.
Edit: As Donkey Kong pointed out in a comment, it is better to use is not None instead of != None. I updated the code accordingly.
Let's call your lists xs and ys.
ys = [y for (x, y) in zip(xs, ys) if x]
xs = [x for x in xs if x]
should do the trick.
First go through and figure out what you need to remove:
remove_ix = {ix for ix, val in enumerate(x) if val is None}
Now you can use it to filter x and y:
x = [item for ix, item in enumerate(x) if ix not in remove_ix]
y = [item for ix, item in enumerate(y) if ix not in remove_ix]
Note that with your version, you'll end up skipping indices and probably having IndexErrors because if the initial length of the list is N and you remove a single item, then you'll still be looping N times, but the list is now N-1 items long. Also with list.remove, you can't guarantee that you're removing the correct item if there are duplicates (which there are in your example)
Use zip to remove items that are None (in x) from matching pairs of x and y, then use another zip(*...) to transpose the list of lists back to the references x and y:
x, y = zip(*[[ix, iy] for ix, iy in zip(x, y) if ix is not None])
I think the most performant way to achieve desired output would be dict comprehension. In that case you avoid avoid 2 list comprehension expressions:
x_list = [ None , None , "foo" , "bar" ]
y_list = [ "bar" , "foo" , "foo" ,"bar"]
d = {val:y_list[i] for i,val in enumerate(x_list) if val is not None}
new_l_y = d.values()
new_l_x = d.keys()
NOTE: You would miss order of elements but keep correspondence of items. If order matters you can use OrderedDict.
from collections import OrderedDict
d = OrderedDict((val,y_list[i]) for i,val in enumerate(x_list) if val is not None)
new_l_y = d.keys()
new_l_y = d.values()
Posting an answer in a dead thread. It might help someone.
One very neat method is to use itertool's compress method.
from itertools import compress
x = [None, None, 1, 2]
y = [5, 6, 2, 4]
y = list(compress(y, x))
x = list(compress(x, x))

Categories