Nested list comprehension on python - python

I'm a beginner in python and I want to use comprehension to create a dictionary. Let's say I have the below two list and want to convert them to a dictionary like {'Key 1':['c','d'], 'Key 2':['a','f'], 'Key 3':['b','e']}. I can only think of the code below and I don't know how to change the value of the key and the filter using comprehension. How should I change my code?
value = ['a','b','c','d','e','f']
key = [2, 3, 1, 1, 3, 2]
{"Key 1" : [value for key,value in list(zip(key,value)) if key==1]}

This should do it:
value = ['a','b','c','d','e','f']
key = [2, 3, 1, 1, 3, 2]
answer = {}
for k, v in zip(key, value):
if k in answer:
answer[k].append(v)
else:
answer[k] = [v]
print(answer)
{2: ['a', 'f'], 3: ['b', 'e'], 1: ['c', 'd']}
EDIT: oops, jumped the gun. Apologies.
Here's the comprehension version, but it's not very efficient:
{
k: [v for i, v in enumerate(value) if key[i] == k]
for k in set(key)
}
EDIT 2:
Here's an one that has better complexity:
import pandas as pd
series = pd.Series(key)
{
k: [value[i] for i in indices]
for k, indices in series.groupby(series).groups.items()
}

You could do it with dictionary comprehension and list comprehension:
{f"Key {k}" : [value for key,value in zip(key,value) if key == k] for k in key}
Your lists would yield the following:
{'Key 2': ['a', 'f'], 'Key 3': ['b', 'e'], 'Key 1': ['c', 'd']}
As requested.

use dict setdefault
value = ['a', 'b', 'c', 'd', 'e', 'f']
key = [2, 3, 1, 1, 3, 2]
d = {}
{d.setdefault(f'Key {k}', []).append(v) for k, v in zip(key, value)}
print(d)
output
{'Key 2': ['a', 'f'], 'Key 3': ['b', 'e'], 'Key 1': ['c', 'd']}

Usually, it is written as an explicit loop (O(n) solution):
>>> letters = 'abcdef'
>>> digits = [2, 3, 1, 1, 3, 2]
>>> from collections import defaultdict
>>> result = defaultdict(list) # digit -> letters
>>> for digit, letter in zip(digits, letters):
... result[digit].append(letter)
>>> result
defaultdict(<class 'list'>, {2: ['a', 'f'], 3: ['b', 'e'], 1: ['c', 'd']})
Nested comprehensions (O(n n) solution) like in other answers:
>>> {
... digit: [letter for d, letter in zip(digits, letters) if digit == d]
... for digit in set(digits)
... }
{1: ['c', 'd'], 2: ['a', 'f'], 3: ['b', 'e']}
If you need to write it as a single dict comprehension, itertools.groupby could be used (O(n log n) solution):
>>> from itertools import groupby
>>> {
... digit: [letter for _, letter in group]
... for digit, group in groupby(
... sorted(zip(digits, letters), key=lambda x: x[0]),
... key=lambda x: x[0]
... )
... }

Related

Unable to unpack (loop iterate) for all keys with related pairs (values)

