Need clarification on Sort by Frequency of second element in Tuple List program - python

from collections import Counter
test_list = [(6, 5), (2, 7), (2, 5), (8, 7), (9, 8), (3, 7)]
freq_2ndEle=Counter(val for key,val in test_list)
res=sorted(test_list,key=lambda ele:freq_2ndEle[ele[1]],reverse=True)
print(res)
Input : test_list = [(6, 5), (1, 7), (2, 5), (8, 7), (9, 8), (3, 7)]
Output : [(1, 7), (8, 7), (3, 7), (6, 5), (2, 5), (9, 8)]
Explanation : 7 occurs 3 times as 2nd element, hence all tuples with 7, are aligned first.
please clarify how the code is working especially, this part
res=sorted(test_list,key=lambda ele:freq_2ndEle[ele[1]],reverse=True)
I have confusion on ele:freq_2ndEle[ele[1]].

Here is an explanation - in the future, you should try following similar steps, including reading the documentation:
Counter takes an iterable or a map as an argument. In your case, val for key,val in test_list is an iterable. You fetch values from test_list and feed them to Counter.
You don't need the key, val semantics, it is confusing in this context, as it suggests you are looping through a dictionary. Instead, you are looping through a list of tuples so freq_2ndEle=Counter(tp[1] for tp in test_list) is much clearer - here you access the second tuple element, indexed with 1.
Counter gives you number of occurrences of each of the second tuple elements. If you print freq_2ndEle, you will see this:
Counter({7: 3, 5: 2, 8: 1}), which is a pair of how many times each second element appears in the list.
In the last step you're sorting the original list by the frequency of the second element using sorted,
res=sorted(test_list,key=lambda ele:freq_2ndEle[ele[1]],reverse=True)
So you take in test_list as an argument to sort, and then you specify the key by which you want to sort: in your case the key is the the time second tuple element occurred.
freq_2ndEle stores key-value pairs of second second element name:times it ocurred in test_list - it is a dictionary in a way, so you access it as you would access a dictionary, that is - you get the value that corresponds to ele[1] which is the (name) of the second tuple element. Name is not the base term, but I thought it may be clearer. The value you fetch with freq_2ndEle[ele[1]] is exactly the time ele[1] occurred in test_list
Lastly, you sort the keys, but in reverse order - that is, descending, highest to lowest, [(2, 7), (8, 7), (3, 7), (6, 5), (2, 5), (9, 8)] with the values that have the same keys (like 7 and 5) grouped together. Note, according to the documentation sorted is stable, meaning it will preserve the order of elements from input, and this is why when the keys are the same, you get them in the order as in test_list i.e. (2,7) goes first and (3,7) last in the "7" group.

freq_2ndEle is a dictionary that contains the second elements of the tuple as keys, and their frequencies as values. Passing this frequency as a return value of lambda in the key argument of the function sorted will sort the list by this return value of lambda (which is the frequency).
If your question is about how lambda works, you can refer to this brief explanation which is pretty simple.

Related

How to sort a tuple based on a value within the list of tuples

In python, I wish to sort tuples based on the value of their last element. For example, i have a tuple like the one below.
tuples = [(2,3),(5,7),(4,3,1),(6,3,5),(6,2),(8,9)]
which after sort I wish to be in this format.
tuples = [(4,3,1),(6,2),(2,3),(6,3,5),(5,7),(8,9)]
How do i get to doing that?
Povide list.sort with an appropriate key function that returns the last element of a tuple:
tuples.sort(key=lambda x: x[-1])
You can use:
from operator import itemgetter
tuples = sorted(tuples, key=itemgetter(-1))
The point is that we use key as a function to map the elements on an orderable value we wish to sort on. With itemgetter(-1) we construct a function, that for a value x, will return x[-1], so the last element.
This produces:
>>> sorted(tuples, key=itemgetter(-1))
[(4, 3, 1), (6, 2), (2, 3), (6, 3, 5), (5, 7), (8, 9)]

How to create a sorted representation of dictionary, using a custom comparator on tuple values?

