I am trying to learn Python dictionary comprehension, and I think it is possible to do in one line what the following functions do. I wasn't able to make the n+1 as in the first or avoid using range() as in the second.
Is it possible to use a counter that automatically increments during the comprehension, as in test1()?
def test1():
l = ['a', 'b', 'c', 'd']
d = {}
n = 1
for i in l:
d[i] = n
n = n + 1
return d
def test2():
l = ['a', 'b', 'c', 'd']
d = {}
for n in range(len(l)):
d[l[n]] = n + 1
return d
It's quite simple using the enumerate function:
>>> L = ['a', 'b', 'c', 'd']
>>> {letter: i for i,letter in enumerate(L, start=1)}
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
Note that, if you wanted the inverse mapping, i.e. mapping 1 to a, 2 to b etc, you could simply do:
>>> dict(enumerate(L, start=1))
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
This works
>>> l = ['a', 'b', 'c', 'd']
>>> { x:(y+1) for (x,y) in zip(l, range(len(l))) }
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
Related
I have two lists:
common_nodes_list = ['A', 'A', 'B', 'C', 'C', 'C']
uniquePatterns = ['A', 'B', 'C']
I am trying to create a dict with the counts of each unique pattern. Like this:
A: 2
B: 1
C: 3
I have a for loop inside of another for loop:
patternRank = {}
for i in common_nodes_list:
score = 0
for pattern in uniquePatterns:
if pattern == i:
score += 1
patternRank[pattern]=score
patternRank
but It's only returning:
'C': 1
An alternative one line:
patternRank = {i: common_nodes_list.count(i) for i in uniquePatterns}
# {'A': 2, 'B': 1, 'C': 3}
You should do it the other way: for each pattern in the unique patterns, count how many there are in the common_nodes_list:
common_nodes_list = ['A', 'A', 'B', 'C', 'C', 'C']
unique_patterns = ['A', 'B', 'C']
pattern_rank = {}
for pattern in unique_patterns:
score = 0
for node in common_nodes_list:
if node == pattern:
score += 1
pattern_rank[pattern] = score
print(pattern_rank)
>> {'A': 2, 'B': 1, 'C': 3}
And maybe, try to be consistant with the way you name the variables: snake_case or CapWords.
Maybe it would be better:
pattern_count = {x: 0 for x in uniquePatterns}
for t in common_nodes_list:
pattern_count[t] += 1
Your solution will work if you do this modifications:
patternRank = {}
for i in common_nodes_list:
for pattern in uniquePatterns:
score = 0 if pattern not in patternRank.keys() else patternRank[pattern] # <-
if pattern == i:
score += 1
patternRank[pattern]=score # <- Tab
patternRank
we can use a for loop that iterates on the unique_patterns list. then using dict.update({key: value}).
common_nodes_list = ['A', 'A', 'B', 'C', 'C', 'C']
unique_patterns = ['A', 'B', 'C']
char_hist = {}
for char in unique_patterns:
char_hist.update({char: common_nodes_list.count(char)})
print(char_hist)
I want to get something input like:
a = ['a', 'b']
b = ['c', 'd']
and output like:
[{a:c, b:d},{a:d, b:c}, {a:c}, {b:c}, {a:d, b:d}]
how to write it in python?
the array length not always 2
I had resolved this by this code:
c = []
for i in range(len(a)):
d = []
for j in range(len(b)):
d.append({a[i]:b[j]})
if len(c) == 0:
c = d
else:
tmp = []
for k in range(len(c)):
for l in range(len(d)):
#print(c[k])
tmp.append(dict(**c[k], **d[l]))
c = tmp
This is one way using zip:
a = ['a', 'b']
b = ['c', 'd']
i = dict(zip(a, b))
j = dict(zip(a, b[::-1]))
lst = [i, j]
print(lst)
# [{'a': 'c', 'b': 'd'}, {'a': 'd', 'b': 'c'}]
Can you provide some more examples of the input. For this specific one I came up with this. It's a bad solution, so would like to know better.
def fun(inp1, inp2):
lst = []
for i in range(max(len(inp1), len(inp2))):
if i == 0:
dct = {inp1[i]: inp2[i], inp1[i+1]: inp2[i+1]}
lst.append(dct)
elif i == 1:
dct = {inp1[i-1]: inp2[i], inp1[i]: inp2[i-1]}
lst.append(dct)
return lst
a = ['a', 'b']
b = ['c', 'd']
print([{x: y} for x in a for y in b])
I think this should help.
Output: [{'a': 'c'}, {'a': 'd'}, {'b': 'c'}, {'b': 'd'}]
Here I have a code that initiates by a list, it takes two random letter and put them back into the main list. Then I count each letter from each generated list:
import random
import collections
def randMerge(l:list, count:int) -> list:
return l + [random.sample(l,k=count)]
def flatten(d):
return [i for b in [[c] if not isinstance(c, list) else flatten(c)
for c in d] for i in b]
num = 2
aList = ['A','B','C','D']
newList = aList[:]
for _ in range(3):
newList = randMerge(newList,num)
print(newList)
new_counts = collections.Counter(flatten(newList))
print(new_counts)
which gives:
['A', 'B', 'C', 'D', ['A', 'C']]
Counter({'A': 2, 'C': 2, 'B': 1, 'D': 1})
['A', 'B', 'C', 'D', ['A', 'C'], ['D', 'A']]
Counter({'A': 3, 'C': 2, 'D': 2, 'B': 1})
['A', 'B', 'C', 'D', ['A', 'C'], ['D', 'A'], ['A', 'B']]
Counter({'A': 4, 'B': 2, 'C': 2, 'D': 2})
Now I wonder how can I make a dataframe such that each column the numbers in counters and the row will be representing the letters. I did this:
df = pandas.DataFrame.from_dict(new_counts, orient='index')
yet this gives me only the last Counter. Also how can I make a histogram of each Counter and show them together?
If you need each column to represent the contents of a Counter object, you can create your dataframe from the list of objects, then transpose.
import collections, random, pandas as pd
def randMerge(l:list, count:int) -> list:
return l + [random.sample(l,k=count)]
def flatten(d):
return [i for b in [[c] if not isinstance(c, list) else flatten(c)
for c in d] for i in b]
num = 2
aList = ['A','B','C','D']
res = []
newList = aList[:]
for _ in range(3):
newList = randMerge(newList,num)
new_counts = collections.Counter(flatten(newList))
res.append(new_counts)
print(res)
# [Counter({'A': 2, 'C': 2, 'B': 1, 'D': 1}),
# Counter({'C': 3, 'A': 2, 'D': 2, 'B': 1}),
# Counter({'C': 4, 'A': 2, 'B': 2, 'D': 2})]
df = pd.DataFrame(res).T
print(df)
# 0 1 2
# A 2 2 2
# B 1 1 1
# C 2 3 5
# D 1 2 3
So I have a list of possible elments:
elems = ['a', 'b', 'c', 'd']
Then I have another list of random items chosen from the first list.
Something like (for example):
items = ['a', 'a', 'c', 'a', 'c', 'd']
What is the Pythonic way to find out which element in elems is contained least often in items?
In this example it is 'b', because that's not contained in items at all.
Short
print(min(elems, key=items.count)) # b
Relatively short and efficient (only use it if you're sure there're no duplicates in elems)
from collections import Counter
c = Counter(items + elems)
print(c.most_common()[-1][0]) # b
Just efficient
d = {x: 0 for x in elems}
for x in items:
d[x] += 1
print(min(d, key=d.get)) # b
Another "just efficient"
from collections import defaultdict
d = defaultdict(int)
for x in items:
d[x] += 1
print(min(elems, key=d.__getitem__))
# or print(min(elems, key=lambda x: d[x])) - gives same result
>>> from collections import Counter
>>> elems = ['a', 'b', 'c', 'd']
>>> items = ['a', 'a', 'c', 'a', 'c', 'd']
>>> c = Counter(dict.fromkeys(elems, 0))
>>> c.update(Counter(items))
>>> c
Counter({'a': 3, 'c': 2, 'd': 1, 'b': 0})
>>> min(c, key=c.get)
'b'
The fastest way is to make a dictionary with counts in it:
aDict = { k:0 for k in elems }
for x in items:
if x not in aDict: aDict[x] = 0
aDict[x] += 1
print min(aDict, key=aDict.get)
x = [['a', 'b', 'c'], ['a', 'c', 'd'], ['e', 'f', 'f']]
Let's say we have a list with random str letters.
How can i create a function so it tells me how many times the letter 'a' comes out, which in this case 2. Or any other letter, like 'b' comes out once, 'f' comes out twice. etc.
Thank you!
You could flatten the list and use collections.Counter:
>>> import collections
>>> x = [['a', 'b', 'c'], ['a', 'c', 'd'], ['e', 'f', 'f']]
>>> d = collections.Counter(e for sublist in x for e in sublist)
>>> d
Counter({'a': 2, 'c': 2, 'f': 2, 'b': 1, 'e': 1, 'd': 1})
>>> d['a']
2
import itertools, collections
result = collections.defaultdict(int)
for i in itertools.chain(*x):
result[i] += 1
This will create result as a dictionary with the characters as keys and their counts as values.
Just FYI, you can use sum() to flatten a single nested list.
>>> from collections import Counter
>>>
>>> x = [['a', 'b', 'c'], ['a', 'c', 'd'], ['e', 'f', 'f']]
>>> c = Counter(sum(x, []))
>>> c
Counter({'a': 2, 'c': 2, 'f': 2, 'b': 1, 'e': 1, 'd': 1})
But, as Blender and John Clements have addressed, itertools.chain.from_iterable() may be more clear.
>>> from itertools import chain
>>> c = Counter(chain.from_iterable(x)))
>>> c
Counter({'a': 2, 'c': 2, 'f': 2, 'b': 1, 'e': 1, 'd': 1})