Creating directed graph from dictionary with scores - python

I have this dictionary:
d1={
'a':['b','c','b'],
'b':['a','d','e']
}
it is sort of a directed graph. For example, d1['a'] points twice to 'b', and once to 'c' (see graph below)
What I want is to create two dictionaries out of d1 - pointing_to and pointed_by with values describing how many times they are pointing to or pointed by, respectively.
pointing_to={
'a':{'b':2,'c':1},
'b':{'a':1,'d':1,'e':1},
}
pointed_by={
'a':{'b':1},
'b':{'a':2},
'c':{'a':1},
'd':{'b':1},
'e':{'b':1}
}

You can use some collections utils to get to your output:
from collections import Counter, defaultdict
d1 = {'a': ['b', 'c', 'b'], 'b': ['a', 'd', 'e']}
pointed_to = {k: Counter(v) for k, v in d1.items()}
pointed_from = defaultdict(dict)
for k, v in pointed_to.items():
for k_, v_ in v.items():
pointed_from[k_][k] = v_
# pointed_to
{'a': Counter({'b': 2, 'c': 1}),
'b': Counter({'d': 1, 'a': 1, 'e': 1})}
# pointed_from
defaultdict(<class 'dict'>, {'d': {'b': 1},
'a': {'b': 1},
'c': {'a': 1},
'b': {'a': 2},
'e': {'b': 1}})
Note that both Counter and deafultdict are subclasses of dict, so these two can, for all intents and purposes, be used as your desired output dicts.
If you really want dict objects, you can easily do that:
pointed_to = {k: dict(v) for k, v in pointed_to.items()}
pointed_from = dict(pointed_from)

Related

Is there any way to change the keys (not Values) in the dictionary in Python?

I want to change just Keys (not Values) in a Dictionary in Python. Is there any way to do that?
You can pop the value of the old key and reassign:
d = {'A': 1, 'B': 2, 'C': 3}
d['b'] = d.pop('B')
print(d)
# {'A': 1, 'C': 3, 'b': 2}
Note that this won't maintain the order of the keys (python 3.6+). The renamed key will be instead at the end.
maintaining order
If order is important you need to create a new dictionary
d = {'A': 1, 'B': 2, 'C': 3}
rename = {'B': 'b', 'A': 'a'}
d = {rename.get(k, k): v for k,v in d.items()}
print(d)
# {'a': 1, 'b': 2, 'C': 3}
in place modification while maintaining order
If you want to modify the dictionary in place (i.e. not creating a new object), you need to pop and reinsert all keys in order:
d = {'A': 1, 'B': 2, 'C': 3}
rename = {'B': 'b', 'A': 'a'}
keys = list(d)
for k in keys:
d[rename.get(k, k)] = d.pop(k)
print(d)
{'a': 1, 'b': 2, 'C': 3}

python list of dicts - convert each key-value as a individual dict

