Return tuple with biggest increase of second value in a list of tuples - python

like the title says I have a list of tuples: [(3, 20), (9, 21), (18, 19)]. I need to find the tuple that has a positive y-increase wrt its predecessor. In this case 21-20 = 1. So tuple (9,21) should be returned. 19-21 = -1 so tuple (18,19) shouldn't be returned. The very first tuple in the list should never be returned. I've tried putting all the values in a list and then trying to figure it out but I'm clueless. It should work for lists of tuples of any length. I hope you guys can help me out, thanks in advance.

You could compare the second element of each tuple with the previous one, while iterating over the list:
data = [(3, 20), (9, 21), (18, 19), (1, 35), (4, 37), (1, 2)]
maxIncrease = [0, 0] # store max increase value and it's index
for i in range(1, len(data)):
lst = data[i - 1]
cur = data[i]
diff = cur[1] - lst[1]
if diff > maxIncrease[0]:
maxIncrease = [diff, i]
print(
f'Biggest increase of {maxIncrease[0]} found at index {maxIncrease[1]}: {data[maxIncrease[1]]}'
)
Out:
Biggest increase of 16 found at index 3: (1, 35)

I think something like that can solve your problem:
import numpy as np
data = [(3, 20), (9, 21), (18, 19), (10, 22)]
diff_with_previous = []
for i in range(len(data)):
if i == 0:
diff_with_previous.append(-np.inf)
else:
diff_with_previous.append(data[i][1] - data[i-1][1])
indices = np.where(np.array(diff_with_previous) > 0)
print([data[i] for i in indices[0]])
[EDIT]
Without numpy:
data = [(3, 20), (9, 21), (18, 19), (10, 22)]
indices = []
for i in range(1, len(data)):
if (data[i][1] - data[i-1][1]) > 0:
indices.append(i)
print([data[i] for i in indices])

Related

Sort list of tuples based on unique occurrence from beginning to end

Let's say I have a list of tuples like:
x = [(2, 18), (18, 5), (3, 2)]
How can I sort this list of tuples based on the unique occurrence of the values in the tuples?
For example, since the number 3 only occurs in the tuple (3, 2) and is the first value of the tuple, it should be the first entry in the list. This entry is followed by (2, 18) because the second value (2) of (3, 2) occurs in the first value of (2, 18). And finally, the last entry in the list should be (18, 5), since its first value matches the last value of (2, 18).
Expected result:
[(3, 2), (2, 18), (18, 5)]
Pls tell me if you need more info.
Use a recursive function to pick dominos one by one:
def get_elements_filtered_by_first_values(original_list, first_value):
return [each_element for each_element in original_list if each_element[0] == first_value]
def add_next_domino(list_of_remained_dominos, list_of_picked_dominos):
possible_domino_list_to_pick = get_elements_filtered_by_first_values(list_of_remained_dominos, list_of_picked_dominos[-1][1])
if not len(possible_domino_list_to_pick):
return None
for each_possible_domino_to_pick in possible_domino_list_to_pick:
new_list_of_picked_dominos = list_of_picked_dominos + [each_possible_domino_to_pick]
new_list_of_remained_dominos = list_of_remained_dominos[:]
new_list_of_remained_dominos.remove(each_possible_domino_to_pick)
if not len(new_list_of_remained_dominos):
return new_list_of_picked_dominos
pick_domino_result = add_next_domino(new_list_of_remained_dominos, new_list_of_picked_dominos)
if pick_domino_result is not None:
return pick_domino_result
return None
x = [(2, 18), (18, 5), (3, 2)]
eligible_elements = [each_element for each_element in x if each_element[0] not in map(lambda x: x[1], x)]
while len(eligible_elements):
next_eligible_element = eligible_elements.pop()
return_list = add_next_domino([each_element for each_element in x if each_element != next_eligible_element] ,[next_eligible_element])
if return_list is not None:
print(return_list)
break
The output:
[(3, 2), (2, 18), (18, 5)]

list.sort() not sorting values correctly by second tuple parameter