I know that when creating dict, the key(s) must be unique, but each key can have multiple values,
take for example:
This is the dict:
d = {1:a, 2:[a,b,c], 3:[e,f,g]}
What I want to do is to use for loop in python to extract the keys and associated values and turn all into list results, so that I can then make two parallel lists:
Expect result for key:
listA: [1,2,2,2,3,3,3]
Expect result for values:
listB: [a,a,b,c,e,f,g]
For list B, I can do the code to finish it:
listkey = [] #assign a list container
listvalue = [] #assign a list container
for k,v in d.items():
listkey.append(k)
listvalue.append(v)
listB = [ item for elem in listvalue for item in elem]
listB result success: [a,a,b,c,e,f,g]
However, for listA,
listA = [ item for elem1 in listvalue for item in elem1]
it just results [1,2,3],but not [1,2,2,2,3,3,3]
I am wondering what's wrong with my code. Can I achieve this by using for loop?
Your sample input is inconsistent (one of the dictionary value is not a list). If it were consistent you could use list comprehensions with nested for loops to get the two lists:
d = {1:['a'], 2:['a','b','c'], 3:['e','f','g']}
listA = [ k for k,v in d.items() for _ in range(len(v)) ]
listB = [ c for v in d.values() for c in v ]
print(listA) # [1, 2, 2, 2, 3, 3, 3]
print(listB) # ['a', 'a', 'b', 'c', 'e', 'f', 'g']
The list comprehensions are equivalent to nested for loops:
# [ k for k,v in d.items() for _ in range(len(v)) ]
listA = []
for k,v in d.items():
for _ in range(len(v)):
listA.append(k)
# [ c for v in d.values() for c in v ]
listB = []
for v in d.values():
for c in v:
listB.append(v)
It is often simpler to handle the values of the same type in a dict. So I have converted the first value into a list in this answer.
You can use nested comprehension and a zip:
d = {1: ['a'], 2: ['a', 'b', 'c'], 3: ['e', 'f', 'g']}
key_value = ((k, v) for k, l in d.items() for v in l)
a, b = zip(*key_value)
print(a) # (1, 2, 2, 2, 3, 3, 3)
print(b) # ('a', 'a', 'b', 'c', 'e', 'f', 'g')
If you do want a and b as lists, you can use instead:
a, b = map(list, zip(*key_value))
# [1, 2, 2, 2, 3, 3, 3]
# ['a', 'a', 'b', 'c', 'e', 'f', 'g']
EDIT: using plain nested for loop:
d = {1: ['a'], 2: ['a', 'b', 'c'], 3: ['e', 'f', 'g']}
a, b = [], []
for key, lst in d.items():
for val in lst:
a.append(key)
b.append(val)
print(a) # [1, 2, 2, 2, 3, 3, 3]
print(b) # ['a', 'a', 'b', 'c', 'e', 'f', 'g']
Simple solution :
In the time that you want add the value use list.extend instead of append:
a = [ 1, 2, 3 ]
b = [ 4, 5, 6 ]
a.extend(b)
The result it that a will put b elemnts in itself. :)

I need to take a dictionary as a parameter for a function and then invert it. The hard part being is that my dictionary has lists as the values

Here are my lists:
a = {'A':'red', 'B':'blue', 'C':'cyan'}
b = {'A':[1,1,2,4], 'B':[1,2,5]}
Here is the code I have for inversion of the dictionary:
def again(dict):
tuple(value for value in dict)
result_dict = {}
for key, value in dict.items():
if not value in result_dict.keys():
result_dict[value] = []
result_dict[value].append(key)
return result_dict
The function works for dictionary labeled "a" but does not work for dictionary "b". This is because dictionary b has an unhashable data type (i.e. a list).
Running the function:
again(a)
output --> {'red': ['A'], 'blue': ['B'], 'cyan': ['C']}
I am trying to take dictionary "b" and invert it just like with dictionary "a", but I have no clue how to account for the list values with multiple integers, e.g.:
{'A' : [1, 1, 2, 4}, 'B' : [1, 2, 5]}
would return you:
{1 : ['A', 'B'], 2 : ['A', 'B'], 4 : ['A'], 5 : ['B']}
You need another for loop to iterate over the value if it is a list.
def again(d):
result = dict()
for key, value in d.items():
if isinstance(value, list):
for num in value:
if not num in result:
result[num] = list()
if key not in result[num]:
result[num].append(key)
else:
if not value in result:
result[value] = list()
result[value].append(key)
return result
>>> again({'A':'red', 'B':'blue', 'C':'cyan'})
{'red': ['A'], 'blue': ['B'], 'cyan': ['C']}
>>> again({'A':[1,1,2,4], 'B':[1,2,5]})
{1: ['A', 'B'], 2: ['A', 'B'], 4: ['A'], 5: ['B']}
Using defaultdict with if-else condition to check if dictionary value is a list:
from collections import defaultdict
def again(d):
res = defaultdict(list)
for k,v in d.items():
if isinstance(v,list):
temp = list(set([(x,k) for x in v]))
for x,y in temp:
res[x].append(y)
else:
res[v].append(k)
return dict(res)
print(again({'A':'red', 'B':'blue', 'C':'cyan'}))
print(again({'A':[1,1,2,4], 'B':[1,2,5]}))
Output:
{'red': ['A'], 'blue': ['B'], 'cyan': ['C']}
{1: ['A', 'B'], 2: ['A', 'B'], 4: ['A'], 5: ['B']}

