Sorting in a dictionary [duplicate] - python

This question already has answers here:
How do I sort a dictionary by value?
(34 answers)
Closed 2 years ago.
Im trying to get the output from my dictionary to be ordered from their values in stead of keys
Question:
ValueCount that accepts a list as a parameter. Your function will return a list of tuples. Each tuple will contain a value and the number of times that value appears in the list
Desired outcome
>>> data = [1,2,3,1,2,3,5,5,4]
>>> ValueCount(data)
[(1, 2), (2, 2), (5, 1), (4, 1)]
My code and outcome
def CountValues(data):
dict1 = {}
for number in data:
if number not in dict1:
dict1[number] = 1
else:
dict1[number] += 1
tuple_data = dict1.items()
lst = sorted(tuple_data)
return(lst)
>>>[(1, 2), (2, 2), (3, 2), (4, 1), (5, 2)]
How would I sort it ascendingly by using the values instead of keys.

If you want to sort by the values(second item in each tuple), specify key:
sorted(tuple_data, key=lambda x: x[1])
Or with operator.itemgetter:
sorted(tuple_data, key=operator.itemgetter(1))
Also as a side note, your counting code:
dict1 = {}
for number in data:
if number not in dict1:
dict1[number] = 1
else:
dict1[number] += 1
Can be simplified with collections.Counter:
dict1 = collections.Counter(data)
With all the above in mind, your code could look like this:
from operator import itemgetter
from collections import Counter
def CountValues(data):
counts = Counter(data)
return sorted(counts.items(), key=itemgetter(1))
print(CountValues([1,2,3,1,2,3,5,5,4]))
# [(4, 1), (1, 2), (2, 2), (3, 2), (5, 2)]

You can use the sorted with the help of key parameter. it is not a in-place sorting . Thus it never modifies the original array.
for more
In [18]: data = [1,2,3,1,2,3,5,5,4]
In [19]: from collections import Counter
In [20]: x=Counter(data).items()
#Sorted OUTPUT
In [21]: sorted(list(x), key= lambda i:i[1] )
Out[21]: [(4, 1), (1, 2), (2, 2), (3, 2), (5, 2)]
In [22]: x
Out[22]: dict_items([(1, 2), (2, 2), (3, 2), (5, 2), (4, 1)])

"Sort" function uses first element of data.
To sort dictionary by its values you can use for-loop for values:
d={1:1,2:2,5:2,4:3,3:2}
x=[]
for i in set(sorted(d.values())):
for j in sorted(d.items()):
if j[1]==i:
x.append(j)
print(x)
if you don't convert sorted(d.values()) to set{} , it will check every value, even there are same numbers. For example if your values list is [1,2,2,3] , it will check items for value "2" two times and as a result your sorted list will contain repeated data which both have value "2" . But set{} keeps only one of each element and in this case, for-loop will check every different value of d.values() . And if there are items with a same value, code will sort them by keys because of sorted(d.items()) .
(to understand better you can use this code without that set{} and use d.items() instead of sorted(d.items()))

Related

How to i make "rows" consiting of pairs from a list of objects that is sorted based on their attributes

I have created a class with attributes and sorted them based on their level of x, from 1-6. I then want to sort the list into pairs, where the objects with the highest level of "x" and the object with the lowest level of "x" are paired together, and the second most and second less and so on. If it was my way it would look like this, even though objects are not itereable.
for objects in sortedlist:
i = 0
row(i) = [[sortedlist[i], list[-(i)-1]]
i += 1
if i => len(sortedlist)
break
Using zip
I think the code you want is:
rows = list(zip(sortedList, reversed(sortedList)))
However, note that this would "duplicate" the elements:
>>> sortedList = [1, 2, 3, 4, 5]
>>> list(zip(sortedList, reversed(sortedList)))
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]
If you know that the list has an even number of elements and want to avoid duplicates, you can instead write:
rows = list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
With the following result:
>>> sortedList = [1,2,3,4,5,6]
>>> list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
[(1, 6), (2, 5), (3, 4)]
Using loops
Although I recommend using zip rather than a for-loop, here is how to fix the loop you wrote:
rows = []
for i in range(len(sortedList)):
rows.append((sortedList[i], sortedList[-i-1]))
With result:
>>> sortedList=[1,2,3,4,5]
>>> rows = []
>>> for i in range(len(sortedList)):
... rows.append((sortedList[i], sortedList[-i-1]))
...
>>> rows
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]

How can I save the original index after sorting a list?

