Sorting dictionary using a tuple and operator.itemgetter - python

I am before my last milestone preparing suitable output. Its quite simple, I think, but my approach doesn't work:
Given an dictionary like
mydict = {x1: (val1, dist1, (lat1, lon1)), x2: (val2, dist2, (lat2, lon2)), ...}
I tried to sort this in a nested way in an array, first using the "values" and if equals, sorting for "dist".
However, I did my homework and tried it using this way:
import operator
s = sorted(mydict.iteritems(), key=operator.itemgetter(1))
The thing is that if the sort method would be appliable to tuples, it would be quite easy, because it's already in the right order.
Unfortunately I get:
'list' object is not callable
Do you have an idea?

Is it possible you overwrote the name sorted with a list value?

The thing is that if the sort method would be appliable to tuples, it would be quite easy
sorted() works perfectly fine on a list of tuples, in which case it uses a lexicographic ordering.
(a,b) ≤ (a′,b′) if and only if a < a′ or (a = a′ and b ≤ b′).
This can be expanded to any number of dimensions.
>>> sorted([(10, 1), (5, 3), (6, 5), (3, 2), (5, 10)])
[(3, 2), (5, 3), (5, 10), (6, 5), (10, 1)]

Related

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

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.

Problem in getting unique elements from list of tuples

I got sample input as a=[(1,2),(2,3),(1,1),(2,1)], and the expected ouput is a=[(1,2),(2,3),(1,1)].
Here, (2,1) is removed, since the same combinational pair (1,2) is already available. I tried below code to remove duplicate pairs
map(tuple, set(frozenset(x) for x in a))
However, the output is [(1, 2), (2, 3), (1,)]. How to get (1,1) pair as (1,1) instead of (1,).
You can use a dict instead of a set to map the frozensets to the original tuple values. Build the dict in reversed order of the list so that duplicating tuples closer to the front can take precedence:
{frozenset(x): x for x in reversed(a)}.values()
This returns:
[(1, 2), (2, 3), (1, 1)]
This is one approach using sorted
Ex:
a=[(1,2),(2,3),(1,1),(2,1)]
print set([tuple(sorted(i)) for i in a])
Output:
set([(1, 2), (2, 3), (1, 1)])

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 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.

Categories