Hi can anyone explain why we have the following behavior:
>>> k = [0.5, 1, 2]
>>> m = [0.5, 1, 2]
>>> dict1 = dict.fromkeys(k, dict.fromkeys(m, 0))
>>> dict1
{0.5: {0.5: 0, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
>>> dict1[0.5][0.5]= 4.5
>>> dict1
{0.5: {0.5: 4.5, 1: 0, 2: 0}, 1: {0.5: 4.5, 1: 0, 2: 0}, 2: {0.5: 4.5, 1: 0, 2: 0}}
>>> dict2 = {0.5: {0.5: 0, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
>>> dict2[0.5][0.5] = 4.5
>>> dict2
{0.5: {0.5: 4.5, 1: 0, 2: 0}, 1: {0.5: 0, 1: 0, 2: 0}, 2: {0.5: 0, 1: 0, 2: 0}}
so in the first case whenever I try to change a value of dict1 all the values with the same second key are changing (e.g dict1[0.5][0.5]=4.5 will also change dict1[1][0.5] for some reason).
I haven't found a good dupe target, although this general issue is common:
Because dict.fromkeys(m, 0) runs once when the function is called, and then puts the same dictionary in every value for dict.fromkeys(k, dict.fromkeys(m, 0)). You can check this by doing for dct in dict1: print id(dict1[dct]) they will all be the same id, eg. the same object:
>>> for dct in dict1: print id(dict1[dct])
140401847695352
140401847695352
140401847695352
This happens because parameters are only evaluated once, when the function is called... so dict.fromkeys will put the same value (whatever that may be) in every key. It is equivalent to:
default_dct = dict.fromkeys(m, 0)
dict1 = dict.fromkeys(k, default_dct)
Instead, you can use a dictionary comprehension:
dict1 = {key: {ikey: 0 for ikey in m} for key in k}
or, via copying:
inner_dct = dict.fromkeys(m, 0)
dict1 = {}
for key in k:
dict1[key] = inner_dct.copy()
Both of which yield different values:
>>>for dct in dict1: print id(dict1[dct])
140401847695352
140401847646200
140401847644240
Only one sub-dictionary was ever created. What you have now is that dictionary itself accessible in multiple ways.
dict[0.5] and dict[1] refer to the same dictionary (and not copies of it).
One way to achieve what you want to do is to use a dict comprehension:
dict1 = {k_outer: {k_inner:0 for k_inner in m} for k_outer in k}
This creates a new nested dict for each key, thereby avoiding the problem of them all accessing the same nested dict.
Related
I'm doing this in Python 3.8 and I havent been able to fine a clean solution.
I am trying to remove any values from a nested dictionary if the value is equal to 0.
So the nested dictionary kinda looks like this:
{1: {"alert1" : 0, "alert2": 1, "alert3" : 3, "alert4": 1},
2: {"alert1" : 45, "alert2": 2, "alert3" : 0, "alert4": 54},
3: {"alert1" : 2, "alert2": 1, "alert3" : 33, "alert4": 11},
4: {"alert1" : 1, "alert2": 0, "alert3" : 2, "alert4": 0}}
And so if I want to print it or view it but only if an alert is not 0
so print(nested_dic) would look like this.
1 - alert2: 1 alert3: 3 alert4: 1
2 - alert1: 45 alert2: 2 alert4: 54
3 - alert1: 2 alert2: 1 alert3: 33 alert3: 11
4 - alert1: 1 alert3: 2
EDIT: Either delete the non zeroes or save a new dictionary without zeroes to be printed out.
If you only have one level of nesting I think using a comprehension inside a comprehension is still readable, so I would suggest this (d is your dictionary here):
>>> {k: {k_sub: v_sub for k_sub, v_sub in v.items() if v_sub != 0} for k, v in d.items()}
{1: {'alert2': 1, 'alert3': 3, 'alert4': 1},
2: {'alert1': 45, 'alert2': 2, 'alert4': 54},
3: {'alert1': 2, 'alert2': 1, 'alert3': 33, 'alert4': 11},
4: {'alert1': 1, 'alert3': 2}}
But maybe you prefer code that is more detailed and add more names so it's easier to read:
def non_zero(alerts):
return {code: value for code, value in alerts.items() if value != 0}
all_alerts = {1: {"alert1" : 0, "alert2": 1, "alert3" : 3, "alert4": 1},
2: {"alert1" : 45, "alert2": 2, "alert3" : 0, "alert4": 54},
3: {"alert1" : 2, "alert2": 1, "alert3" : 33, "alert4": 11},
4: {"alert1" : 1, "alert2": 0, "alert3" : 2, "alert4": 0}}
all_non_zero_alerts = {identifier: non_zero(alerts) for identifier, alerts in all_alerts.items()}
This does the exact same thing as before but it may be a bit more clear what the code is achieving.
My solution is to use a list comprehension. This is best for iterating and removing any unwanted items. Here's an example of how this would work:
dic = {1: {"alert1" : 0, "alert2": 1, "alert3" : 3, "alert4": 1},
2: {"alert1" : 45, "alert2": 2, "alert3" : 0, "alert4": 54},
3: {"alert1" : 2, "alert2": 1, "alert3" : 33, "alert4": 11},
4: {"alert1" : 1, "alert2": 0, "alert3" : 2, "alert4": 0}}
for key in dic:
value = dic[key]
keys = {k:v for (k, v) in value.items() if v != 0}
print(keys)
Here, the only filtering you're doing happens on that for iteration loop, where you loop through all keys if the value isn't 0.
This solution is preferable since it uses the Pythonic dictionary comprehension, which allows you to neatly iterate over each items in the individual dict in a simple line. Relative to loops, this is a more beneficial approach since Python can easily optimize list comprehensions to improve efficiency.
Variations
If you want to modify the existing dictionary, you can simply modify the key while you're iterating over it, as follows:
for key in dic:
value = dic[key]
updated = {k:v for (k, v) in value.items() if v != 0}
dic[key] = updated
If you want to make a copy, simply create another dictionary and add it:
new_dict = {}
for key in dic:
value = dic[key]
updated = {k:v for (k, v) in value.items() if v != 0}
new_dict[key] = updated
These approaches can be seen here (commented out)
rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
rec(diry,level+1)
rec({},0)
print(rows)
The above code prints
[{'column_0': 0, 'column_1': 1, 'column_2': 2},
{'column_0': 0, 'column_1': 1, 'column_2': 2},
{'column_0': 0, 'column_1': 1, 'column_2': 2}]
Shouldn't it print
[{'column_0': 0},
{'column_0': 0, 'column_1': 1},
{'column_0': 0, 'column_1': 1, 'column_2': 2}]
Any suggestions will be helpful. Thanks
It is something distracting for beginners. You have to make sure that you pass a copy of dictionary or else it is the same reference to dictionary that you end up modifying:
rec(diry.copy(), level+1)
Code:
rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
rec(diry.copy(),level+1) # CHANGE HERE
rec({},0)
print(rows)
I am going from top to bottom but i think it is what you want:
def rec_append(n):
if n == 0:
return [{f'col{n}': n}]
else:
return (rec_append(n-1)) + [{f'col{n}': n for n in range(n+1)}]
print(rec_append(3))
[{'col0': 0},
{'col0': 0, 'col1': 1},
{'col0': 0, 'col1': 1, 'col2': 2},
{'col0': 0, 'col1': 1, 'col2': 2, 'col3': 3}]
You are changing the dictionary in your iterations and inserting a reference to it in the list. At the end you have inserted three times the reference to the same dictionary and therefore the last print is just three printing outputs of the same dictionary.
If you really want to have each time a reference to the dictionary at that time you have to make a copy:
rows = []
import copy
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
rows.append(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(copy.deepcopy(diry))
rec(diry,level+1)
rec({},0)
print(rows)
Output:
[{'column_0': 0}, {'column_0': 0, 'column_1': 1}, {'column_0': 0, 'column_1': 1, 'column_2': 2}]
You can change your code to this to get the desired output:-
rows = []
def rec(diry,level):
if level == 2:
diry['column_{}'.format(level)] = level
# print(diry)
rows.append(diry)
print(diry)
else:
diry['column_{}'.format(level)] = level
rows.append(diry)
print(diry)
rec(diry,level+1)
rec({},0)
Output:-
{'column_0': 0}
{'column_0': 0, 'column_1': 1}
{'column_0': 0, 'column_1': 1, 'column_2': 2}
Now you can compare this code and your code to get the intuition of why you are getting the result you were thinking.
I have a list which is like this:
[{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
How can I extract a list of keys from it?
[0, 0, 1, 0, 1, 0]
Use dict.keys() to get keys out of a dictionary and use it in a list-comprehension like below:
lst = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
print([y for x in lst for y in x.keys()])
# [0, 0, 1, 0, 1, 0]
Or, this should be further simplified as:
print([y for x in lst for y in x])
Because, when you simply iterate through dictionary like for y in x, you are actually iterating through keys of the dictionary.
Use dict.keys to extract the keys of each of the dict, convert to a list, and then extract the first element
>>> lst = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
>>> [list(d.keys())[0] for d in lst]
[0, 0, 1, 0, 1, 0]
Alternatively, you can use list comprehension as below
>>> [k for d in lst for k in d.keys()]
[0, 0, 1, 0, 1, 0]
I suggest you to simply iterate over the list of dictionaries, and to iterate over the keys of each dictionary, using list comprehension as follows:
myList = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
newList = [k for d in myList for k in d]
print(newList) # [0, 0, 1, 0, 1, 0]
Those are the keys so you would use the dict.keys() method:
L = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
L2 = [list(d.keys())[0] for d in L]
use keys() function of dict().
a = [{0: 26}, {0: 36}, {1: 1}, {0: 215}, {1: 63}, {0: 215}]
keys = list(a.keys()[0])
vaues = (a.values()[0])
Cheers!
you can do it this way :
list = [list(d.keys())[0] for d in originalList] # originalList is the first list you posted
Here's the output : [0, 0, 1, 0, 1, 0]
You can use dict.keys and itertools.chain to build a list of all keys:
from itertools import chain
w = [{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
keys = list(chain.from_iterable(map(dict.keys, w)))
OR try the below code, using map:
lod=[{0: 26},
{0: 36},
{1: 1},
{0: 215},
{1: 63},
{0: 215}]
print(list(map(lambda x: list(x.keys())[0])))
I tried running this and I'm getting shortest path with memory address.How can I remove the memory address from the output
import networkx as nx
G=nx.Graph()
G.add_nodes_from([1,2,3,4])
G.add_weighted_edges_from([(1,2,8),(1,3,5),(2,4,1),(3,4,2)])
print(nx.floyd_warshall(G))
Here's the output
{1: defaultdict(<function floyd_warshall_predecessor_and_distance.<locals>.<lambda>.<locals>.<lambda> at 0x000002AA0C397B70>, {1: 0, 2: 8, 3: 5, 4: 7}),
`2: defaultdict(<function floyd_warshall_predecessor_and_distance.<locals>.<lambda>.<locals>.<lambda> at 0x000002AA0D96A378>, {1: 8, 2: 0, 3: 3, 4: 1}),
3: defaultdict(<function floyd_warshall_predecessor_and_distance.<locals>.<lambda>.<locals>.<lambda> at 0x000002425C098F28>, {1: 5, 2: 3, 3: 0, 4: 2}),
4: defaultdict(<function floyd_warshall_predecessor_and_distance.<locals>.<lambda>.<locals>.<lambda> at 0x000002425C0A2048>, {1: 7, 2: 1, 3: 2, 4: 0})}
The output that I want is something like this-
1:{1:0, 2:8, 3:5, 4:7}
2:{1:8, 2:0, 3:3, 4:1}
3:{1:5, 2:3, 3:0, 4:2}
4:{1:7, 2:1, 3:2, 4:0}
Looks like it's a dictionary inside a dictionary:
You can do something like this:
data = nx.floyd_warshall(G)
for key, value in data.iteritems():
print('%s %s' % (key, value.values()))
Or if you want the key:value pair:
import json
data = nx.floyd_warshall(G)
for key, value in data.iteritems():
print('%s %s' % (key, json.dumps(value)))
It's printing out a dict of defaultdicts (which is what networkx returns), but you want it to print a dict of dicts, so you need to convert those defaultdicts into dicts.
X = nx.floyd_warshall(G)
Y = {a:dict(b) for a,b in X.items()}
print(Y)
> {1: {1: 0, 2: 8, 3: 5, 4: 7},
2: {1: 8, 2: 0, 3: 3, 4: 1},
3: {1: 5, 2: 3, 3: 0, 4: 2},
4: {1: 7, 2: 1, 3: 2, 4: 0}}
I have several dictionaries which I'd like to combine such that if a key is in multiple dictionaries the values are added together. For example:
d1 = {1: 10, 2: 20, 3: 30}
d2 = {1: 1, 2: 2, 3: 3}
d3 = {0: 0}
merged = {1: 11, 2: 22, 3: 33, 0: 0}
What is the best way to do this in Python? I was looking at defaultdict and trying to come up with something. I'm using Python 2.6.
using a defaultdict:
>>> d = defaultdict(int)
>>> for di in [d1,d2,d3]:
... for k,v in di.items():
... d[k] += v
...
>>> dict(d)
{0: 0, 1: 11, 2: 22, 3: 33}
>>>
With the very most python standard functions and libraries:
dlst = [d1, d2, d3]
for i in dlst:
for x,y in i.items():
n[x] = n.get(x, 0)+y
Instead of using if-else checks, using dict.get with default value 0 is simple and easy.
without importing anything. .
d4={}
for d in [d1,d2,d3]:
for k,v in d.items():
d4.setdefault(k,0)
d4[k]+=v
print d4
output:
{0: 0, 1: 11, 2: 22, 3: 33}