I'm trying to sort a dict by multiple keys. This is the dict I have:
standings = {1: {1: 1, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Jack', 'points': 15},
2: {1: 1, 2: 0, 3: 2, 4: 2, 5: 0, 'player': 'Kate', 'points': 15},
3: {1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Sawyer', 'points': 5}}
I want to sort it by, in this order: 'points', 1, 2, 3, 4, 5.
I could do this, I assume:
reversed(sorted(standings, key=lambda x: (standings[x]['points'],
standings[x][1],
standings[x][2],
standings[x][3],
standings[x][4],
standings[x][5])))
However, the 1, 2, 3, 4, 5 keys are dynamic (and could be 1, 2, 3, 4, 5, 6, 7, 8, 9, etc.)
So, somehow I want to make the sorting keys dynamic in sorted(), except for 'points' which will always be used.
The result I want is a reversed sorted list with the keys (which are player ids from the db) from the first dict. i.e. for the given example it will be[2, 1, 3].
Basically, what you are looking for is itemgetter with range:
from operator import itemgetter
standings = ... # your dictionary of dictionaries
n = 5 # number of keys to sort on (1, 2, 3, ..., n)
# The following will collect values by 'points', 1, 2, ..., n in a tuple:
get_values = itemgetter('points', *range(1, n + 1))
result = sorted(standings,
key=lambda x: get_values(standings[x]),
reverse=True)
# [2, 1, 3]
Explanation:
In order to achieve the sorting by several dict keys, you could use itemgetter to create a function that will return a tuple of values by specified keys. So, as a simple example, if you would have this dictionary:
my_dict = {1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 'player': 'Ben'}
and you would want to get the values by keys player, 1 and 2, you would write:
from operator import itemgetter
get_values = itemgetter('player', 1, 2)
get_values(my_dict)
# ('Ben', 10, 20)
Now, as the number of the values can vary and those are actually ordered integers (1, 2, 3, ...), you could unpack the given range to the itemgetter:
get_values = itemgetter('player', *range(1, 4)) # 'player', 1, 2, 3
get_values(my_dict)
# ('Ben', 10, 20, 30)
Finally, for your given example dictionary of dictionaries we get these tuples for each child dictionary and sort by them:
standings = {1: {1: 1, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Jack', 'points': 15},
2: {1: 1, 2: 0, 3: 2, 4: 2, 5: 0, 'player': 'Kate', 'points': 15},
3: {1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Sawyer', 'points': 5}}
max_key = 5 # you may also calculate it as a max integer key
get_values = itemgetter('points', *range(1, n + 1))
result = sorted(standings, key=lambda x: get_values(standings[x]))
# [3, 1, 2]
# or reversed:
sorted(standings,
key=lambda x: get_keys(standings[x]),
reverse=True)
# [2, 1, 3]
Related
I have Nested dictionary something like this.
{'A': {'21-26': 2,
'26-31': 7,
'31-36': 3,
'36-41': 2,
'41-46': 0,
'46-51': 0,
'Above 51': 0},
'B': {'21-26': 2,
'26-31': 11,
'31-36': 5,
'36-41': 4,
'41-46': 1,
'46-51': 0,
'Above 51': 3}}
And I want to create list by key from second dictionary.
And i don't want duplicates in my list.
Required Output is
ls = ['21-26','26-31','31-36','36-41','41-46','46-51','Above 51']
Thank you for your time and consideration.
You can use:
>>> list(set(key for val in d.values() for key in val.keys()))
['21-26', '36-41', '31-36', '46-51', 'Above 51', '26-31', '41-46']
Where d is your dictionary.
Simple set comprehension, then convert to list. a is your dict.
list({k for v in a.values() for k in v.keys()})
Output ordering is random, but you can sort how you like.
Can you use pandas? IF so:
import pandas as pd
a = {'A': {'21-26': 2, '26-31': 7, '31-36': 3, '36-41': 2, '41-46': 0, '46-51': 0, 'Above 51': 0}, 'B': {'21-26': 2, '26-31': 11, '31-36': 5, '36-41': 4, '41-46': 1, '46-51': 0, 'Above 51': 3}}
pd.DataFrame(a).index.to_list()
output:
['21-26', '26-31', '31-36', '36-41', '41-46', '46-51', 'Above 51']
You can use chain.from_iterable() to chain inner dictionaries and dict.fromkeys() to remove duplicates:
from itertools import chain
c = chain.from_iterable(dct.values())
result = list(dict.fromkeys(c))
I am using Python3. I have a list a of only integers. Now, I want to save the element and the number it repeats itself in a row in another list.
Example:
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
Output:
result = ["6,1", "0, 2", "2, 4", "1, 1", "89, 2"]
# the number before the "," represents the element, the number after the "," represents how many times it repeats itself.
How to efficiently achieve my goal ?
I believe all the solutions given are counting the total occurrences of a number in the list rather than counting the repeating runs of a number.
Here is a solution using groupby from itertools. It gathers the runs and appends them to a dictionary keyed by the number.
from itertools import groupby
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
d = dict()
for k, v in groupby(a):
d.setdefault(k, []).append(len(list(v)))
Dictionary created:
>>> d
{6: [1], 0: [2], 2: [4], 1: [1], 89: [2]}
Note that all runs only had 1 count in their list. If there where other occurrences of a number already seen, there would be multiple counts in the lists (that are the values for dictionary).
for counting an individual element,
us list.count,
i.e, here, for, say 2, we user
a.count(2),
which outputs 4,
also,
set(a) gives the unique elements in a
overall answer,
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
nums = set(a)
result = [f"{val}, {a.count(val)}" for val in set(a)]
print(result)
which gives
['0, 2', '1, 1', '2, 4', '6, 1', '89, 2']
Method 1: using for loop
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
result = []
a_set = set(a) # transform the list into a set to have unique integer
for nbr in a_set:
nbr_count = a.count(nbr)
result.append("{},{}".format(nbr, nbr_count))
print(result) # ['0,2', '1,1', '2,4', '6,1', '89,2']
Method 2: using list-comprehensions
result = ["{},{}".format(item, a.count(item)) for item in set(a)]
print(result) # ['0,2', '1,1', '2,4', '6,1', '89,2']
you can use Python List count() Method, method returns the number of elements with the specified value.
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
print ({x:a.count(x) for x in a})
output:
{6: 1, 0: 2, 2: 4, 1: 1, 89: 2}
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
dic = dict()
for i in a:
if(i in dic):
dic[i] = dic[i] + 1
else:
dic[i] = 1
result = []
for i in dic:
result.append(str(i) +"," + str(dic[i]))
Or:
from collections import Counter
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
mylist = [Counter(a)]
print(mylist)
You can use Counter from collections:
from collections import Counter
a = [6, 0, 0, 2, 2, 2, 2, 1, 89, 89]
counter = Counter(a)
result = ['{},{}'.format(k, v) for k,v in counter.items()]
This question already has answers here:
How do I count the occurrences of a list item?
(30 answers)
Closed 3 years ago.
I am trying to track seen elements, from a big array, using a dict.
Is there a way to force a dictionary object to be integer type and set to zero by default upon initialization?
I have done this with a very clunky codes and two loops.
Here is what I do now:
fl = [0, 1, 1, 2, 1, 3, 4]
seenit = {}
for val in fl:
seenit[val] = 0
for val in fl:
seenit[val] = seenit[val] + 1
Of course, just use collections.defaultdict([default_factory[, ...]]):
from collections import defaultdict
fl = [0, 1, 1, 2, 1, 3, 4]
seenit = defaultdict(int)
for val in fl:
seenit[val] += 1
print(fl)
# Output
defaultdict(<class 'int'>, {0: 1, 1: 3, 2: 1, 3: 1, 4: 1})
print(dict(seenit))
# Output
{0: 1, 1: 3, 2: 1, 3: 1, 4: 1}
In addition, if you don't like to import collections you can use dict.get(key[, default])
fl = [0, 1, 1, 2, 1, 3, 4]
seenit = {}
for val in fl:
seenit[val] = seenit.get(val, 0) + 1
print(seenit)
# Output
{0: 1, 1: 3, 2: 1, 3: 1, 4: 1}
Also, if you only want to solve the problem and don't mind to use exactly dictionaries you may use collection.counter([iterable-or-mapping]):
from collections import Counter
fl = [0, 1, 1, 2, 1, 3, 4]
seenit = Counter(f)
print(seenit)
# Output
Counter({1: 3, 0: 1, 2: 1, 3: 1, 4: 1})
print(dict(seenit))
# Output
{0: 1, 1: 3, 2: 1, 3: 1, 4: 1}
Both collection.defaultdict and collection.Counter can be read as dictionary[key] and supports the usage of .keys(), .values(), .items(), etc. Basically they are a subclass of a common dictionary.
If you want to talk about performance I checked with timeit.timeit() the creation of the dictionary and the loop for a million of executions:
collection.defaultdic: 2.160868141 seconds
dict.get: 1.3540439499999999 seconds
collection.Counter: 4.700308418999999 seconds
collection.Counter may be easier, but much slower.
You can use collections.Counter:
from collections import Counter
Counter([0, 1, 1, 2, 1, 3, 4])
Output:
Counter({1: 3, 0: 1, 2: 1, 3: 1, 4: 1})
You can then address it like a dictionary:
>>> Counter({1: 3, 0: 1, 2: 1, 3: 1, 4: 1})[1]
3
>>> Counter({1: 3, 0: 1, 2: 1, 3: 1, 4: 1})[0]
1
Using val in seenit is a bit faster than .get():
seenit = dict()
for val in fl:
if val in seenit :
seenit[val] += 1
else:
seenit[val] = 1
For larger lists, Counter will eventually outperform all other approaches. and defaultdict is going to be faster than using .get() or val in seenit.
I have a dictionary like this:
dict1 = {0: set([1, 4, 5]), 1: set([2, 6]), 2: set([3]), 3: set([0]), 4: set([1]), 5: set([2]), 6: set([])}
and from this dictionary I want to build another dictionary that count the occurrences of keys in dict1 in every other value ,that is the results should be:
result_dict = {0: 1, 1: 2, 2: 2, 3: 1, 4: 1, 5: 1, 6: 1}
My code was this :
dict1 = {0: set([1, 4, 5]), 1: set([2, 6]), 2: set([3]), 3: set([0]), 4: set([1]), 5:set([2]), 6: set([])}
result_dict = {}
for pair in dict1.keys():
temp_dict = list(dict1.keys())
del temp_dict[pair]
count = 0
for other_pairs in temp_dict :
if pair in dict1[other_pairs]:
count = count + 1
result_dict[pair] = count
The problem with this code is that it is very slow with large set of data.
Another attempt was in a single line, like this :
result_dict = dict((key ,dict1.values().count(key)) for key in dict1.keys())
but it gives me wrong results, since values of dict1 are sets:
{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0}
thanks a lot in advance
I suppose, for a first stab, I would figure out which values are there:
all_values = set().union(*dict1.values())
Then I'd try to count how many times each value occurred:
result_dict = {}
for v in all_values:
result_dict[v] = sum(v in dict1[key] for key in dict1)
Another approach would be to use a collections.Counter:
result_dict = Counter(v for set_ in dict1.values() for v in set_)
This is probably "cleaner" than my first solution -- but it does involve a nested comprehension which can be a little difficult to grok. It does work however:
>>> from collections import Counter
>>> dict1
{0: set([1, 4, 5]), 1: set([2, 6]), 2: set([3]), 3: set([0]), 4: set([1]), 5: set([2]), 6: set([])}
>>> result_dict = Counter(v for set_ in dict1.values() for v in set_)
Just create a second dictionary using the keys from dict1, with values initiated at 0. Then iterate through the values in the sets of dict1, incrementing values of result_dict as you go. The runtime is O(n), where n is the aggregate number of values in sets of dict1.
dict1 = {0: set([1, 4, 5]), 1: set([2, 6]), 2: set([3]), 3: set([0]), 4: set([1]), 5:set([2]), 6: set([])}
result_dict = dict.fromkeys(dict1.keys(), 0)
# {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0}
for i in dict1.keys():
for j in dict1[i]:
result_dict[j] += 1
print result_dict
# {0: 1, 1: 2, 2: 2, 3: 1, 4: 1, 5: 1, 6: 1}
I have dictionary like:
item_count_per_section = {1: 3, 2: 5, 3: 2, 4: 2}
And total count of items retrieved from this dictionary:
total_items = range(sum(item_count_per_section.values()))
Now I want to transform total_items by values of dictionary following way:
items_no_per_section = {1: [0,1,2], 2: [3,4,5,6,7], 3:[8,9], 4:[10,11] }
I.e. slice total_items sequencially to sublists which startrs from previous "iteration" index and finished with value from initial dictionary.
You don't need to find total_items at all. You can straightaway use itertools.count, itertools.islice and dictionary comprehension, like this
from itertools import count, islice
item_count_per_section, counter = {1: 3, 2: 5, 3: 2, 4: 2}, count()
print {k:list(islice(counter, v)) for k, v in item_count_per_section.items()}
Output
{1: [0, 1, 2], 2: [3, 4, 5, 6, 7], 3: [8, 9], 4: [10, 11]}
dict comprehension of itertools.isliced iter of total_items:
from itertools import islice
item_count_per_section = {1: 3, 2: 5, 3: 2, 4: 2}
total_items = range(sum(item_count_per_section.values()))
i = iter(total_items)
{key: list(islice(i, value)) for key, value in item_count_per_section.items()}
Outputs:
{1: [0, 1, 2], 2: [3, 4, 5, 6, 7], 3: [8, 9], 4: [10, 11]}
Note: this works for any total_items, not just range(sum(values)), assuming that was just your sample to keep the question generic. If you do just want the numbers, go with #thefourtheye's answer