Variable Locality - python

I'm trying to implement Dijkstra alogorthim. While doing so, I've coded graph part.
I'm observing this strange output. Not sure which feature causing this. I'm getting same output for two print though one is local variable and another is in class.
class Graph(object):
def __init__(self, nodes, init_graph):
self.nodes = nodes
self.graph = self.construct_graph(nodes, init_graph)
def construct_graph(self, nodes, init_graph):
'''
This method makes sure that the graph is symmetrical.
In other words, if there's a path from node A to B with a value V, there needs to be a path from node B to node A with a value V.
:param self:
:param nodes:
:param init_graph:
:return:
'''
graph = {}
for node in nodes:
graph[node] = {}
graph.update(init_graph)
for node, edges in graph.items():
for adjacent_node, value in edges.items():
if graph[adjacent_node].get(node, False) == False:
graph[adjacent_node][node] = value
print(graph)
return graph
if __name__=="__main__":
nodes = ["Reykjavik", "Oslo", "Moscow", "London", "Rome", "Berlin", "Belgrade", "Athens"]
init_graph = {}
for node in nodes:
init_graph[node] = {}
init_graph["Reykjavik"]["Oslo"] = 5
init_graph["Reykjavik"]["London"] = 4
init_graph["Oslo"]["Berlin"] = 1
init_graph["Oslo"]["Moscow"] = 3
init_graph["Moscow"]["Belgrade"] = 5
init_graph["Moscow"]["Athens"] = 4
init_graph["Athens"]["Belgrade"] = 1
init_graph["Rome"]["Berlin"] = 2
init_graph["Rome"]["Athens"] = 2
graph = Graph(nodes, init_graph)
print(init_graph)
Output :
{'Reykjavik': {'Oslo': 5, 'London': 4}, 'Oslo': {'Berlin': 1, 'Moscow': 3, 'Reykjavik': 5}, 'Moscow': {'Belgrade': 5, 'Athens': 4, 'Oslo': 3}, 'London': {'Reykjavik': 4}, 'Rome': {'Berlin': 2, 'Athens': 2}, 'Berlin': {'Oslo': 1, 'Rome': 2}, 'Belgrade': {'Moscow': 5, 'Athens': 1}, 'Athens': {'Belgrade': 1, 'Moscow': 4, 'Rome': 2}}
{'Reykjavik': {'Oslo': 5, 'London': 4}, 'Oslo': {'Berlin': 1, 'Moscow': 3, 'Reykjavik': 5}, 'Moscow': {'Belgrade': 5, 'Athens': 4, 'Oslo': 3}, 'London': {'Reykjavik': 4}, 'Rome': {'Berlin': 2, 'Athens': 2}, 'Berlin': {'Oslo': 1, 'Rome': 2}, 'Belgrade': {'Moscow': 5, 'Athens': 1}, 'Athens': {'Belgrade': 1, 'Moscow': 4, 'Rome': 2}}
Expected Output :
{'Reykjavik': {'Oslo': 5, 'London': 4}, 'Oslo': {'Berlin': 1, 'Moscow': 3, 'Reykjavik': 5}, 'Moscow': {'Belgrade': 5, 'Athens': 4, 'Oslo': 3}, 'London': {'Reykjavik': 4}, 'Rome': {'Berlin': 2, 'Athens': 2}, 'Berlin': {'Oslo': 1, 'Rome': 2}, 'Belgrade': {'Moscow': 5, 'Athens': 1}, 'Athens': {'Belgrade': 1, 'Moscow': 4, 'Rome': 2}}
{'Reykjavik': {'Oslo': 5, 'London': 4}, 'Oslo': {'Berlin': 1, 'Moscow': 3}, 'Moscow': {'Belgrade': 5, 'Athens': 4}, 'London': {}, 'Rome': {'Berlin': 2, 'Athens': 2}, 'Berlin': {}, 'Belgrade': {}, 'Athens': {'Belgrade': 1}}