I am trying to sort a list of tuples by the second parameter in the tuple in Python 3.5.2 to find which algorithms take the least -> most time in ascending order, however for some reason the looks to be sorting by random. My code:
import math
def speeds(n):
new_dictionary = {}
six_n_log_n = 6 * n * math.log(n, 2)
thr_n_exp05 = 3 * (n ** 0.5)
four_expn = 4 ** n
ceil_sqrt_n = math.ceil(math.sqrt(n))
five_n = 5 * n
n_cubed = n ** 3
log_log_n = math.log(math.log(n, 2))
n_exp_01 = n ** 0.01
floor_2_n_log_exp2_n = math.floor(2 * n * (math.log(n, 2)**2))
n_exp2_log_n = (n ** 2) * math.log(n, 2)
log_exp2_n = math.log(n, 2) ** 2
one_div_n = 1 / n
two_exp_n = 2 ** n
four_exp_logn = 4 ** (math.log(n, 2))
two_exp_logn = 2 ** (math.log(n, 2))
four_n_exp_threehalves = 4 * (n ** (3/2))
n_exp2 = n ** 2
sqrt_log_n = math.sqrt(math.log(n, 2))
new_dictionary[0] = six_n_log_n
new_dictionary[1] = thr_n_exp05
new_dictionary[2] = four_expn
new_dictionary[3] = ceil_sqrt_n
new_dictionary[4] = five_n
new_dictionary[5] = n_cubed
new_dictionary[6] = log_log_n
new_dictionary[7] = n_exp_01
new_dictionary[8] = floor_2_n_log_exp2_n
new_dictionary[9] = n_exp2_log_n
new_dictionary[10] = log_exp2_n
new_dictionary[11] = one_div_n
new_dictionary[12] = two_exp_n
new_dictionary[13] = four_exp_logn
new_dictionary[14] = two_exp_logn
new_dictionary[15] = four_n_exp_threehalves
new_dictionary[16] = n_exp2
new_dictionary[17] = sqrt_log_n
sorted_list = []
for key in new_dictionary:
sorted_list.append((key, new_dictionary[key]))
sorted_list.sort(key=lambda x: x[1])
for i, x in sorted_list:
print(sorted_list[i])
return sorted_list
n = 15
speeds(n)
The expected output should be tuples in ascending order by the second parameter, but instead, I receive this:
(15, 232.379000772445)
(10, 15.263794126054286)
(14, 15.000000000000002)
(2, 1073741824)
(17, 1.9765855902562173)
(7, 1.027450511266727)
(9, 879.0503840119167)
(13, 225.00000000000006)
(3, 4)
(12, 32768)
(8, 457)
(5, 3375)
(11, 0.06666666666666667)
(4, 75)
(16, 225)
(1, 11.618950038622252)
(0, 351.6201536047667)
(6, 1.3627418135330593)
Can anyone tell me why I'm getting a seemingly random order from this? Can't seem to find where my problem is.
If you examine sorted_list following the sort, you will see that it has been sorted correctly.
[(11, 0), (7, 1.027450511266727), (6, 1.3627418135330593), (17, 1.9765855902562173), (3, 4.0), (1, 11.618950038622252), (14, 15.000000000000002), (10, 15.263794126054286), (15, 60), (4, 75), (16, 225), (13, 225.00000000000006), (0, 351.6201536047667), (8, 457.0), (9, 879.0503840119167), (5, 3375), (12, 32768), (2, 1073741824)]
The error occurs in the following line:
for i, x in sorted_list:
You are not iterating over the keys and values as you think. Rather, this is unpacking each tuple in the list and assigning its first component to i and its second component to x. You are then accessing the element at the ith position in the list, which leads to what appears to be a random ordering. You can instead write:
for i, x in enumerate(sorted_list):
Or more simply, you can print the tuple you are trying to display
for item in sorted_list:
print(item)
When you iterate over your tuples, you want to print the tuple itself:
for tup in sorted_list:
print(tup)
otherwise, you are printing the values at the index based on the first value of the index. For example, the first value in the sorted list is:
(11, 0)
is actually looking for:
sorted_list[11]
which is why you see the improper first value.

How to generate list of tuples relating records