with a list like below that has one or more dicts
l = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'} } ]
need to extract each key-value pair as an individual dict
Expected output
[{'b': 'h'}, {'c': (1,2)}, {'d': [0, 1]}, {'e': {'f': 2, 'g': 'i'} } ]
I have been trying to do this via list comprehension - the outer comprehension could be something like [ {k,v} for k, v in ?? - need some help in getting the inner comprehension.
I believe this is what you're looking for - except that the order of the elements might be different, but that's to be expected when dealing with dictionaries:
lst = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'}}]
[{k: v} for d in lst for k, v in d.items()]
=> [{'c': (1, 2)}, {'b': 'h'}, {'e': {'g': 'i', 'f': 2}}, {'d': [0, 1]}]
This should work:
[{k: v} for i in l for k, v in i.items()]

Creating a function that filters a nest dictionary by asking certain values

I am a beginner in python trying to create a function that filters through my nested dictionary through by asking multiple values in a dictionary like
filtered_options = {'a': 5, 'b': "Cloth'}
For my dictionary
my_dict = {1.0:{'a': 1, 'b': "Food', 'c': 500, 'd': 'Yams'},
2.0:{'a': 5, 'v': "Cloth', 'c': 210, 'd': 'Linen'}}
If I input my dictionary in the filter function with such options I should get something that looks like
filtered_dict(my_dict, filtered_options = {'a': 5, 'b': "Cloth'})
which outputs the 2nd key and other keys with the same filtered options in my dictionary.
This should do what you want.
def dict_matches(d, filters):
return all(k in d and d[k] == v for k, v in filters.items())
def filter_dict(d, filters=None):
filters = filters or {}
return {k: v for k, v in d.items() if dict_matches(v, filters)}
Here's what happens when you test it:
>>> filters = {'a': 5, 'b': 'Cloth'}
>>> my_dict = {
... 1.0: {'a': 1, 'b': 'Food', 'c': 500, 'd': 'Yams'},
... 2.0: {'a': 5, 'b': 'Cloth', 'c': 210, 'd': 'Linen'}
... }
>>> filter_dict(my_dict, filters)
{2.0: {'b': 'Cloth', 'a': 5, 'd': 'Linen', 'c': 210}}
You can do this :
import operator
from functools import reduce
def multi_level_indexing(nested_dict, key_list):
"""Multi level index a nested dictionary, nested_dict through a list of keys in dictionaries, key_list
"""
return reduce(operator.getitem, key_list, nested_dict)
def filtered_dict(my_dict, filtered_options):
return {k : v for k, v in my_dict.items() if all(multi_level_indexing(my_dict, [k,f_k]) == f_v for f_k, f_v in filtered_options.items())}
So that:
my_dict = {1.0:{'a': 1, 'b': 'Food', 'c': 500, 'd': 'Yams'},
2.0:{'a': 5, 'b': 'Cloth', 'c': 210, 'd': 'Linen'}}
will give you:
print(filtered_dict(my_dict, {'a': 5, 'b': 'Cloth'}))
# prints {2.0: {'a': 5, 'b': 'Cloth', 'c': 210, 'd': 'Linen'}}

Update list value in list of dictionaries

I have a list of dictionaries (much like in JSON). I want to apply a function to a key in every dictionary of the list.
>> d = [{'a': 2, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
# Desired value
[{'a': 200, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}]
# If I do this, I can only get the changed key
>> map(lambda x: {k: v * 100 for k, v in x.iteritems() if k == 'a'}, d)
[{'a': 200}, {'a': 100}, {'a': 100}, {'a': 100}]
# I try to add the non-modified key-values but get an error
>> map(lambda x: {k: v * 100 for k, v in x.iteritems() if k == 'a' else k:v}, d)
SyntaxError: invalid syntax
File "<stdin>", line 1
map(lambda x: {k: v * 100 for k, v in x.iteritems() if k == 'a' else k:v}, d)
How can I achieve this?
EDIT: 'a' and 'b' are not the only keys. These were selected for demo purposes only.
Iterate through the list and update the desired dict item,
lst = [{'a': 2, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
for d in lst:
d['a'] *= 100
Using list comprehension will give you speed but it will create a new list and n new dicts, It's useful if you don't wanna mutate your list, here it is
new_lst = [{**d, 'a': d['a']*100} for d in lst]
In python 2.X we can't use {**d} so I built custom_update based on the update method and the code will be
def custom_update(d):
new_dict = dict(d)
new_dict.update({'a':d['a']*100})
return new_dict
[custom_update(d) for d in lst]
If for every item in the list you want to update a different key
keys = ['a', 'b', 'a', 'b'] # keys[0] correspond to lst[0] and keys[0] correspond to lst[0], ...
for index, d in enumerate(lst):
key = keys[index]
d[key] *= 100
using list comprehension
[{**d, keys[index]: d[keys[index]] * 100} for index, d in enumerate(lst)]
In python 2.x the list comprehension will be
def custom_update(d, key):
new_dict = dict(d)
new_dict.update({key: d[key]*100})
return new_dict
[custom_update(d, keys[index]) for index, d in enumerate(lst)]
You can use your inline conditionals (ternaries) in a better location within a comprehension:
>>> d = [{'a': 2, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
>>> d2 = [{k: v * 100 if k == 'a' else v for k, v in i.items()} for i in d]
>>> d2
[{'a': 200, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}]
Your map() call is close to working, you just need to change the order of your dict comprehension, and turn else k:v into else v:
>>> d = [{'a': 2, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
>>> list(map(lambda x: {k: v * 100 if k == 'a' else v for k, v in x.items()}, d))
[{'a': 200, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}]
If you are using a function, you may want to provide a target key and corresponding value:
d = [{'a': 2, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
f = lambda new_val, d1, key='a': {a:b*new_val if a == key else b for a, b in d1.items()}
new_d = list(map(lambda x:f(100, x), d))
Output:
[{'a': 200, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}]
After your edit of "'a' and 'b' are not the only keys. These were selected for demo purposes only", here is a very simple map function that alters only the value of 'a', and leaves the rest as is:
map(lambda x: x.update({'a': x['a']*100}), d)
My original answer was:
I think the simplest and most appropriate way of this is iterating in d and utilizing the fact that each item in d is a dictionary that has keys 'a' and 'b':
res = [{'a':e['a']*100, 'b':e['b']} for e in d]
Result:
[{'a': 200, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}, {'a': 100, 'b': 2}]

Is there a way to group several keys which points to the same value in a dictionary?

Is there a way to group several keys which points to the same value in a dictionary?
from collections import defaultdict
newdict = defaultdict(list)
for k,v in originaldict.items():
newdict[v].append(k)
Not exactly sure you want the result structured, but here's one guess:
from collections import defaultdict
mydict = {'a': 1, 'b': 2, 'c': 3, 'd': 2, 'e': 4, 'f': 2, 'g': 4}
tempdict = defaultdict(list)
for k,v in mydict.iteritems():
tempdict[v].append(k)
groupedkeysdict = {}
for k,v in tempdict.iteritems():
groupedkeysdict[tuple(v) if len(v)>1 else v[0]] = k
print groupedkeysdict
# {'a': 1, 'c': 3, ('e', 'g'): 4, ('b', 'd', 'f'): 2}

Categories

Resources