graph.update(init_graph) creates references to the nested dictionaries in init_graph rather than creating a separate copy. This allows the data in init_graph to be overwritten and is causing the strange outputs.
This fixed the issue for me:
from copy import deepcopy
graph = Graph(nodes, deepcopy(init_graph))
Here's some info on dictionary copying that was helpful for me: Copy a Python Dictionary: A Complete Guide

Related

Dynamically brute-force all numerical combinations

As the title says I'm trying to get all possible numerical combinations for an unknown number of parameters. I don't mind if you use pandas or numpy. See the code section to understand my problem easily. Any help would be appreciated.
# n can be any integer. 4 is just an example.
n = 4
possibleValues = range(1, n+1)
# [1, 2, 3, 4]
# The number of elements in this list is unknown. It's only 2 elements just for example
parameters = ["foo", "boo"]
Expected results:
{'foo': 1, 'boo': 1},
{'foo': 1, 'boo': 2},
{'foo': 1, 'boo': 3},
{'foo': 1, 'boo': 4},
{'foo': 2, 'boo': 1},
{'foo': 2, 'boo': 2},
{'foo': 2, 'boo': 3},
{'foo': 2, 'boo': 4}
...
{'foo': 4, 'boo': 1},
{'foo': 4, 'boo': 2},
{'foo': 4, 'boo': 3},
{'foo': 4, 'boo': 4}
itertools.product() will do this for you. You need to tell it how many to repeat, which in this case is the length of the dict keys. Then just zip them up and pass to dict():
from itertools import product
n = 4
possibleValues = range(1, n+1)
parameters = ["foo", "boo"]
[dict(zip(parameters, pair)) for pair in product(possibleValues, repeat=len(parameters))]
Which gives you:
[{'foo': 1, 'boo': 1},
{'foo': 1, 'boo': 2},
{'foo': 1, 'boo': 3},
{'foo': 1, 'boo': 4},
{'foo': 2, 'boo': 1},
{'foo': 2, 'boo': 2},
{'foo': 2, 'boo': 3},
{'foo': 2, 'boo': 4},
{'foo': 3, 'boo': 1},
{'foo': 3, 'boo': 2},
{'foo': 3, 'boo': 3},
{'foo': 3, 'boo': 4},
{'foo': 4, 'boo': 1},
{'foo': 4, 'boo': 2},
{'foo': 4, 'boo': 3},
{'foo': 4, 'boo': 4}]

python iterate to append to a list, but all elements was changed to the last element

