How to get all combination from two lists? - python

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'}]

Related

nested for loop not counting correctly

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)

How to find a single value in a nested list (and its position) with minimal iterations?

Say I have the following nested list:
my_list = [
['a', 'b', 'c', 'd'],
['a', 'c', 'd'],
['b', 'c', 'd', 'e'],
['a', 'c'],
]
I want to find any values that appear only once in the entire list, and also their position in the parent list. For example, 'e' appears only once, and it's in my_list[2].
The only ways I can think of doing this involve at least three iterations. For example:
for letter in ['a', 'b', 'c', 'd', 'e']: # Iteration 1
count = 0
for child_list in my_list: # Iteration 2
if letter in child_list:
count += 1
if count == 1:
print(letter)
for i in range(len(my_list)): # Iteration 3
if letter in my_list[i]:
print(i)
or:
counter = {}
for i in range(len(my_list)): # Iteration 1
for letter in my_list[i]: # Iteration 2
if letter not in counter:
# Store the count and (first) position of each letter
counter[letter] = [1, i]
else:
counter[letter][0] += 1
for letter in counter: # Iteration 3
if counter[letter][0] == 1:
print(letter)
print(counter[letter][1])
Is there a way of doing this that doesn't require so many iterations?
Here's a way to do what your question asks using a python set and dict:
my_list = [
['a', 'b', 'c', 'd'],
['a', 'c', 'd'],
['b', 'c', 'd', 'e'],
['a', 'c'],
]
visited = set()
singletons = {}
for i, L in enumerate(my_list):
for v in L:
if v not in visited:
visited.add(v)
singletons[v] = i
else:
singletons.pop(v, None)
print(singletons)
Output:
{'e': 2}
Here is a simple way with a single iteration (i.e. each element is visited only once) that creates a dictionary of {letter: [count, first position]}:
d = {}
for i, lst in enumerate(my_list):
for item in lst:
if item in d:
d[item][0] +=1
else:
d[item] = [1, i]
output:
{'a': [3, 0], 'b': [2, 0], 'c': [4, 0], 'd': [3, 0], 'e': [1, 2]}
Interesting problem, not sure if this is what you are after but here it is:
x = 0
y = 0
output = {}
duplicates = []
while x < len(my_list):
if y < len(my_list[x]):
value = my_list[x][y]
y = y + 1
if value in output:
del output[value]
duplicates.append(value)
elif not value in output and not value in duplicates:
output[value] = x
else:
y = 0
x = x + 1
What you get is:
{'e': 2}
It is a single loop but work involved is probably comparable to running two nested loops.

Making a table and a histogram from various counters

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

Python: Find the list element that is contained least times in another list

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)

Python dictionary comprehension example

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}

Categories

Resources