Let's say I have the following array:
a = [4,2,3,1,4]
Then I sort it:
b = sorted(A) = [1,2,3,4,4]
How could I have a list that map where each number was, ex:
position(b,a) = [3,1,2,0,4]
to clarify this list contains the positions not values)
(ps' also taking in account that first 4 was in position 0)
b = sorted(enumerate(a), key=lambda i: i[1])
This results is a list of tuples, the first item of which is the original index and second of which is the value:
[(3, 1), (1, 2), (2, 3), (0, 4), (4, 4)]
def position(a):
return sorted(range(len(a)), key=lambda k: a[k])

Sort tuple list with another list

I have a tuple list to_order such as:
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
And a list which gives the order to apply to the second element of each tuple of to_order:
order = [2, 1, 3]
So I am looking for a way to get this output:
ordered_list = [(2, 2), (3,2), (0, 1), (1, 3)]
Any ideas?
You can provide a key that will check the index (of the second element) in order and sort based on it:
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
print(sorted(to_order, key=lambda item: order.index(item[1]))) # [(2, 2), (3, 2), (0, 1), (1, 3)]
EDIT
Since, a discussion on time complexities was start... here ya go, the following algorithm runs in O(n+m), using Eric's input example:
N = 5
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)
def eric_sort(to_order, order):
bins = {}
for pair in to_order:
bins.setdefault(pair[1], []).append(pair)
return [pair for i in order for pair in bins[i]]
def alfasin_new_sort(to_order, order):
arr = [[] for i in range(len(order))]
d = {k:v for v, k in enumerate(order)}
for item in to_order:
arr[d[item[1]]].append(item)
return [item for sublist in arr for item in sublist]
from timeit import timeit
print("eric_sort", timeit("eric_sort(to_order, order)", setup=setup, number=1000))
print("alfasin_new_sort", timeit("alfasin_new_sort(to_order, order)", setup=setup, number=1000))
OUTPUT:
eric_sort 59.282021682999584
alfasin_new_sort 44.28244407700004
Algorithm
You can distribute the tuples in a dict of lists according to the second element and iterate over order indices to get the sorted list:
from collections import defaultdict
to_order = [(0, 1), (1, 3), (2, 2), (3, 2)]
order = [2, 1, 3]
bins = defaultdict(list)
for pair in to_order:
bins[pair[1]].append(pair)
print(bins)
# defaultdict(<class 'list'>, {1: [(0, 1)], 3: [(1, 3)], 2: [(2, 2), (3, 2)]})
print([pair for i in order for pair in bins[i]])
# [(2, 2), (3, 2), (0, 1), (1, 3)]
sort or index aren't needed and the output is stable.
This algorithm is similar to the mapping mentioned in the supposed duplicate. This linked answer only works if to_order and order have the same lengths, which isn't the case in OP's question.
Performance
This algorithm iterates twice over each element of to_order. The complexity is O(n). #alfasin's first algorithm is much slower (O(n * m * log n)), but his second one is also O(n).
Here's a list with 10000 random pairs between 0 and 1000. We extract the unique second elements and shuffle them in order to define order:
from random import randrange, shuffle
from collections import defaultdict
from timeit import timeit
from itertools import chain
N = 1000
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)
def eric(to_order, order):
bins = defaultdict(list)
for pair in to_order:
bins[pair[1]].append(pair)
return list(chain.from_iterable(bins[i] for i in order))
def alfasin1(to_order, order):
arr = [[] for i in range(len(order))]
d = {k:v for v, k in enumerate(order)}
for item in to_order:
arr[d[item[1]]].append(item)
return [item for sublist in arr for item in sublist]
def alfasin2(to_order, order):
return sorted(to_order, key=lambda item: order.index(item[1]))
print(eric(to_order, order) == alfasin1(to_order, order))
# True
print(eric(to_order, order) == alfasin2(to_order, order))
# True
print("eric", timeit("eric(to_order, order)", globals=globals(), number=100))
# eric 0.3117517130003762
print("alfasin1", timeit("alfasin1(to_order, order)", globals=globals(), number=100))
# alfasin1 0.36100843100030033
print("alfasin2", timeit("alfasin2(to_order, order)", globals=globals(), number=100))
# alfasin2 15.031453827000405
Another solution:
[item for key in order for item in filter(lambda x: x[1] == key, to_order)]
This solution works off of order first, filtering to_order for each key in order.
Equivalent:
ordered = []
for key in order:
for item in filter(lambda x: x[1] == key, to_order):
ordered.append(item)
Shorter, but I'm not aware of a way to do this with list comprehension:
ordered = []
for key in order:
ordered.extend(filter(lambda x: x[1] == key, to_order))
Note: This will not throw a ValueError if to_order contains a tuple x where x[1] is not in order.
I personally prefer the list objects sort function rather than the built-in sort which generates a new list rather than changing the list in place.
to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
to_order.sort(key=lambda x: order.index(x[1]))
print(to_order)
>[(2, 2), (3, 2), (0, 1), (1, 3)]
A little explanation on the way: The key parameter of the sort method basically preprocesses the list and ranks all the values based on a measure. In our case order.index() looks at the first occurrence of the currently processed item and returns its position.
x = [1,2,3,4,5,3,3,5]
print x.index(5)
>4

How to sort dictionary on first element of the key (tuple)