my_list = []
pre_dict = {}
for i in range(5):
my_dict = {'Ali': 2, 'Luna': 6}
if pre_dict:
pre_dict['Ali'] = i
print("[WTF] pre_dict: ", pre_dict)
my_list.append(pre_dict)
print("[WTF] my_list 1: ", my_list)
else:
my_dict['Ali'] = i
print("[WTF] my_dict: ", my_dict)
my_list.append(my_dict)
print("[WTF] my_list 2: ", my_list)
pre_dict = my_dict
print("[WTF] my_list x: ", my_list)
exit()
the output is :
[{'Ali': 4, 'Luna': 6}, {'Ali': 4, 'Luna': 6}, {'Ali': 4, 'Luna': 6}, {'Ali': 4, 'Luna': 6}, {'Ali': 4, 'Luna': 6}]
what I want is:
[{'Ali': 4, 'Luna': 6}, {'Ali': 3, 'Luna': 6}, {'Ali': 2, 'Luna': 6}, {'Ali': 1, 'Luna': 6}, {'Ali': 0, 'Luna': 6}]
I am very confused what is the logic. How to modify my code?
anyone give some advice? thanks !
You are facing a Python assignment problem. See: https://docs.python.org/3/library/copy.html
Assignment statements in Python do not copy objects, they create bindings between a target and an object.
Therefore, you should copy objects before append them to a list.
Example without copy.copy():
my_list = []
my_dict = {'A': None, 'B':6}
for i in range(5):
my_dict['A'] = i
my_list.append(my_dict)
print(my_list)
> [{'A': 4, 'B': 6}, {'A': 4, 'B': 6}, {'A': 4, 'B': 6}, {'A': 4, 'B': 6}, {'A': 4, 'B': 6}]
Example with copy.copy():
import copy
my_list = []
my_dict = {'A': None, 'B':6}
for i in range(5):
my_dict['A'] = i
my_list.append(copy.copy(my_dict))
print(my_list)
> [{'A': 0, 'B': 6}, {'A': 1, 'B': 6}, {'A': 2, 'B': 6}, {'A': 3, 'B': 6}, {'A': 4, 'B': 6}]
If you want a reverse order, use range(5-1, -1, -1).
You can do it with a list comprehension:
[{'Ali':i, 'Luna':6} for i in reversed(range(5))]
Output:
[{'Ali': 4, 'Luna': 6},
{'Ali': 3, 'Luna': 6},
{'Ali': 2, 'Luna': 6},
{'Ali': 1, 'Luna': 6},
{'Ali': 0, 'Luna': 6}]
You can use itemgetter from operator library to sort the list of dics, and I fixed your code to get the right values.
from operator import itemgetter
my_list = []
pre_dict = {}
for i in range(5):
my_dict = {'Ali': 0, 'Luna': 6}
if pre_dict:
pre_dict['Ali'] = i
print("[WTF] pre_dict: ", pre_dict)
my_list.append(pre_dict)
print("[WTF] my_list 1: ", my_list)
else:
my_dict['Ali'] = i
print("[WTF] my_dict: ", my_dict)
my_list.append(my_dict)
print("[WTF] my_list 2: ", my_list)
Sortedlist = sorted(my_list, key=itemgetter('Ali'), reverse=True)
print("[WTF] my_list x: ", Sortedlist)

how to make values of a key of a dic uniqe?

I have a list of dic. the value contains some results. I want to uniq them. (some of them are repetitive)
Example of dic[0]:
{'key': 'art director',
'Results': [{'actor': 1},
{'art director': 4},
{'creative designer': 2},
{'art director': 4},
{'creative designer': 2},
{'digital designer': 1},
{'graphic designer': 1},
{'communications consultant': 1},
{'designer': 1},
{'art director': 4},
{'art director': 4}]}
output:
{'key': 'art director',
'Results': [{'actor': 1},
{'art director': 4},
{'creative designer': 2},
{'digital designer': 1},
{'graphic designer': 1},
{'communications consultant': 1},
{'designer': 1},
]}
I wanted to use loop and use the following if condition:
if dic[i]['Results'][k]==dic[i]['Results'][j]
but it is not efficient at all. since there are 3 indices. any better way?
You could use a dictionary for your "Results" values rather than a list of single key dictionaries. So the following:
{
'key': 'art director',
'Results': [
{'actor': 1},
{'art director': 4},
{'creative designer': 2},
{'art director': 4},
{'creative designer': 2},
{'digital designer': 1},
{'graphic designer': 1},
{'communications consultant': 1},
{'designer': 1},
{'art director': 4},
{'art director': 4}
]
}
becomes
import pprint
pprint.pprint({
'key': 'art director',
'Results': {
'actor': 1,
'art director': 4,
'creative designer': 2,
'art director': 4,
'creative designer': 2,
'digital designer': 1,
'graphic designer': 1,
'communications consultant': 1,
'designer': 1,
'art director': 4,
'art director': 4
}
})
Since dictionaries can only have one value per key, this will automatically handle deduplication as you build the Results dictionary.
You can use this line, in order to take the whole list (The value of the key 'Result') and check for duplicates, then change it accordingly if it is already inside your key.
d['Results'] = [dict(t) for t in {tuple(d.items()) for d in d['Results']}]
I have two solutions.
Solution 1
This first one will output exactly what you pretended:
res = [] # represents the final results list
aux = {} # used to check if the key is already in 'Results': []
for i in dic:
for r in i['Results']:
for k,v in r.items():
if k not in aux.keys():
aux[k] = v
res.append({k:v})
i['Results'] = res
print(dic)
Solution 2
This second one will transform 'Results' value in dic[I] into a single dictionary instead of a list of dictionaries:
res = {}
for i in dic:
for r in i['Results']:
for k,v in r.items():
res[k] = v
i['Results'] = res
print(dic)
Using your input as an example:
Output:
[{'Results': {'actor': 1,
'art director': 4,
'communications consultant': 1,
'creative designer': 2,
'designer': 1,
'digital designer': 1,
'graphic designer': 1},
'key': 'art director'}]
Sample dic:
dic = [{'key': 'art director',
'Results': [{'actor': 1},
{'art director': 4},
{'creative designer': 2},
{'art director': 4},
{'creative designer': 2},
{'digital designer': 1},
{'graphic designer': 1},
{'communications consultant': 1},
{'designer': 1},
{'art director': 4},
{'art director': 4}]}]

