Take elements from multiple lists - python

Given multiple lists like the ones shown:
a = [1, 2, 3]
b = [5, 6, 7, 8]
c = [9, 0, 1]
d = [2, 3, 4, 5, 6, 7]
...
I want to be able to combine them to take as many elements from the first list as I can before starting to take elements from the second list, so the result would be:
result = [1, 2, 3, 8, 6, 7]
Is there a particularly nice way to write this? I can't think of a really simple one without a for loop. Maybe a list comprehension with a clever zip.

Simple slicing and concatenation:
a + b[len(a):]
Or with more lists:
res = []
for lst in (a, b, c, d):
res += lst[len(res):]
# [1, 2, 3, 8, 6, 7]

With itertools.zip_longest() for Python 3, works on any number of input lists:
>>> from itertools import zip_longest
>>> [next(x for x in t if x is not None) for t in zip_longest(a,b,c,d)]
[1, 2, 3, 8, 6, 7]
The default fill value is None so take the first none None element in each tuple created with the zip_longest call (you can change the defaults and criteria if None is a valid data value)

With functools.reduce:
from functools import reduce
print(list(reduce(lambda a, b: a + b[len(a):], [a, b, c, d])))
This outputs:
[1, 2, 3, 8, 6, 7]

Related

Find the lists that share common values

My question is closely related to the following topic.
I have a number of lists and I want to find the lists that share common values. All lists have the same size. The total number of lists is variable and can increase. Minimum number of lists is 2
a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
c = [9, 10, 11, 1]
The expected output is:
[a, c]
Ideally, I also want the fastest method. Thanks in advance,
You could cast them to sets and use the intersection() function, if it does return a value, there are some values in common
If you want the ['a', 'c'] output for n named lists, you could save them in a dict and use any to check if they intersect while looping through them:
lists = {
"a" : [1, 2, 3, 4],
"b" : [5, 6, 7, 8],
"c" : [9, 10, 11, 1]
}
res = []
for l in lists:
for l2 in lists:
if l is not l2:
if any(i in lists[l] for i in lists[l2]):
res.append(l)
break;
print(res)
OUT: ['a', 'c']
lists = []
for i in a:
if i in b:
lists.append([a, b])
if i in c:
lists.append([a, c])
for i in b:
if i in c:
lists.append([b, c])
print(lists)
Using my limited knowledge on Python lists, I came up with this:
class countedList:
def __init__(self,listt):
self.listt = listt
self.sharedNum = 0
def mostCommon(*lists):
for item in lists:
for listItem in item.listt:
for item2 in lists:
item2.sharedNum+=item2.listt.count(listItem)
new = sorted(lists,key=lambda clist: clist.sharedNum,reverse=True)
return new[:2]
test = mostCommon(countedList([1, 2, 3, 4]),countedList([5, 6, 7, 8]),countedList([9, 10, 11, 1]))
Well yeah, I had to make up a custom class for it. In the test run it gave:
>>> test[0].listt
[1, 2, 3, 4]
>>> test[1].listt
[9, 10, 11, 1]
To simply check if two lists share at least one value you can use...
a = [1, 2, 3, 4]
b = [9, 10, 11, 1]
if any(a) == any(b):
print(True)

How do I get the index of the common integer element from two separate lists and plug it to another list?