I'm trying to figure out how I can create a sorted representation of a dictionary with values (which are tuples), using with custom comparator on the values in Python 3, in a generalised way.
I have read these topics, but I'm still struggling:
Sort a Python dictionary by value
How to use a custom comparison function in Python 3?
As a specific example, one could consider the problem I'm trying to solve as, "get a list of products sorted by total cost, given a dictionary that contains the products (the key) a customer has in their checkout, along with the number and cost of each product (stored as a 2-tuple). In python 2, one could use something like this:
checkout_dict = {'Apples': (1, 3), 'Oranges': (3, 3), 'Grapes': (7, 1),
'Cheese': (10, 1), 'Crackers': (4, 4)}
from operator import itemgetter
def sort_dict(dict, comparison_func):
return sorted(dict.iteritems(), key=itemgetter(1),
cmp=comparison_func)
def cmp_total_cost(product_data_1, product_data_2):
total_product_cost_1 = (product_data_1[0]) * (product_data_1[0])
total_product_cost_2 = (product_data_2[0]) * (product_data_2[0])
return total_product_cost_2 - total_product_cost_1
print sort_dict(checkout_dict, cmp_total_cost)
The expected output would look something like this:
[('Crackers', (4, 4)), ('Cheese', (10, 1)), ('Oranges', (3, 3)),
('Grapes', (7, 1)), ('Apples', (1, 3))]
However in Python 3, the cmp parameter for sorted was deprecated, and instead we need to include the behaviour as part of the key parameter.
I understand that we need to use something like like the cmp_to_key function from the functools module, but I can't wrap my head around how I can keep everything generalised. I'm confused about how the itemgetter(1) can be combined with the cmp_to_key function and a custom comparison function.
Also, I understand that with the above example I could easily just loop over the dictionary first, and calculate the total costs, then do the sort, but I'm looking for a general solution I can apply for many different types of comparisons.
Note
I'd also like this to be as performant as possible. I found some info that using operator.itemgetter can really help speed things up:
Sorting Dictionaries by Value in Python (improved?)
If you simply want to get a list of tuples, ordered by the first element times the second element, this will do:
sorted(checkout_dict.items(), key=lambda item: item[1][0] * item[1][1])
# [('Apples', (1, 3)), ('Grapes', (7, 1)), ('Oranges', (3, 3)), ('Cheese', (10, 1)),
# ('Crackers', (4, 4))]
# or in the other way around
sorted(checkout_dict.items(), key=lambda item: item[1][0] * item[1][1], reverse=True)
# [('Crackers', (4, 4)), ('Cheese', (10, 1)), ('Oranges', (3, 3)), ('Grapes', (7, 1)),
# ('Apples', (1, 3))]

How does `key=function` in `sorted(tuple, key=function)` work?

Given a list of non-empty tuples, return a list sorted in increasing order by the last element in each tuple.
e.g. [(1, 7), (1, 3), (3, 4, 5), (2, 2)] yields [(2, 2), (1, 3), (3, 4, 5), (1, 7)]
Hint: use a custom key= function to extract the last element form each tuple.
The solution to the problem is:
def last(a):
return a[-1]
def sort_last(tuples):
return sorted(tuples, key=last)
Can anyone help me to understand what arguments are passed to the last function? Specifically, what does a contain?
We have not passed any values or arguments while calling the last function in the sorted method.
This is what is called a "lambda".
It's passing the current element of your list to the function "last" which will then get the last element of the tuple.
So the parameter "a" is the tuple being currently processed.
Iterating through a tuple via for loop - (Sorted initial list with 2nd element of the tuple.)
Nested for loops - comparing original tuple with the sorted 2nd element list.
tuple1 = [(2, 5), (1, 2), (4, 4), (2, 3), (2, 1)]
List2 =[]
List3 =[]
for t in tuple1:
List2.append(t[1],)
List2.sort()
print(List2)
for l in List2:
for q in tuple1:
if l == int(q[1],):
List3.append(q)
print(List3)

How to select increasing elements of a list of tuples?

I have the following list of tuples:
a = [(1, 2), (2, 4), (3, 1), (4, 4), (5, 2), (6, 8), (7, -1)]
I would like to select the elements which second value in the tuple is increasing compared to the previous one. For example I would select (2, 4) because 4 is superior to 2, in the same manner I would select (4, 4) and (6, 8).
Can this be done in a more elegant way than a loop starting explicitly on the second element ?
To clarify, I want to select the tuples which second elements are superior to the second element of the prior tuple.
>>> [right for left, right in pairwise(a) if right[1] > left[1]]
[(2, 4), (4, 4), (6, 8)]
Where pairwise is an itertools recipe you can find here.
You can use a list comprehension to do this fairly easily:
a = [a[i] for i in range(1, len(a)) if a[i][1] > a[i-1][1]]
This uses range(1, len(a)) to start from the second element in the list, then compares the second value in each tuple with the second value from the preceding tuple to determine whether it should be in the new list.
Alternatively, you could use zip to generate pairs of neighbouring tuples:
a = [two for one, two in zip(a, a[1:]) if two[1] > one[1]]
You can use enumerate to derive indices and then make list comprehension:
a = [t[1] for t in enumerate(a[1:]) if t[1][1] > a[t[0]-1][1]]
You can use list comprehension
[i for i in a if (i[0] < i[1])]
Returns
[(1, 2), (2, 4), (6, 8)]
Edit: I was incorrect in my understanding of the question. The above code will return all tuples in which the second element is greater than the first. This is not the answer to the question OP asked.

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))])

Categories