How to compare two nested dictionaries with the same keys and update values with a condition in python?

d1 = {'Berlin': {'Boston' : 9, 'LA' : 7, 'Chicago' : 1},
'Vienna': {'Boston' : 5, 'LA' : 2, 'Chicago' : 8},
'London': {'Boston' : 8, 'LA' : 6, 'Chicago' : 5}}
d2 = {'Berlin': {'Boston' : 8, 'LA' : 9, 'Chicago' : 4},
'Vienna': {'Boston' : 3, 'LA' : 1, 'Chicago' : 5},
'London': {'Boston' : 5, 'LA' : 8, 'Chicago' : 8}}
I would like to update the values in d1 if the values in d2 smaller than in d1 to get a new dictionary d:
d = {'Berlin': {'Boston' : 8, 'LA' : 7, 'Chicago' : 1},
'Vienna': {'Boston' : 3, 'LA' : 1, 'Chicago' : 5},
'London': {'Boston' : 5, 'LA' : 6, 'Chicago' : 5}}
This works as per your requirement (take min of dict items by iterating them together via zip and re-combine as a dict again, put it as value with same k as the key again):
d1 = {'Berlin': {'Boston' : 9, 'LA' : 7, 'Chicago' : 1},
'Vienna': {'Boston' : 5, 'LA' : 2, 'Chicago' : 8},
'London': {'Boston' : 8, 'LA' : 6, 'Chicago' : 5}}
d2 = {'Berlin': {'Boston' : 8, 'LA' : 9, 'Chicago' : 4},
'Vienna': {'Boston' : 3, 'LA' : 1, 'Chicago' : 5},
'London': {'Boston' : 5, 'LA' : 8, 'Chicago' : 8}}
>>> {k: dict([min(i, j) for i, j in zip(d1[k].items(), d2[k].items())]) for k in d1.keys()}
{'Berlin': {'Boston': 8, 'LA': 7, 'Chicago': 1},
'Vienna': {'Boston': 3, 'LA': 1, 'Chicago': 5},
'London': {'Boston': 5, 'LA': 6, 'Chicago': 5}}
This data seems like it would be better as a dataframe (basically a table) instead of a nested dict.
Once you create the input dataframes, you just need to combine them using a function that selects the minimal value for each cell. Here's a way to do that straight from the documentation:
import numpy as np
import pandas as pd
df1 = pd.DataFrame(d1)
df2 = pd.DataFrame(d2)
df_new = df1.combine(df2, np.minimum)
print(df_new)
Output:
Berlin Vienna London
Boston 8 3 5
LA 7 1 6
Chicago 1 5 5
If you need to get it back to a dict, you can use DataFrame.to_dict():
d = df_new.to_dict()
Which becomes:
{'Berlin': {'Boston': 8, 'LA': 7, 'Chicago': 1},
'Vienna': {'Boston': 3, 'LA': 1, 'Chicago': 5},
'London': {'Boston': 5, 'LA': 6, 'Chicago': 5}}
P.S. I'm not an expert at Pandas

