Sorting list of tuples (Python) - python

I am new in python and for practice reasons, I am trying to solve the following task:
Given a list of tuples
A = [(17, 8), (17, 12), (7, 2), (9, 15), (9, 17), (1, 4), (3, 9), (12, 14)]
My goal is: to sort in (n log n) time in descending order the list according to the first element of the set and if the first element of two sets are the same, sorting in descending order according to the second element. --> x <= x', y <= y'
So, I want to get the result:
A = [(17, 12), (17, 8), (12, 14), (9, 17), (9, 15), (7, 2), (3, 9), (1, 4)]
I have tried using the following code:
A.sort(reverse = True, key=lambda x: x[0] )
But it just sorts according to the first element and I do know if it is in n log n time.
Could you please help me with that?
Thank you!

To sort based on both values, remove the key function:
>>> A.sort(reverse = True)
>>> A
[(17, 12), (17, 8), (12, 14), (9, 17), (9, 15), (7, 2), (3, 9), (1, 4)]
And yes, Python sorts in O(n log n), for more, check out Timsort on Wikipedia, the algorithm Python uses for sorting.

Python's inbuilt sorted() method with a comparator function(Similar to C++) will work
sorted(tup, key = lambda x: x[0])

Tuples are naturally sorted by the first element, then the second:
A.sort(reverse = True)
Output as requested.

There you go:
A = [(17, 8), (17, 12), (7, 2), (9, 15), (9, 17), (1, 4), (3, 9), (12, 14)]
reverse_sorted = []
for item in reversed(sorted(A)):
reverse_sorted.append(item)
print(reverse_sorted)

As to add to the other's excellent answers. Search on Google with "python list sort time complexity" will return for you what you seek regarding python's sort time complexities (that others have already written above).
In addition, if you need to move away from using python's inbuilt sort function. There are many different sorting algorithms created over the years that you can recreate that have the desired time complexities. It would be a good "training" exercise if you're new to programming in general.

Related

Is there a difference between these two expressions in python

So, basically I just started learning python and I can't find something that proves if these two instructions do the same thing or not
I have two lists and I want to copy one into another, the thing is that I do not understood if this instruction is right
newList = [x[:] for x in List]
because newList = List does the same thing
Is there a difference between these two instructions?
Thanks!
In the first cursory look they look essentially same but there could be a difference if the List is not a list of lists [as is immediately understood by its name] ..if List is actually a tuple of tuples (supposing a naming mistake .. :-)) or something else. In such a case you are creating a list of tuples (by the first command) from what was initially something else. Other such possibilities also exist. So please see the print and type of NewList and List carefully. A subtle difference and they are not same.
You might be actually doing a type conversion if you are following the first command.
Consider the code below.
List = tuple( (x,x+1) for x in range(10))
newList = [x[:] for x in List]
The output of print and type reveal the difference instantly.
print(List)
print(newList)
type(List)
type(newList)
respective Outputs
((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10))
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10)]
<class 'tuple'>
<class 'list'>

Storing python list of tuples in a redis sorted set where second element of tuple is used as score

I have a Python list of tuples like so:
lst = [(22, -150.0), (23, -150.0), (18, -148.5), (15, -99.4), (5, -75.75), (4, -49.2), (13, -49.0), (9, -41.3), (20, -25.5), (17, -22.3), (10, -13.1), (16, -12.5), (14, -9.8), (3, -8.5), (1, -8.4), (12, -1.5), (7, -0.6), (2, -0.4), (6, 1.7), (21, 2.7)]
How do I pass that into a redis sorted set, such that the second value in each tuple is used as the score? I tried zadd(sorted_set, *lst), but I'm getting the error value is not a valid float.
The zadd method of redis.StrictRedis expects arguments in the form of
zadd(key, score1, name1, score2, name2, ...)
In other words, you need to reverse and unpack the inner tuples as well:
In [6]: r = redis.StrictRedis(...)
In [8]: r.zadd('sset', *(y for x in lst for y in x[::-1]))
Out[8]: 20
Redis.zadd and StrictRedis.zadd behave differently -- see README for more details. So if you use redis.Redis instead of redis.StrictRedis (you shouldn't), do not reverse tuples:
r.zadd('sset', *(y for x in lst for y in x))

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.

Sort dict of tuples by either tuple value with customised comparator

I'm working on some python dicts of tuples. Each tuple containing 2 ints. The first digit in the tuple is reffered to as value and the second digit is referred to as work. I have 3 different comparators and I need to sort the dicts into descending order. This order should be determined by which comparator is called. i.e. the dict can be sorted 3 different ways. I've tried as many different ways as I could find to get this to work. I can do it without using the comparator by just breaking it up into a list and sorting by slicing the tuples but if anyone can shed some light on the syntax to sort using the comparators it would be greatly appreciated. Mine seems to be returning correctly for cmpWork but the other 2 aren't reversed.
Also it would be great if I could get the dict sorted by the tuple values.
I got a sort working with
sortedSubjects = sorted(tmpSubjects.iteritems(), key = operator.itemgetter(1), reverse = True)
but this doesn't let me slice the tuples.
First time posting noob so apologies for any mistakes.
def cmpValue(subInfo1, subInfo2):
return cmp(subInfo2[0] , subInfo1[0])
def cmpWork(subInfo1, subInfo2):
return cmp(subInfo1[1] , subInfo2[1])
def cmpRatio(subInfo1, subInfo2):
return cmp((float(subInfo2[0]) / subInfo2[1]) , (float(subInfo1[0]) / subInfo1[1]))
def greedyAdvisor(subjects, comparator):
tmpSubjects = subjects.copy()
sortedSubjects = sorted(tmpSubjects.values(), comparator, reverse = True)
print sortedSubjects
smallCatalog = {'6.00': (16, 8),'1.00': (7, 7),'6.01': (5, 3),'15.01': (9, 6)}
greedyAdvisor(smallCatalog, cmpRatio)
greedyAdvisor(smallCatalog, cmpValue)
greedyAdvisor(smallCatalog, cmpWork)
[(7, 7), (9, 6), (5, 3), (16, 8)]
[(5, 3), (7, 7), (9, 6), (16, 8)]
[(16, 8), (7, 7), (9, 6), (5, 3)]
ps
The line
sortedSubjects = sorted(tmpSubjects.iteritems(), key = operator.itemgetter(1), reverse = True)
returns
[('6.00', (16, 8)), ('15.01', (9, 6)), ('1.00', (7, 7)), ('6.01', (5, 3))]
which is almost exactly what I'm looking for except that I can't sort by the second value in the tuple and I can't sort by cmpRatio either.
but this doesn't let me slice the tuples
Starting with your example:
sortedSubjects = sorted(tmpSubjects.iteritems(),
key=operator.itemgetter(1),
cmp=comparator, # What about specifying the comparison?
reverse=True)
If you need to sort dictionary - use collections.OrderedDict
E.g., sort by 1st element of tuple
OrderedDict(sorted(smallCatalog.items(), key=lambda e:e[1][0]))
Out[109]: OrderedDict([('6.01', (5, 3)), ('1.00', (7, 7)), ('15.01', (9, 6)), ('6.00', (16, 8))])

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