counts = defaultdict(int)
for elem in sets: #list of sets
for _ in elem: #each set contains elements that may be duplicates among the sets
counts[_] += 1
Is there a way to use dict comprehension for code like this?
Don't use a list comprehension for side effects (i.e. updating counts).
Instead, if you replace defaultdict(int) with Counter, you can write the solution in one line. (I assume your end goal is to make your code shorter/cleaner.)
sets = [
{1, 2, 3},
{1, 2},
{3},
{3}]
# ---
from collections import Counter
counts = Counter(x for s in sets for x in s)
print(counts) # -> Counter({3: 3, 1: 2, 2: 2})
Note: Instead of the nested generator expression, you might prefer to use itertools.chain.from_iterable(sets).
If you are adamant at using list comprehension, you can do this way:
>>> counts = {1:4, 2:5, 3:8, 4:2, 5:9}
>>> sets = [{2,3}, {3,1}, {2}]
>>> [[counts.update({e: counts[e]+1}) for e in si] for si in sets]
[[None, None], [None, None], [None]]
>>> counts
{1: 5, 2: 7, 3: 10, 4: 2, 5: 9}
But yes, as others suggested, you can do it using Counter:
#1: use Counter to get new counts from sets
#2: merge both new & default counters
#3: get Counter back into your dictionary
With sample input:
>>> counts = {1:4, 2:5, 3:8, 4:2, 5:9}
>>> sets = [{2,3}, {3,1}, {2}]
>>> from collections import Counter
>>> cntr_new = Counter(x for s in sets for x in s)
>>> cntr_new
Counter({2: 2, 3: 2, 1: 1})
>>> cntr_orig = Counter(counts)
>>> cntr_final = cntr_orig + cntr_new
Final result:
>>> dict(cntr_final)
{1: 5, 2: 7, 3: 10, 4: 2, 5: 9}
Related
I tried to add the string elements to the dictionary but it doesnt work
adatok = ["marha", 1, "kacsa", 2, "kacsa", 1, "birka", 3, "marha", 4, "marha", 2]
sorszámok = list(set([x for x in adatok if type(x) == int]))
szótár = {}
for i in sorszámok:#Létrehozzuk a sorszámokat egy szótárba
szótár.update({i:set()})
for j in adatok:
if type(j) == int:
szótár[j].add(adatok[adatok.index(j)-1])
#Expected output:{1: {'marha','kacsa'}, 2: {'kacsa','marha'}, 3: {'birka'}, 4: {'marha'}}
#Output:{1: {'marha'}, 2: {'kacsa'}, 3: {'birka'}, 4: {'marha'}}
Instead of starting with unique keys and then trying to find the values for each key, iterate over the key, value pairs and add them to the sets. Using defaultdict saves you the trouble of setting up the dict ahead of time.
>>> from collections import defaultdict
>>> adatok = ["marha", 1, "kacsa", 2, "kacsa", 1, "birka", 3, "marha", 4, "marha", 2]
>>> szótár = defaultdict(set)
>>> for k, v in zip(adatok[1::2], adatok[0::2]):
... szótár[k].add(v)
...
>>> dict(szótár)
{1: {'marha', 'kacsa'}, 2: {'marha', 'kacsa'}, 3: {'birka'}, 4: {'marha'}}
I wanted to create an mapping between two arrays. But in python, doing this resulted in a mapping with last element getting picked.
array_1 = [0,0,0,1,2,3]
array_2 = [4,4,5,6,8,7]
mapping = dict(zip(array_1, array_2))
print(mapping)
The mapping resulted in {0: 5, 1: 6, 2: 8, 3: 7}
How to pick the most occurring element in this case 4 for key 0.
You can create a dictionary with key and a list of values for the key. Then you can go over the list of values in this dictionary, and update the value to be the most frequent item in the list using Counter.most_common
from collections import defaultdict, Counter
array_1 = [0,0,0,1,2,3]
array_2 = [4,4,5,6,8,7]
mapping = defaultdict(list)
#Create the mapping with a list of values
for key, value in zip(array_1, array_2):
mapping[key].append(value)
print(mapping)
#defaultdict(<class 'list'>, {0: [4, 4, 5], 1: [6], 2: [8], 3: [7]})
res = defaultdict(int)
#Iterate over mapping and chose the most frequent element in the list, and make it the value
for key, value in mapping.items():
#The most frequent element will be the first element of Counter.most_common
res[key] = Counter(value).most_common(1)[0][0]
print(dict(res))
The output will be
{0: 4, 1: 6, 2: 8, 3: 7}
You can count frequencies of all mappings using Counter and then sort those mappings by key and frequency:
from collections import Counter
array_1 = [0,0,0,1,2,3]
array_2 = [4,4,5,6,8,7]
c = Counter(zip(array_1, array_2))
dict(i for i, _ in sorted(c.items(), key=lambda x: (x[0], x[1]), reverse=True))
# {3: 7, 2: 8, 1: 6, 0: 4}
I've got a system in which I often (but not constantly) have to find the next item in a tuple. I'm currently doing this like so:
mytuple = (2,6,4,8,7,9,14,3)
currentelement = 4
def f(mytuple, currentelement):
return mytuple[mytuple.index(currentelement) + 1]
nextelement = f(mytuple, currentelement)
All elements are unique and I am not stuck with the tuple, I can make it something else earlier in the program if needed.
Since I need to do this a lot, I was wondering whether there is any more efficient way to do this?
Use a dict here, dicts provide O(1) lookup compared to list.index which is an O(N) operation.
And this will work for strings as well.
>>> lis = (2,6,4,8,7,9,14,3)
>>> dic = dict(zip(lis, lis[1:]))
>>> dic[4]
8
>>> dic[7]
9
>>> dic.get(100, 'not found') #dict.get can handle key errors
'not found'
A memory efficient version to create the above dict:
>>> from itertools import izip
>>> lis = (2,6,4,8,7,9,14,3)
>>> it1 = iter(lis)
>>> it2 = iter(lis)
>>> next(it2)
2
>>> dict(izip(it1,it2))
{2: 6, 4: 8, 6: 4, 7: 9, 8: 7, 9: 14, 14: 3}
You might wish to build an index using a dictionary:
# The list
>>> lis = (2,6,4,8,7,9,14,3)
# build the index
>>> index = dict(zip(lis, range(len(lis))))
>>> index
{2: 0, 3: 7, 4: 2, 6: 1, 7: 4, 8: 3, 9: 5, 14: 6}
# Retrieve position by using the index
>>> index[6]
1
>>> lis[index[6]+1]
4
If your list change over time, you will have to rebuild the index. For a more memory efficient solution, you might prefer using izip instead of `zip̀ as suggested in an other answer.
I need to unite two lists in Python3,where duplicates can exist,and for one set of these the resulting list will contain as many as max in both lists.An example might clarify it:
[1,2,2,5]( some operator)[2,5,5,5,9]=[1,2,2,5,5,5,9]
Ideas?
You can use the collections.Counter class:
>>> from collections import Counter
>>> combined = Counter([1,2,2,5]) | Counter([2,5,5,5,9])
>>> list(combined.elements())
[1, 2, 2, 5, 5, 5, 9]
It functions as a multiset (an unordered collection where each element can appear multiple times). The | operator gives you the union of the multisets, where each element appears max(apperances_in_counter1, appearances_in_counter2) times.
This class was added in Python 2.7 and 3.1.
Why use lists in the first place? That data looks like a dict to me:
[1,2,2,5] -> {1: 1, 2: 2, 5: 1}
[2,5,5,5,9] -> {2: 1, 5: 3, 9: 1}
Then it's simple:
keys = set(a.keys()+b.keys())
vals = [max(a.get(n, 0), b.get(n, 0)) for n in keys]
d = dict(zip(keys, vals))
print d
Result:
{1: 1, 2: 2, 5: 3, 9: 1}
Convert arrays to dictionaries with a[key] = count
Create new dictionary with rules c[key] = a.get(key, 0) > b.get(key, 0) and a[key] or b[key]. You need to iterate through both keys in a and in b dicts.
Expand dictionary, result += [value] * key
this is what I did. is there a better way in python?
for k in a_list:
if kvMap.has_key(k):
kvMap[k]=kvMap[k]+1
else:
kvMap[k]=1
Thanks
Use defaultdict
from collections import defaultdict
kvmap= defaultdict(int)
for k in a_list:
kvmap[k] += 1
Single element:
a_list.count(k)
All elements:
counts = dict((k, a_list.count(k)) for k in set(a_list))
I dunno, it basically looks fine to me. Your code is simple and easy to read which is an important part of what I consider pythonic.
You could trim it up a bit like so:
for k in a_list:
kvMap[k] = 1 + kvMap.get(k,0)
Such an old question, but considering that adding to a defaultdict(int) is such a common use, It should come as no surprise that collections has a special name for that (since Python 2.7)
>>> from collections import Counter
>>> Counter([1, 2, 1, 1, 3, 2, 3, 4])
Counter({1: 3, 2: 2, 3: 2, 4: 1})
>>> Counter("banana")
Counter({'a': 3, 'n': 2, 'b': 1})
Another solution exploits setdefault():
for k in a_list:
kvMap[k] = kvMap.setdefault(k, 0) + 1
If your list is sorted, an alternative way would be to use itertools.groupby. This might not be the most effective way, but it's interesting nonetheless. It retuns a dict of item > count :
>>> import itertools
>>> l = [1,1,2,3,4,4,4,5,5,6,6,6,7]
>>> dict([(key, len([e for e in group]))
for (key, group)
in itertools.groupby(l)])
{1: 2, 2: 1, 3: 1, 4: 3, 5: 2, 6: 3, 7: 1}