Python - dict of lists - for each unique list element find the associated keys [duplicate]

This question already has answers here:
Inverting a dictionary with list values
(6 answers)
Closed 1 year ago.
Say I have a dictionary like this
D = {'a': [1,2,3], 'b': [2,3,4], 'c': [3,4,5]}
For each unique element present across all the lists, I want to find the associated dict keys.
The wanted output is thus:
out = {1: ['a'], 2: ['a', 'b'], 3: ['a', 'b', 'c'], 4: ['b', 'c'], 5: ['c']}
How do I do this most efficiently?
EDIT: I need to do this for a large dict with ~100 keys and each list between 50-10000 elements
You can use a collections.defaultdict:
from collections import defaultdict
out = defaultdict(list)
for k, vals in D.items():
for val in vals:
out[val].append(k)
out
# defaultdict(<class 'list'>, {1: ['a'], 2: ['a', 'b'], 3: ['a', 'b', 'c'], 4: ['b', 'c'], 5: ['c']})
Alternatively, use dict.setdefault:
out = {}
for k, vals in D.items():
for val in vals:
out.setdefault(val, []).append(k)
You could try this dictionary comprehension with a list comprehension:
>>> {val: [k for k in D.keys() if val in D[k]] for v in D.values() for val in v}
{1: ['a'], 2: ['a', 'b'], 3: ['a', 'b', 'c'], 4: ['b', 'c'], 5: ['c']}
>>>
Or you could try this for loop:
res = {}
for k, val in D.items():
for v in val:
if v in res:
res[v].append(k)
else:
res[v] = [k]
print(res)
Output:
{1: ['a'], 2: ['a', 'b'], 3: ['a', 'b', 'c'], 4: ['b', 'c'], 5: ['c']}
Use get function:
for i,j in D.items():
for k in j:
out[k] = out.get(k,[])+[i]

Python - count and group items in list stored in dictionary

