Shuffle two python list - python

I am having issue figuring out a way to shuffle two python list.
I have two different lists.
first = [1,2,3]
second = [4,5,6]
I want my final list to be a combination of these two lists but shuffled in a particular way.
combined = [1,4,2,5,3,6]
I can shuffle both the lists and combine them, but the result would be [2,1,3,6,5,4] but what I want is [1,4,2,5,3,6].
The combined list should have one item from the first list, and then the subsequent item from the second list.
The two lists might even be of different lengths.

first = [1,2,3]
second = [4,5,6,7]
def shuffle(f, s):
newlist = []
maxlen = len(f) if len(f) > len(s) else len(s)
for i in range(maxlen):
try:
newlist.append(f[i])
except IndexError:
pass
try:
newlist.append(s[i])
except IndexError:
pass
return newlist
print(shuffle(first, second))

If both lists are always going to be the same length then you could do:
x = [1, 2, 3]
y = [4, 5, 6]
z = []
for i in range(len(x)):# Could be in range of y too as they would be the same length
z.append(x[i])
z.append(y[i])
If they are different lengths then you will have to do things slightly differently and check which one is longer and make that the one you get the length of. The you will need to check each iteration of the loop if you are past the length of the shorter one.

Purely because I want to understand itertools one day:
from itertools import chain, zip_longest, filterfalse
first = [1, 2, 3]
second = [4, 5, 6, 9, 10, 11]
result = filterfalse(
lambda x: x is None,
chain.from_iterable(zip_longest(first, second)))
print(tuple(result))
Enjoy!

For lists of unequal length, use itertools.zip_longest:
from itertools import chain, zip_longest
a = [1, 2, 3]
b = [4, 5, 6, 7]
combined = [x for x in chain(*zip_longest(a, b)) if x is not None]
print(combined) # -> [1, 4, 2, 5, 3, 6, 7]
For Python 2, replace zip_longest with izip_longest.
This is based on pylang's answer on Interleave multiple lists of the same length in Python
Here it is generalized, as a function in a module.
#!/usr/bin/env python3
from itertools import chain, zip_longest
def interleave(*args):
"""
Interleave iterables.
>>> list(interleave([1, 2, 3], [4, 5, 6, 7]))
[1, 4, 2, 5, 3, 6, 7]
>>> ''.join(interleave('abc', 'def', 'ghi'))
'adgbehcfi'
"""
for x in chain(*zip_longest(*args)):
if x is not None:
yield x

Related

how to combine same matching items in a 1d list and make it as 2d list [duplicate]

From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]

How do I create a generator within a generator- Python