I have a dictionary where each key is a tuple of values, I want to use the sorted() method to sort the dictionary on the very first element of my tuple. My code looks like this:
def mapData(header_list, dict_obj):
master_dict = {}
client_section_list = []
for element in header_list:
for row in dict_obj:
if (row['PEOPLE_ID'], row['DON_DATE']) == element:
client_section_list.append(row)
element = list(element)
element_list = [client_section_list[0]['DEDUCT_AMT'],
client_section_list[0]['ND_AMT'],
client_section_list[0]['DEDUCT_YTD'],
client_section_list[0]['NONDEDUCT_YTD']
]
try:
element_list.append((float(client_section_list[0]['DEDUCT_YTD']) +
float(client_section_list[0]['NONDEDUCT_YTD'])
))
except ValueError:
pass
element.extend(element_list)
element = tuple(element)
master_dict[element] = client_section_list
client_section_list = []
return sorted(master_dict, key=lambda key: key[master_dict[(1)]]
The last line is where I'm trying to find a way to sort it. My tuple looks like this:
(312178078,6/22/15,25,0,25,0,25.0)
Not entirely sure what you are trying to do, particularly what that function is supposed to return. I assume that you want to return the dictionary sorted by the first element in the key-tuples.
For this, there are two things to note:
Tuples are by default sorted by their first element (and if those are the same, then by the second, and so on), so no special key function is required
Regular dictionaries are unordered, i.e. they can not be permanently sorted in any order; you can only sort their items as a list, or use that list to create an OrderedDict instead
Some minimal example:
>>> d = {(2,4): 1, (1,3): 2, (1,2): 3, (3,1): 4}
>>> sorted(d)
[(1, 2), (1, 3), (2, 4), (3, 1)]
>>> sorted(d.items())
[((1, 2), 3), ((1, 3), 2), ((2, 4), 1), ((3, 1), 4)]
>>> collections.OrderedDict(sorted(d.items()))
OrderedDict([((1, 2), 3), ((1, 3), 2), ((2, 4), 1), ((3, 1), 4)])
In your case, you probably want this:
return collections.OrderedDict(sorted(master_dict.items()))
As #tobias_k has mentioned, sorted sorts tuples by its elements with decreasing priority, e.g. if you take a tuple (a, b, c) the highest sorting priority goes to a, then goes b etc (by default sorted uses object's comparison methods and this is how tuple comparison works). So sorted(master_dict) is all you need if you want a list of sorted keys, yet I believe you really want to leave the values
sorted(master_dict.items(), key=lambda key: key[0])
dict.items returns tuples of form (key, value) so here you need to specify the sorting key.

Making list of list oneliner -python

I have a list
l=[(1,2),(1,6),(3,4),(3,6),(1,4),(4,3)]
I want to return a list that contains lists by the first number in each tuple.
Something like this:
[[2,4,6],[4,6],[3]]
To make a program that iterates on list and writing a whole function that does it is easy.
I want to find a oneliner - python way of doing it.
Any ideas?
>>> from itertools import groupby
>>> from operator import itemgetter
>>> L = [(1,2), (1,6), (3,4), (3,6), (1,4), (4,3)]
>>> [[y for x, y in v] for k, v in groupby(sorted(L), itemgetter(0))]
[[2, 4, 6], [4, 6], [3]]
Explanation
This works by using itertools.groupby. groupby finds consecutive groups in an iterable, returning an iterator through key, group pairs.
The argument given to groupby is a key function, itemgetter(0) which is called for each tuple, returning the first item as the key to groupby.
groupby groups elements in their original order so if you want to group by the first number in the list, it must first be sorted so groupby can go through the first numbers in ascending order and actually group them.
>>> sorted(L)
[(1, 2), (1, 4), (1, 6), (3, 4), (3, 6), (4, 3)]
There is the sorted list where you can clearly see the groups that will be created if you look back to the final output. Now you can use groupby to show the key, group pairs.
[(1, <itertools._grouper object at 0x02BB7ED0>), (3, <itertools._grouper object at 0x02BB7CF0>), (4, <itertools._grouper object at 0x02BB7E30>)]
Here are the sorted items grouped by the first number. groupby returns the group for each key as an iterator, this is great and very efficient but for this example we will just convert it to a list to make sure it's working properly.
>>> [(k, list(v)) for k,v in groupby(sorted(L), itemgetter(0))]
[(1, [(1, 2), (1, 4), (1, 6)]), (3, [(3, 4), (3, 6)]), (4, [(4, 3)])]
That is almost the right thing but the required output shows only the 2nd number in the groups in each list. So the following achieves the desired result.
[[y for x, y in v] for k, v in groupby(sorted(L), itemgetter(0))]
l = [(1, 2), (1, 6), (3, 4), (3, 6), (1, 4), (4, 3)]
d = {}
for (k, v) in l:
d.setdefault(k, []).append(v)
print d.values()
I know it's not a one liner, but perhaps it's easier to read than a one liner.

Categories