I have seen examples on how to count items in dictionary or list. My dictionary stored multiple lists. Each list stores multiple items.
d = dict{}
d = {'text1': ['A', 'C', 'E', 'F'],
'text2': ['A'],
'text3': ['C', 'D'],
'text4': ['A', 'B'],
'text5': ['A']}
1. I want to count frequency of each alphabet, i.e. the results should be
A - 4
B - 1
C - 2
D - 1
E - 1
F - 1
2. I want to have group by each alphabet, i.e. the results should be
A - text1, text2, text4, text5
B - text4
C - text1, text3
D - text3
E - text1
F - text1
How can I achieve both by using some Python existing libraries without using many for loops?
To get to (2), you would have to first invert the keys and values of a dictionary, and store them in a list. Once you are there, use groupby with a key to get to the structure of (2).
from itertools import groupby
arr = [(x,t) for t, a in d.items() for x in a]
# [('A', 'text2'), ('C', 'text3'), ('D', 'text3'), ('A', 'text1'), ('C', 'text1'), ('E', 'text1'), ('F', 'text1'), ('A', 'text4'), ('B', 'text4'), ('A', 'text5')]
res = {g: [x[1] for x in items] for g, items in groupby(sorted(arr), key=lambda x: x[0])}
#{'A': ['text1', 'text2', 'text4', 'text5'], 'C': ['text1', 'text3'], 'B': ['text4'], 'E': ['text1'], 'D': ['text3'], 'F': ['text1']}
res2 = {x: len(y) for x, y in res.items()}
#{'A': 4, 'C': 2, 'B': 1, 'E': 1, 'D': 1, 'F': 1}
PS: I am hoping you'd meaningful variable names in your real code.
There are a few ways to accomplish this, but if you'd like to handle things without worrying about import ing additional modules or installing and importing external modules, this method will work cleanly 'out of the box.'
With d as your starting dictionary:
d = {'text1': ['A', 'C', 'E', 'F'],
'text2': ['A'],
'text3': ['C', 'D'],
'text4': ['A', 'B'],
'text5': ['A']}
create a new dict, called letters, for your results to live in, and populate it with your letters, taken from d.keys(), by creating the letter key if it isn't present, and creating a list with the count and the key from das it's value. If it's already there, increment the count, and append the current key from d to it's d key list in the value.
letters = {}
for item in d.keys():
for letter in d[item]:
if letter not in letters.keys():
letters[letter] = [1,[item]]
else:
letters[letter][0] += 1
letters[letter][1] += [item]
This leaves you with a dict called letters containing values of the counts and the keys from d that contain the letter, like this:
{'E': [1, ['text1']], 'C': [2, ['text3', 'text1']], 'F': [1, ['text1']], 'A': [4, ['text2', 'text4', 'text1', 'text5']], 'B': [1, ['text4']], 'D': [1, ['text3']]}`
Now, to print your first list, do:
for letter in sorted(letters):
print(letter, letters[letter][0])
printing each letter and the contents of the first, or 'count' index of the list as its value, and using the built-in sorted() function to put things in order.
To print the second, likewise sorted(), do the same, but with the second, or 'key', index of the list in its value, .joined using a , into a string:
for letter in sorted(letters):
print(letter, ', '.join(letters[letter][1]))
To ease Copy/Paste, here's the code unbroken by my ramblings:
d = {'text1': ['A', 'C', 'E', 'F'],
'text2': ['A'],
'text3': ['C', 'D'],
'text4': ['A', 'B'],
'text5': ['A']}
letters = {}
for item in d.keys():
for letter in d[item]:
if letter not in letters.keys():
letters[letter] = [1,[item]]
else:
letters[letter][0] += 1
letters[letter][1] += [item]
print(letters)
for letter in letters:
print(letter, letters[letter][0])
print()
for letter in letters:
print(letter, ', '.join(letters[letter][1]))
Hope this helps!
from collections import Counter, defaultdict
from itertools import chain
d = {'text1': ['A', 'C', 'E', 'F'],
'text2': ['A'],
'text3': ['C', 'D'],
'text4': ['A', 'B'],
'text5': ['A']}
counter = Counter(chain.from_iterable(d.values()))
group = defaultdict(list)
for k, v in d.items():
for i in v:
group[i].append(k)
out:
Counter({'A': 4, 'B': 1, 'C': 2, 'D': 1, 'E': 1, 'F': 1})
defaultdict(list,
{'A': ['text2', 'text4', 'text1', 'text5'],
'B': ['text4'],
'C': ['text1', 'text3'],
'D': ['text3'],
'E': ['text1'],
'F': ['text1']})
For your first task:
from collections import Counter
d = {
'text1': ['A', 'C', 'E', 'F'],
'text2': ['A'],
'text3': ['C', 'D'],
'text4': ['A', 'B'],
'text5': ['A']
}
occurrences = Counter(''.join(''.join(values) for values in d.values()))
print(sorted(occurrences.items(), key=lambda l: l[0]))
Now let me explain it:
''.join(values) turns the list (e.g. ['A', 'B', 'C', 'D'] into 'ABCD')
Then you join each list from the dictionary into one string (the outer ''.join())
Counter is a class from the builtin package collections, which simply counts the elements in the iterable (string in this case) and reproduces them as tuples of (key, value) pairs (e.g. ('A', 4))
Finally, I sort the Counter items (it's just like a dictionary) alphabetically (key=lambda l: l[0] where l[0] is the letter from the (key, value) pair.
As I saw, you already have the solution for your second problem.
from collections import defaultdict
alphabets = defaultdict(list)
his is a way to acheive this:
for text, letters in d.items():
for letter in letters:
alphabets[letter].append(text)
for letter, texts in sorted(alphabets.items()):
print(letter, texts)
for letter, texts in sorted(alphabets.items()):
print(letter, len(texts))
note that if you have A - text1, text2, text4, text5 to get to A - 4 is just a matter of counting the texts.

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