Related
I am trying to figure out the following problem. I have a list with integers.
list = [1, 2, 3, 5, 6, 9, 10]
The goal is to find the longest sub-list within the list. The sub-list is defined by having the difference between two integers not being more than 1 (or -1). In this example, the longest sub-list respecting this condition is:
lista = [1, 2, 3, 5, 6, 9, 10]
difference = []
i = 0
for number in range(len(lista)-1):
diff = lista[i]-lista[i+1]
difference.append(diff)
i += 1
print(difference)
winner = 0
ehdokas = 0
for a in difference:
if a == 1 or a == -1:
ehdokas += 1
else:
if ehdokas > winner:
winner = ehdokas
ehdokas = 0
if ehdokas > winner:
winner = ehdokas
print(winner)
Now, the "print(winner)" will print "2" whereas I wish that it would print "3" since the first three integers are "adjacent" to each other (1-2 = -1 , 2-3 = -1)
Basically I am trying to iterate through the list and calculate the difference between the adjacent integers and the calculate the consecutive number of "1" and "-1" in the "difference" list.
This code works sometimes, depending on the list fed through, sometimes it doesn't. Any improvement proposals would be highly appreciated.
Given:
lista = [1, 2, 3, 5, 6, 9, 10]
You can construct a new list of tuples that have the index and difference in the tuple:
diffs=[(i,f"{lista[i]}-{lista[i+1]}={lista[i]-lista[i+1]}",lista[i]-lista[i+1])
for i in range(len(lista)-1)]
>>> m
[(0, '1-2=-1', -1), (1, '2-3=-1', -1), (2, '3-5=-2', -2), (3, '5-6=-1', -1), (4, '6-9=-3', -3), (5, '9-10=-1', -1)]
Given a list like that, you can use groupby, max to find the longest length of the sub lists that satisfy that condition:
from itertools import groupby
lista = [1, 2, 3, 5, 6, 9, 10]
m=max((list(v) for k,v in groupby(
((i,lista[i]-lista[i+1]) for i in range(len(lista)-1)),
key=lambda t: t[1] in (-1,0,1)) if k),key=len)
>>> m
[(0, -1), (1, -1)]
A nice and simple solution based on more_itertools:
#!pip install more_itertools
l = [1, 2, 3, 5, 6, 9, 10]
import more_itertools as mit
sublists = []
for group in mit.consecutive_groups(l):
sublists.append(list(group))
max(sublists, key=len)
This outputs:
[1,2,3]
Which is the longest sublist of consecutive numbers.
Here's a solution without the use of libraries:
l = [1, 2, 3, 4, 10, 11, 20, 19, 18, 17, 16, 30, 29, 28, 27, 40, 41]
r = []
if len(l) > 1:
t = [l[0]]
for i in range(0, len(l)-1):
if abs(l[i]-l[i+1]) == 1:
t.append(l[i+1])
if len(t) > len(r):
r = t.copy()
else:
t.clear()
t.append(l[i+1])
print(r)
Which will print:
[20, 19, 18, 17, 16]
You can get differences between items and their predecessor with zip(). This will allow you to generate a list of break positions (the indexes of items that cannot be combined with their predecessor). Using zip on these breaking positions will allow you to get the start and end indexes of subsets of the list that form groups of consecutive "compatible" items. The difference between start and end is the size of the corresponding group.
L = [1, 2, 3, 5, 6, 9, 10]
breaks = [i for i,(a,b) in enumerate(zip(L,L[1:]),1) if abs(a-b)>1 ]
winner = max( e-s for s,e in zip([0]+breaks,breaks+[len(L)]) )
print(winner) # 3
If you want to see how the items are grouped, you can use the start/end indexes to get the subsets:
[ L[s:e] for s,e in zip([0]+breaks,breaks+[len(L)]) ]
[[1, 2, 3], [5, 6], [9, 10]]
How do I compare two lists side by side and where the value in List 1 doesn't match the value in List 2, will output that number from List 1? For example:
List1 = [2, 3, 4, 10, 8, 24]
List2 = [2, 9, 4, 23, 8, 24]
Output: [3, 10]
Use List comprehension with zip.
final_lst = [x for x, y in zip(list1, list2) if x!=y]
print(final_lst)
Best solution is to do with zip.
List1 = [2, 3, 4, 10, 8, 24]
List2 = [2, 9, 4, 23, 8, 24]
List2=[x for x, y in zip(List1, List2) if x!=y]
However, a traditional solution
List1 = [2, 3, 4, 10, 8, 24]
List2 = [2, 9, 4, 23, 8, 24]
List3=[]
for j,k in enumerate (List1):
if List1[j]!=List2[j]:
List3.append(k)
Since several people have already suggested a list comprehension, here is a solution without list comprehension, using map and filter:
output = list(map(lambda pair: pair[0], filter(lambda pair: pair[0] != pair[1], zip(lst1, lst2))))
# [3, 10]
is there a compact oneliner or python idiom to handle the following task?
I want to transform a list of list of tuples like this:
input = [[(1,2,3),(4,5,6)],[(7,8,9),(10,11,12)]]
to this:
output [[1,2,3,7,8,9], [4,5,6,10,11,12]]
Using map and flattening the list only gave me the follwing
input_trans = map(list, zip(*input))
input_trans_flat = [item for sublist in input_trans for item in sublist]
Out: [(1, 2, 3), (7, 8, 9), (4, 5, 6), (10, 11, 12)]
Many Thanks in Advance!
I'd do:
output = [list(a + b) for a, b in zip(*input)]
The zip part, as you already know, transposes the outer list of lists. Then I grab each pair of tuples and concatenate them, then turn the combined tuple into a list. If you don't care if you have a list of lists or a list of tuples in the end, you could get rid of the list call.
You should be able to generalise Blckknght's answer to any number of tuples inside a list, using sum.
output = [list(sum(x, ())) for x in zip(*input)]
print(output)
[[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]
You can use list comprehension with zip() like below:
output = [[item for tup in (i, j) for item in tup] for i, j in zip(*input)]
Output:
>>> input = [[(1, 2, 3), (4, 5, 6)], [(7, 8, 9), (10, 11, 12)]]
>>>
>>> output = [[item for tup in (i, j) for item in tup] for i, j in zip(*input)]
>>> output
[[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]
Here's one way.
from itertools import chain
l = [[(1,2,3),(4,5,6)],[(7,8,9),(10,11,12)]]
[list(chain.from_iterable(s)) for s in l]
gives me
[[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
This is my list
list1 = [1,2,3,4,5,6,7,8,9,10,11,12]
i tried to do this
it = iter(list1)
chunked_lists = zip(it,it,it)
chunked_lists is now = [(1,2,3),(4,5,6),(7,8,9),(10,11,12)]
Now i'd like to add a 'data' to every chunk so that it would look like
[(1,2,3,'data'),(4,5,6,'data')....etc]
but tuples are immutable and i'd have to destroy them, append and create them again.
is there a better way to accomplish this in python?
it = iter(list1)
from itertools import repeat
chunked_lists = zip(it,it,it,repeat("data"))
[(1, 2, 3, 'data'), (4, 5, 6, 'data'), (7, 8, 9, 'data'), (10, 11, 12, 'data')]
You can use itertools.repeat to create a new iterator contain the data and then apply the zip function on your prior iterators and new repeated iterator :
>>> it = iter(list1)
>>> it2=repeat('data',len(list1))
>>> chunked_lists = zip(it,it,it,it2)
>>> chunked_lists
[(1, 2, 3, 'data'), (4, 5, 6, 'data'), (7, 8, 9, 'data'), (10, 11, 12, 'data')]
>>>
Note that as #Padraic Cunningham mentioned on his answer there is no need to call the len function in itertools.repeat.So you can simply use repeat('data')
If you want to modify the list of tuples after creation you can use a list comprehension to modify your tuples by adding the new elements to the tuples.
Example :
>>> import numpy as np
>>> l2=np.random.randint(0,100,len(list1)/3)
>>> it = iter(list1)
>>> chunked_lists = zip(it,it,it)
>>> chunked_lists = [i+(j,) for i,j in zip(chunked_lists,l2)]
>>> l2
array([35, 22, 35, 95])
>>> chunked_lists
[(1, 2, 3, 35), (4, 5, 6, 22), (7, 8, 9, 35), (10, 11, 12, 95)]
>>>
This should be a linear time way to do it:
list1 = [1,2,3,4,5,6,7,8,9,10,11,12]
result = [tuple(list1[x:x+3]+["data"]) for x in range(0, len(list1), 3)]
If I am not ignoring some Python intricacy, since tuple() has complexity O(n), the slice accessor has complexity O(k) and the overhead caused by the +["data"] isn't greater than simply appending a node at the end of a list (which is O(1)), it should be O((len(list1) / k) * k ^ 2) aka O(n k), where k is fixed to 3 in your case.
result = [tuple(list1[x:x+3]+["data"]) for x in range(0, len(list1), 3)]
means:
[list1[x:x+3]+["data"] for x in (0, 3, ... 12)]
aka:
[
tuple(list1[0:3]+["data"]),
tuple(list1[4:6]+["data"]),
...
tuple(list1[9:12]+["data"])
]
It is also well behaved with odd lists:
>>> list1 = [1,2,3,4,5,6,7,8,9,10,11]
>>> print [tuple(list1[x:x+3]+["data"]) for x in range(0, len(list1), 3)]
[(1, 2, 3, 'data'), (4, 5, 6, 'data'), (7, 8, 9, 'data'), (10, 11, 'data')]
Since tuples are immutable, if you're adding and deleting from them often, you're perhaps better off using lists of lists:
list1 = [1,2,3,4,5,6,7,8,9,10,11,12]
it = iter(list1)
chunked_lists = [list(a) for a in zip(it,it,it)]
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
for l in chunked_lists:
l.append('data')
# => [[1, 2, 3, 'data'], [4, 5, 6, 'data'], [7, 8, 9, 'data'], [10, 11, 12, 'data']]
How about adding them to the zip function when you are creating the chunked_lists itself -
>>> list1 = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> it = iter(list1)
>>> chunked_lists = zip(it,it,it,['data']*(len(list1)/3))
>>> chunked_lists
[(1, 2, 3, 'data'), (4, 5, 6, 'data'), (7, 8, 9, 'data'), (10, 11, 12, 'data')]
For Python 3.x , you would need to divide the len(list1) using // operator (for it to return int) . (And most probably wrap zip in list() , since zip in Python 3.x , returns an iterator.)
I got this list:
input = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
I want to make new lists with each index item:
i.e.
output = [[1,5,9],[2,6,10],[3,7,11],[4,8,12]]
This is a canonical example of when to use zip:
In [6]: inlist = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
In [7]: out=zip(*inlist)
In [8]: out
Out[8]: [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
Or, to get a list of lists (rather than list of tuples):
In [9]: out=[list(group) for group in zip(*inlist)]
In [10]: out
Out[10]: [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Use zip():
input = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
output = zip(*input)
This will give you a list of tuples. To get a list of lists, use
output = map(list, zip(*input))