I have 3 lists.
A_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Q_act = [2, 3]
dur = [0, 4, 5, 2, 1, 3, 4, 8, 2, 3]
All lists are integers.
What I am trying to do is to compare Q_act with A_set then obtain the indices of the numbers that match from A_set.
(Example:
Q_act has the elements [2,3]
it is located in indices [1,2] from A_set)
Afterwards, I will use those indices to obtain the corresponding value in dur and store this in a list called p_dur_Q_act.
(Example: using the result from the previous example, [1,2]
The values in the dur list corresponding to the indices [1,2] should be stored in another list called p_dur_Q_act
i.e. [4,5] should be the values stored in the list p_dur_Q_act)
So, how do I get the index of the common integer element (which is [1,2]) from two separate lists and plug it to another list?
So far here are the code(s) I used:
This one, I wrote because it returns the index. But not [4,5].
p_Q = set(Q_act).intersection(A_set)
p_dur_Q_act = [i + 1 for i, x in enumerate(p_Q)]
print(p_dur_Q_act)
I also tried this but I receive an error TypeError: argument of type 'int' is not iterable
p_dur_Q_act = [i + 1 for i, x in enumerate(Q_act) if any(elem in x for elem in A_set)]
print(p_dur_Q_act)
Another option is to use the enumerate iterator to generate every index, and then select only the ones you want:
a_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
q_act = [2, 3]
dur = [0, 4, 5, 2, 1, 3, 4, 8, 2, 3]
p_dur_q_act = [i for i,v in enumerate(a_set) if v in q_act]
print([dur[p] for p in p_dur_q_act if p in dur]) # [4, 5]
This is more efficient than repeatedly calling index if the number of matches is large, because the number of calls is proportional to the number of matches, but the duration of calls is proportional to the length of a_set. The enumerate approach can be made even more efficient by turning q_act into a set, since in scales better with sets than lists. At these scales, though, there will be no observable difference.
You don't need to map these to index values, though. You can get the same result if you use zip to map a_set to dur and then select the d values whose a values are in q_act.
a_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
q_act = {2, 3}
dur = [0, 4, 5, 2, 1, 3, 4, 8, 2, 3]
p_dur_q_act = [d for a, d in zip(a_set, dur) if a in q_act]
Use index function to get the index of the element in the list.
>>> a_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> q_act = [2, 3]
>>> dur = [0, 4, 5, 2, 1, 3, 4, 8, 2, 3]
>>>
>>> print([dur[a_set.index(q)] for q in set(a_set).intersection(q_act)])
[4, 5]

Better way to swap elements in a list?

I have a bunch of lists that look like this one:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to swap elements as follows:
final_l = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
The size of the lists may vary, but they will always contain an even number of elements.
I'm fairly new to Python and am currently doing it like this:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
final_l = []
for i in range(0, len(l)/2):
final_l.append(l[2*i+1])
final_l.append(l[2*i])
I know this isn't really Pythonic and would like to use something more efficient. Maybe a list comprehension?
No need for complicated logic, simply rearrange the list with slicing and step:
In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [2]: l[::2], l[1::2] = l[1::2], l[::2]
In [3]: l
Out[3]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
 TLDR;