If I created a list:
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
and tried to get every value <= 6 by using a generator:
test = next(i for i in a for a in _list if i <= 6)
(btw this doesn't work ↑↑↑↑↑)
How do I iterate through lists within a list using a generator? If this is not possible, what method can I use in place of this?
I have looked here:
python generator of generators?, but could not find an answer...
Using chain from builtin module itertools:
from itertools import chain
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(list(i for i in chain.from_iterable(_list) if i <= 6))
Output:
[1, 2, 3, 4, 5, 6]
What itertools.chain does? According manual pages:
Make an iterator that returns elements from the first iterable until
it is exhausted, then proceeds to the next iterable, until all of the
iterables are exhausted. Used for treating consecutive sequences as a
single sequence.
in that case, you don't need to create a generator of generator. Just create a double for loop in one generator (to flatten the list, then test for the element values):
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
gen = (x for sublist in _list for x in sublist if x <= 6)
this double for comprehension syntax needs some using to, comprehensions are generally written "the other way round", but not in the case of loops, hence your confusion. A more general form would be:
(expression(item) for sublist in input_list for item in sublist if condition(item))
then to get the first matching element:
print(next(gen,None)) # None is printed if there's no matching element
(to get all the elements, of course you have to iterate on gen, next provides the "next" values, like if you're iterating "manually" on your generator)
to nest two fors in the same generator you must reverse the order
test = next(i for a in _list for i in a if i <= 6)
You can combine itertools.takewhile and itertools.chain to create an iterator matching your requirements
>>> from itertools import chain, takewhile
>>> itr = takewhile(lambda i: i<=6, chain(*_list))
>>> print (list(itr))
[1, 2, 3, 4, 5, 6]
>>> t = (i for a in _list for i in a if i <= 6)
>>> next(t)
1
>>> next(t)
2
>>> next(t)
3
>>> next(t)
4
>>> next(t)
5
>>>
Use list to convert the iterator to a list
>>> t = (i for a in _list for i in a if i <= 6)
>>> list(t)
[1, 2, 3, 4, 5, 6]

Summing elements at the beginning with the elements at the end of a list

Given a list of numbers, create a new list of numbers such that the first and last numbers are added and stored as the first number, the second and second-to-last numbers are stored as the second number, and so on
num_list = [1,2,3,4,5,6]
num_list2 = [num_list[-1] + num_list[0], num_list[-2] + num_list[1],
num_list[-3] + num_list[2]]
print(num_list2)
output is [7,7,7]
I got the correct output this way but I am sure this is not an efficient way to do it. Is there a better way? I also am supposed to check for even and odd length of the list and if its an odd number of integers, add the central integer in the original list to the end of the new list but don't know how I would go about doing this
I think this is more efficient, i just simply did a for loop:
num_list2 = []
num_list = [1,2,3,4,5,6]
for i in range(round(len(num_list)/2)):
num_list2.append(num_list[i]+num_list[-(i+1)])
print(num_list2)
Output:
[7, 7, 7]
Let us using reversed
[x + y for x, y in zip(num_list, list(reversed(num_list)))][:len(num_list)//2]
Out[406]: [7, 7, 7]
Here's an inefficient[1], but clear way of doing this:
from itertools import zip_longest # or izip_longest in Python2
lst = [1,2,3,4,5,6]
chop_index = len(lst) // 2 # (or +1, depending on how you want to handle odd sized lists)
lh, rh = lst[:chop_index], lst[:chop_index-1:-1]
print(lh, rh) # To see what's going on in the "chopping"
sums = [x + y for (x,y) in zip_longest(lh, rh, fillvalue=0)]
print(sums)
You could improve it by using islice and reversed iterators, or use index math exclusively.
Output:
lst = [1,2,3,4,5,6] => [7, 7, 7]
lst = [1,2,3,4,5,6,7] => [8, 8, 8, 4]
[1] This makes two copies of the list parts. For long lists this is silly, and you shouldn't use this method. It was mostly written to highlight zip_longest's fillvalue optional argument.
Using itertools.islice on a generator:
from itertools import islice
num_list = [1,2,3,4,5,6]
generator = (x + y for x, y in zip(num_list, num_list[::-1]))
print(list(islice(generator, len(num_list)//2)))
# [7, 7, 7]
You can use the following method, which is compatible with asymmetrical list.
def sum_start_end(list_):
result = [x + y for x, y in zip(list_, list_[::-1])][:len(list_) // 2]
if len(list_) % 2 != 0:
result.append(list_[len(list_) // 2])
return result
so for a symmetric list
>>> num_list = [1, 2, 3, 4, 5, 6]
>>> sum_start_end(num_list)
[7, 7, 7]
and for asymmetric list
>>> num_list = [1, 2, 3, 4, 5, 6, 7]
>>> sum_start_end(num_list)
[8, 8, 8, 4]
It's simpler than you imagine.
Just observe your manual attempt and try to infer from it. We can simply do
x = len(num_list)//2 + len(num_list)%2
for i in range(x):
sumBoth = num_list[i] + num_list[-i-1]
num_list2.append(sumBoth)
or with a simpler one-liner
num_list2 = [ num_list[i] + num_list[-i-1] for i in range(len(num_list)//2+len(num_list)%2)]
This works for even as well as odd lengths because of the len(num_list)%2 at the end in the range.

Python Iterate through list of list to make a new list in index sequence

How would you iterate through a list of lists, such as:
[[1,2,3,4], [5,6], [7,8,9]]
and construct a new list by grabbing the first item of each list, then the second, etc. So the above becomes this:
[1, 5, 7, 2, 6, 8, 3, 9, 4]
You can use a list comprehension along with itertools.izip_longest (or zip_longest in Python 3)
from itertools import izip_longest
a = [[1,2,3,4], [5,6], [7,8,9]]
[i for sublist in izip_longest(*a) for i in sublist if i is not None]
# [1, 5, 7, 2, 6, 8, 3, 9, 4]
As long as you know that None will not appear, you can take advantage of how map() works with no function:
outlist = [y for sub in map(None, *inlist) for y in sub if not y is None]
DRY: itertools has a recipe that sounds right for this task: roundrobin
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
l = [[1,2,3,4], [5,6], [7,8,9]]
print (list(roundrobin(*l)))
#[1, 5, 7, 2, 6, 8, 3, 9, 4]
You can do it manually without using any import if you don't want to.
Keep N different counters where n is the number of lists in your list and increment them whenever you add a new element from that sublists. While adding them you should control that respective sets counter must be the minimum of all other counters if all lists have remaining elements.When you add the last element of list you can increment its counter to something like 9999 to protect our minimum rule.
This will be harder to implement and I do not suggest you to implement in this way but this is also a possibility if you do not want to import anything or want to have a programming challenge for your level.
Solutions that filter out None won't work if None is a value from the sublists that you would like to keep. To fix this, you can do a list comprehension that iterates over the indices of the longest sublist, and only adds sublist items if the index is in range.
a = [[1,2,3,4],[5,6,None],[7,8,9]]
range_longest = range(max(map(len, a)))
[sublist[i] for i in range_longest for sublist in a if i < len(sublist)]
# [1, 5, 7, 2, 6, 8, 3, None, 9, 4]

Track value changes in a repetitive list in Python

I have a list with repeating values as shown below:
x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
This list is generated from a pattern matching regular expression (not shown here). The list is guaranteed to have repeating values (many, many repeats - hundreds, if not thousands), and is never randomly arranged because that's what the regex is matching each time.
What I want is to track the list indices at which the entries change from the previous value. So for the above list x, I want to obtain a change-tracking list [3, 6] indicating that x[3] and x[6] are different from their previous entries in the list.
I managed to do this, but I was wondering if there was a cleaner way. Here's my code:
x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
flag = []
for index, item in enumerate(x):
if index != 0:
if x[index] != x[index-1]:
flag.append(index)
print flag
Output: [3, 6]
Question: Is there a cleaner way to do what I want, in fewer lines of code?
It can be done using a list comprehension, with a range function
>>> x = [1, 1, 1, 2, 2, 2, 3, 3, 3]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]
>>> x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]
You can do something like this using itertools.izip, itertools.tee and a list-comprehension:
from itertools import izip, tee
it1, it2 = tee(x)
next(it2)
print [i for i, (a, b) in enumerate(izip(it1, it2), 1) if a != b]
# [3, 6]
Another alternative using itertools.groupby on enumerate(x). groupby groups similar items together, so all we need is the index of first item of each group except the first one:
from itertools import groupby
from operator import itemgetter
it = (next(g)[0] for k, g in groupby(enumerate(x), itemgetter(1)))
next(it) # drop the first group
print list(it)
# [3, 6]
If NumPy is an option:
>>> import numpy as np
>>> np.where(np.diff(x) != 0)[0] + 1
array([3, 6])
I'm here to add the obligatory answer that contains a list comprehension.
flag = [i+1 for i, value in enumerate(x[1:]) if (x[i] != value)]
instead multi-indexing that has O(n) complexity you can use an iterator to check for the next element in list :
>>> x =[1, 1, 1, 2, 2, 2, 3, 3, 3]
>>> i_x=iter(x[1:])
>>> [i for i,j in enumerate(x[:-1],1) if j!=next(i_x)]
[3, 6]
itertools.izip_longest is what you are looking for:
from itertools import islice, izip_longest
flag = []
leader, trailer = islice(iter(x), 1), iter(x)
for i, (current, previous) in enumerate(izip_longest(leader, trailer)):
# Skip comparing the last entry to nothing
# If None is a valid value use a different sentinel for izip_longest
if leader is None:
continue
if current != previous:
flag.append(i)

Categories