I need to generate a list from the list of tuples:
a = [(1,2), (1,3), (2,3), (2,5), (2,6), (3,4), (3,6), (4,7), (5 6), (5,9), (5,10), (6,7)
(6.10) (6.11) (7.8) (7.12) (8.12) (9.10) (10.11)]
The rule is:
- I have a record from any (begin = random.choice (a))
- Items from the new list must have the following relationship:
the last item of each tuple in the list must be equal to the first item of the next tuple to be inserted.
Example of a valid output (starting by the tuple (3.1)):
[(3, 1), (1, 2), (2, 3), (3, 4), (4, 7), (7, 8), (8, 12), (12, 7), (7, 6), (6, 2), (2, 5), (5, 6), (6, 10), (10, 5) (5, 9), (9, 10), (10, 11), (11, 6), (6, 3)]
How can I do this? Its make using list comprehensions?
Thanks!
Here, lisb will be populated with tuples in the order that you seek. This is, of course, if lisa provides appropriate tuples (ie, each tuple has a 1th value matching another tuple's 0th value). Your sample list will not work, regardless of the implementation, because all the values don't match up (for example, there is no 0th element with 12, so that tuple can't be connected forward to any other tuple)...so you should come up with a better sample list.
Tested, working.
import random
lisa = [(1, 2), (3, 4), (2, 3), (4, 0), (0, 9), (9, 1)]
lisb = []
current = random.choice(lisa)
while True:
lisa.remove(current)
lisb.append(current)
current = next((y for y in lisa if y[0] == current[1]), None)
if current == None:
break
print lisb
If you don't want to delete items from lisa, just slice a new list.
As a generator function:
def chained_tuples(x):
oldlist = x[::]
item = random.choice(oldlist)
oldlist.remove(item)
yield item
while oldlist:
item = next(next_item for next_item in oldlist if next_item[0] == item[1])
oldlist.remove(item)
yield item
As noted, you'll get an incomplete response if your list isn't actually chainable all the way through, like your example list.
Just to add another way of solving this problem:
import random
from collections import defaultdict
lisa = [(1, 2), (3, 4), (2, 3), (4, 0), (0, 9), (9, 1)]
current_start, current_end = lisa[random.randint(0, len(lisa) - 1)]
starts = defaultdict(list)
lisb = [(current_start, current_end)]
for start, end in lisa:
starts[start].append(end)
while True:
if not starts[current_end]:
break
current_start, current_end = current_end, starts[current_end].pop()
lisb.append((current_start, current_end))
Note: You have to make sure lisa is not empty.
I think all of the answers so far are missing the requirement (at least based on your example output) that the longest chain be found.
My suggested solution is to recursively parse all possible chains that can be constructed, and return the longest result. The function looks like this:
def generateTuples(list, offset, value = None):
if value == None: value = list[offset]
list = list[:offset]+list[offset+1:]
res = []
for i,(a,b) in enumerate(list):
if value[1] in (a,b):
if value[1] == a:
subres = generateTuples(list, i, (a,b))
else:
subres = generateTuples(list, i, (b,a))
if len(subres) > len(res):
res = subres
return [value] + res
And you would call it like this:
results = generateTuples(a, 1, (3,1))
Producing the list:
[(3, 1), (1, 2), (2, 3), (3, 4), (4, 7), (7, 8), (8, 12), (12, 7), (7, 6),
(6, 2), (2, 5), (5, 6), (6, 10), (10, 5), (5, 9), (9, 10), (10, 11),
(11, 6), (6, 3)]
The first parameter of the function is the source list of tuples, the second parameter is the offset of the first element to use, the third parameter is optional, but allows you to override the value of the first element. The latter is useful when you want to start with a tuple in its reversed order as you have done in your example.

Collapse a list of range tuples into the overlapping ranges

I'm looking for the most memory efficient way to solve this problem.
I have a list of tuples representing partial string matches in a sentence:
[(0, 2), (1, 2), (0, 4), (2,6), (23, 2), (22, 6), (26, 2), (26, 2), (26, 2)]
The first value of each tuple is the start position for the match, the second value is the length.
The idea is to collapse the list so that only the longest continue string match is reported. In this case it would be:
[(0,4), (2,6), (22,6)]
I do not want just the longest range, like in algorithm to find longest non-overlapping sequences, but I want all the ranges collapsed by the longest.
In case your wondering, I am using a pure python implementation of the Aho-Corasick for matching terms in a static dictionary to the given text snippet.
EDIT: Due to the nature of these tuple lists, overlapping but not self-contained ranges should be printed out individually. For example, having the words betaz and zeta in the dictionary, the matches for betazeta are [(0,5),(4,8)]. Since these ranges overlap, but none is contained in the other, the answer should be [(0,5),(4,8)]. I have also modified the input dataset above so that this case is covered.
Thanks!
import operator
lst = [(0, 2), (1, 2), (0, 4), (2,6), (23, 2), (22, 6), (26, 2), (26, 2), (26, 2)]
lst.sort(key=operator.itemgetter(1))
for i in reversed(xrange(len(lst)-1)):
start, length = lst[i]
for j in xrange(i+1, len(lst)):
lstart, llength = lst[j]
if start >= lstart and start + length <= lstart + llength:
del lst[i]
break
print lst
#[(0, 4), (2, 6), (22, 6)]
a = [(0, 2), (1, 2), (0, 4), (23, 2), (22, 6), (26, 2), (26, 2), (26, 2)]
b = [set(xrange(i, i + j)) for i, j in a]
c = b.pop().union(*b)
collapsed = sorted(c)
print collapsed
#Maybe this is useful?:
[0, 1, 2, 3, 22, 23, 24, 25, 26, 27]
#But if you want the requested format, then do this:
d = []
start = collapsed[0]
length = 0
for val in collapsed:
if start + length < val:
d.append((start,length))
start = val
length = 0
elif val == collapsed[-1]:
d.append((start,length + 1))
length += 1
print d
#Output:
[(0,4), (22,6)]
So, taking you at your word that your main interest is space efficiency, here's one way to do what you want:
lst = [(0, 2), (1, 2), (0, 4), (23, 2), (22, 6), (26, 2), (26, 2), (26, 2)]
lst.sort()
start, length = lst.pop(0)
i = 0
while i < len(lst):
x, l = lst[i]
if start + length < x:
lst[i] = (start, length)
i += 1
start, length = x, l
else:
length = max(length, x + l - start)
lst.pop(i)
lst.append((start, length))
This modifies the list in place, never makes the list longer, only uses a small handful of variables to keep state, and only needs one pass through the list
A much faster algorithm is possible if you don't want to modify the list in place - popping items from the middle of a list can be slow, especially if the list is long.
One reasonable optimization would be to keep a list of which indices you're going to remove, and then come back and rebuild the list in a second pass, that way you could rebuild the whole list in one go and avoid the pop overhead. But that would use more memory!

Python multiple list comprehensions containing ranges of tuples in one list

I need a list like this:
[(16,2), (14,3), (15,3), (16,3), (18,3), (19,3), (12,4), (13,4), (14,4)]
But much, much longer. There are certain really large range patterns in this list, but also irregularities. So it would be unfeasible to write down all tuples, but I can't make a simple listcomp either.
I wanted to use:
[(16,2), (x,3) for x in range(14,19), (x,4) for x in range(12,14)]
But according to the docs, [x, y for ...] is not allowed, and my example is interpreted as an unparenthesed tuple of 2 parenthesed tuples, instead of a tuple followed by a list comprehension.
Any solutions?
Try this:
[(16, 2)] + [(x,3) for x in range(14,19)] + [(x,4) for x in range(12,14)]
From your question is not clear if you're trying to increase the second index at every new range.
If that's the case you could put all the ranges in a list and use itertools.count():
from itertools import count
indexes = [(16,17), (14, 20), (12, 15)]
[(x, n) for i,n in zip(indexes,count(2)) for x in range(*i)]
Which give exactly:
[(16, 2), (14, 3), (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (12, 4), (13, 4), (14, 4)]
You could create separate lists and then append them to each other.
a = [(16, 2)]
b = [(x, 3) for x in range(14, 19)]
c = [(x, 4) for x in range(12, 15)]
a.extend(b)
a.extend(c)

Categories