Edited with explanation
I believe most viewers are already familiar with list slicing and multiple assignment. In case you don't I will try my best to explain what's going on (hope I do not make it worse).
To understand list slicing, here already has an excellent answer and explanation of list slice notation.
Simply put:
a[start:end] # items start through end-1
a[start:] # items start through the rest of the array
a[:end] # items from the beginning through end-1
a[:] # a copy of the whole array
There is also the step value, which can be used with any of the above:
a[start:end:step] # start through not past end, by step
Let's look at OP's requirements:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # list l
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
0 1 2 3 4 5 6 7 8 9 # respective index of the elements
l[0] l[2] l[4] l[6] l[8] # first tier : start=0, step=2
l[1] l[3] l[5] l[7] l[9] # second tier: start=1, step=2
-----------------------------------------------------------------------
l[1] l[3] l[5] l[7] l[9]
l[0] l[2] l[4] l[6] l[8] # desired output
First tier will be: l[::2] = [1, 3, 5, 7, 9]
Second tier will be: l[1::2] = [2, 4, 6, 8, 10]
As we want to re-assign first = second & second = first, we can use multiple assignment, and update the original list in place:
first , second = second , first
that is:
l[::2], l[1::2] = l[1::2], l[::2]
As a side note, to get a new list but not altering original l, we can assign a new list from l, and perform above, that is:
n = l[:] # assign n as a copy of l (without [:], n still points to l)
n[::2], n[1::2] = n[1::2], n[::2]
Hopefully I do not confuse any of you with this added explanation. If it does, please help update mine and make it better :-)
Here a single list comprehension that does the trick:
In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [2]: [l[i^1] for i in range(len(l))]
Out[2]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
The key to understanding it is the following demonstration of how it permutes the list indices:
In [3]: [i^1 for i in range(10)]
Out[3]: [1, 0, 3, 2, 5, 4, 7, 6, 9, 8]
The ^ is the exclusive or operator. All that i^1 does is flip the least-significant bit of i, effectively swapping 0 with 1, 2 with 3 and so on.
You can use the pairwise iteration and chaining to flatten the list:
>>> from itertools import chain
>>>
>>> list(chain(*zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
Or, you can use the itertools.chain.from_iterable() to avoid the extra unpacking:
>>> list(chain.from_iterable(zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
A benchmark between top answers:
Python 2.7:
('inp1 ->', 15.302665948867798) # NPE's answer
('inp2a ->', 10.626379013061523) # alecxe's answer with chain
('inp2b ->', 9.739919185638428) # alecxe's answer with chain.from_iterable
('inp3 ->', 2.6654279232025146) # Anzel's answer
Python 3.4:
inp1 -> 7.913498195000102
inp2a -> 9.680125927000518
inp2b -> 4.728151862000232
inp3 -> 3.1804273489997286
If you are curious about the different performances between python 2 and 3, here are the reasons:
As you can see #NPE's answer (inp1) performs very better in python3.4, the reason is that in python3.X range() is a smart object and doesn't preserve all the items between that range in memory like a list.
In many ways the object returned by range() behaves as if it is a list, but in fact it isn’t. It is an object which returns the successive items of the desired sequence when you iterate over it, but it doesn’t really make the list, thus saving space.
And that's why in python 3 it doesn't return a list while you slice the range object.
# python2.7
>>> range(10)[2:5]
[2, 3, 4]
# python 3.X
>>> range(10)[2:5]
range(2, 5)
The second significant change is performance accretion of the third approach (inp3). As you can see the difference between it and the last solution has decreased to ~2sec (from ~7sec). The reason is because of the zip() function which in python3.X it returns an iterator which produces the items on demand. And since the chain.from_iterable() needs to iterate over the items once again it's completely redundant to do it before that too (what that zip does in python 2).
Code:
from timeit import timeit
inp1 = """
[l[i^1] for i in range(len(l))]
"""
inp2a = """
list(chain(*zip(l[1::2], l[0::2])))
"""
inp2b = """
list(chain.from_iterable(zip(l[1::2], l[0::2])))
"""
inp3 = """
l[::2], l[1::2] = l[1::2], l[::2]
"""
lst = list(range(100000))
print('inp1 ->', timeit(stmt=inp1,
number=1000,
setup="l={}".format(lst)))
print('inp2a ->', timeit(stmt=inp2a,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp2b ->', timeit(stmt=inp2b,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp3 ->', timeit(stmt=inp3,
number=1000,
setup="l={}".format(lst)))
One of the possible answer using chain and list comprehension
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain([(l[2*i+1], l[2*i]) for i in range(0, len(l)/2)]))
[(2, 1), (4, 3), (6, 5), (8, 7), (10, 9)]
Another way, create nested lists with pairs reversing their order, then flatten the lists with itertools.chain.from_iterable
>>> from itertools import chain
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain.from_iterable([[l[i+1],l[i]] for i in range(0,(len(l)-1),2)]))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
EDIT: I just applied Kasramvd's benchmark test to my solution and I found this solution is slower than the other top answers, so I wouldn't recommend it for large lists. I still find this quite readable though if performance is not critical.
Another approach with simply re-assigning and slicing technique
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for a in range(0,len(l),2):
l[a:a+2] = l[a-len(l)+1:a-1-len(l):-1]
print l
output
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
For fun, if we interpret "swap" to mean "reverse" in a more general scope, the itertools.chain.from_iterable approach can be used for subsequences of longer lengths.
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def chunk(list_, n):
return (list_[i:i+n] for i in range(0, len(list_), n))
list(chain.from_iterable(reversed(c) for c in chunk(l, 4)))
# [4, 3, 2, 1, 8, 7, 6, 5, 10, 9]
An(other) alternative:
final_l = list() # make an empty list
for i in range(len(l)): # for as many items there are in the original list
if i % 2 == 0: # if the item is even
final_l.append(l[i+1]) # make this item in the new list equal to the next in the original list
else: # else, so when the item is uneven
final_l.append(l[i-1]) # make this item in the new list equal to the previous in the original list
This assumes that the original list has an even number of items. If not, a try-except can be added:
final_l = list()
for i in range(len(l)):
if i % 2 == 0:
try: # try if we can add the next item
final_l.append(l[i+1])
except: # if we can't (because i+1 doesnt exist), add the current item
final_l.append(l[i])
else:
final_l.append(l[i-1])
A way using Numpy
import numpy as np
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = np.array(l)
final_l = list(np.flip(l.reshape(len(l)//2,2), 1).flatten())
New to stack overflow. Please free to leave a comment or feedback on this solution.
swap = [2, 1, 4, 3, 5]
lst = []
for index in range(len(swap)):
if index%2 == 0 and index < len(swap)-1:
swap[index],swap[index+1] = swap[index+1],swap[index]
lst.append(swap[index])
print(lst)
out = [1, 2, 3, 4, 5]
I don't see anything wrong with your implementation at all. But you could perhaps do a simple swap instead.
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
old = l[i]
l[i] = l[i+1]
l[i+1] = old
EDIT
Apparently, Python has a nicer way to do a swap which would make the code like this
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
l[i], l[i+1] = l[i+1], l[i]
newList = [(x[2*i+1], x[2*i]) for i in range(0, len(x)/2)]
Now find a way to unzip the tuples. I won't do all of your homework.
Here a solution based in the modulo operator:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = []
uneven = []
for i,item in enumerate(l):
if i % 2 == 0:
even.append(item)
else:
uneven.append(item)
list(itertools.chain.from_iterable(zip(uneven, even)))

Select unique value in many lists in python

I'd like to select unique value in many lists, but I don't know how to do that:
a = [1,2,3,4,5]
b = [2,3,4,5,6]
c = [5,6,7,8,9]
I'd like to make one new list which is [1,2,3,4,5,6,7,8,9]
I know that using the following could be done, but I am searching quicker way to do that.
for i in (a, b, c):
for j in EachValueInEachList:
NewList.append(j)
list(set(NewList)
By the way, there are thousand of lists in my real program.
Thank you very much.
>>> a = [1,2,3,4,5]
>>> b = [2,3,4,5,6]
>>> c = [5,6,7,8,9]
>>> list(set(a + b + c))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
To avoid creating temporary list, use itertools.chain:
>>> import itertools
>>> list(set(itertools.chain(a, b, c)))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
UPDATE (answer to the comment)
If you have a list of lists, use itertools.chain.from_iterable:
list(set(itertools.chain.from_iterable(a_list_of_lists)))
>>> list(set(a) | set(b) | set(c))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
numpy is best option if long list.
import numpy as np
import time
start_time = time.time()
a = np.array([[1,2,3,4,5], [2,3,4,5,6],[5,6,7,8,9]])
print np.unique(a).tolist()
print time.time() - start_time # Execution time
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
0.000999927520752

Sort list values to get a new order of its index, Python way

Sorry for the vague of my question's title.
My question is, I have a list a = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
I need to get the new order b = [4, 2, 3, 5, 1, 6, 10, 8, 7, 9], where the first element of b is 4 because the 4th element of a 10 is the largest number in a. Similarly, the 2nd element in b is 2 because the second large number in a is its second number 9
So, hopefully you got my question: Sort the list a and get the new order b.
Currently, I get it done by using list.sort with some prepare.
tmp = zip(range(1,11), a)
tmp.sort(key=lambda x:(-x[1],x[0]))
b = [x[0] for x in tmp]
I wonder whether there are better python way to achieve my goal?
Thanks for any suggestions~
I would just use the key argument to sort range(1, len(a) + 1) by using a's values.
sorted(range(1, len(a) + 1), key=lambda i: a[i-1], reverse=True)
That's basically the idea, but you can do:
import operator
tmp = sorted(enumerate(a,1),key=itemgetter(1,0),reverse=True)
b = [x[0] for x in tmp]
#In python2.x, the following are equivalent to the list comprehension.
#b = zip(*tmp)[0]
#b = map(itemgetter(0),tmp)
I think that enumerate is a little cleaner than zip with range and itemgetter is a little cleaner than lambda.
You could use sorted and enumerate:
print [el[0] for el in sorted(enumerate(a, start=1), key=lambda L: L[1], reverse=True)]
# [4, 2, 3, 5, 1, 6, 10, 8, 7, 9]
For completeness an alternative using numpy (should you happen to use it any time in the near future):
np.argsort(a)[::-1] + 1
a = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
b = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
a.sort(reverse = True)
print(a)
print(b)
c = [b.index(y)+1 for y in a ]
print(c)
i have just got this stupid answers...

Categories