add dictionaries to empty list dict.value()

I have four dictionaries I would like to add as items in empty list that is a dictionary value. and I have no idea how to do this. Could someone please help me figure out how to turn this:
data = {'Cars': []}
dict1 = {'subaru': 1, 'honda': 5, 'volkswagen': 8}
dict2 = {'subaru': 7, 'honda': 3, 'volkswagen': 9}
dict3 = {'subaru': 9, 'honda': 2, 'volkswagen': 1}
dict4 = {'subaru': 2, 'honda': 8, 'volkswagen': 2}
print (data)
into this:
{'Cars': [{'subaru': 1, 'honda': 5, 'volkswagen': 8},
{'subaru': 7, 'honda': 3, 'volkswagen': 9},
{'subaru': 9, 'honda': 2, 'volkswagen': 1},
{'subaru': 2, 'honda': 8, 'volkswagen': 2}]}
data = {'Cars': []}
dict1 = {'subaru': 1, 'honda': 5, 'volkswagen': 8}
dict2 = {'subaru': 7, 'honda': 3, 'volkswagen': 9}
dict3 = {'subaru': 9, 'honda': 2, 'volkswagen': 1}
dict4 = {'subaru': 2, 'honda': 8, 'volkswagen': 2}
for item in [dict1, dict2, dict3, dict4]:
data['Cars'].append(item)
import pprint
pp = pprint.PrettyPrinter()
pp.pprint(data)
gives:
{'Cars': [{'subaru': 1, 'honda': 5, 'volkswagen': 8},
{'subaru': 7, 'honda': 3, 'volkswagen': 9},
{'subaru': 9, 'honda': 2, 'volkswagen': 1},
{'subaru': 2, 'honda': 8, 'volkswagen': 2}]}
Citation: """data['Cars'] is your initially-empty list. You add elements to a list by calling .append() on it. Thus, data['Cars'].append(dict1), and so on.""" – jasonharper
and this can be done in one step in a loop constructed as above.
To get the pretty print you import the pprint module, create a pp object using pp = pprint.PrettyPrinter() and use it's pp.pprint() function to print the list nested in the dictionary in a pretty way :) .
By the way: you can create the data dictionary with a list already containing the elements in one step using:
data = {'Cars': [
{'subaru': 1, 'honda': 5, 'volkswagen': 8},
{'subaru': 7, 'honda': 3, 'volkswagen': 9},
{'subaru': 9, 'honda': 2, 'volkswagen': 1},
{'subaru': 2, 'honda': 8, 'volkswagen': 2}]}
You need to access the Cars key in the data dictionary, then append to that.
data = {'Cars': []}
dict1 = {'subaru': 1, 'honda': 5, 'volkswagen': 8}
dict2 = {'subaru': 7, 'honda': 3, 'volkswagen': 9}
dict3 = {'subaru': 9, 'honda': 2, 'volkswagen': 1}
dict4 = {'subaru': 2, 'honda': 8, 'volkswagen': 2}
data['Cars'].append(dict1)
data['Cars'].append(dict2)
data['Cars'].append(dict3)
data['Cars'].append(dict4)
You could simplify this to just
data['Cars'].append({'subaru': 1, 'honda': 5, 'volkswagen': 8})
data['Cars'].append({'subaru': 7, 'honda': 3, 'volkswagen': 9})
data['Cars'].append({'subaru': 9, 'honda': 2, 'volkswagen': 1})
data['Cars'].append({'subaru': 2, 'honda': 8, 'volkswagen': 2})
Just append all the dicts to your data["Cars"] which is a list.
for i in [dict1, dict2, dict3, dict4]:
data["Cars"].append(i)
print data

Categories