I'm looking for a way to be able to define dictionary keys by function parameters. In the code below I make divisions of the first and second letters of the dictionary keys but Python's function parameters are not strings.
def x(a, b, c):
dict = {'ab': 0, 'ac': 0, 'bc': 0}
for d, e in dict.keys:
dict[de] = d/e
x(10, 20, 30)
Here's some code that will handle any number of arguments. It first sorts the argument names into alphabetical order. Then it creates all pairs of arguments and performs the divisions (with the value of the earlier arg in alphabetical order being divided by the value of the later one), storing the results in a dict, with the dict's keys being constructed by the concatenating the argument names. The function returns the constructed dict, but of course in your code you may wish to perform further actions on it.
from itertools import combinations
def ratios(**kwargs):
pairs = combinations(sorted(kwargs.keys()), 2)
return dict((p + q, kwargs[p] / kwargs[q]) for p, q in pairs)
print(ratios(a=600, b=3, c=2))
output
{'ac': 300.0, 'ab': 200.0, 'bc': 1.5}
Related
Here's a function that is supposed to swap dictionary keys and values. {'a': 3} is supposed to become {3: 'a'}.
def change_keys_values(d):
for key in d:
value = d[key]
del d[key]
d[value] = key
return d
I've realized that this function shouldn't work because I'm changing dictionary keys during iteration. This is the error I get: "dictionary keys changed during iteration". However, I don't get this error on a three key-value pair dictionary. So, while {'a': 3, 't': 8, 'r': 2, 'z': 44, 'u': 1, 'b': 4} results in the above mentioned error, {'a': 3, 't': 8, 'r': 2} gets solved without any issues. I'm using python 3. What is causing this?
You must never modify a dictionary inside a loop. The reason is the way the dictionaries are often implemented.
Hash Tables
Basically, when you create a dictionary, each item is indexed using the hash value of the key.
Dictionaries are implemented sparsely
Another implementation detail involves the fact that dictionaries are implemented in a sparse manner. Namely, when you create a dictionary, there are empty places in memory (called buckets). When you add or remove elements from a dictionary, it may hit a threshold where the dictionary key hashes are re-evaluated and as a consequence, the indexes are changed.
Roughly speaking, these two points are the reason behind the problem you are observing.
Moral Point: Never modify a dictionary inside a loop of any kind.
Here's a simple code to do what you want:
def change_keys_values(d):
new_dict = {value: key for key, value in d.items()}
return new_dict
You need to verify that the values are unique, after that, no problem :)
But be sure not to change a dictionary while parsing it. Otherwise, you could encounter an already changed index that get's interpreted twice or even more. I suggest making a new variable (a copy):
def invert(dict_: dict) -> dict:
if list(set(dict_.values())) == list(dict_.values()): # evaluates if "inverting key:value" is possible (if keys are unique)
return {b: a for a, b in dict_.items()}
else:
raise ValueError("Dictionary values contain duplicates. Inversion not possible!")
print(invert({"a": 1, "b": 2, "c": 3, "d": 4})) # works
print(invert({"a": 1, "b": 2, "c": 3, "d": 3})) # fails
To fix your issue, just iterate over copy, not the original dict:
import copy
def change_keys_values(d):
for key in copy.deepcopy(d):
value = d[key]
del d[key]
d[value] = key
return d
Then the good alternative using zip would be:
def change_keys_values(d):
a, b = zip(*d.items())
d = dict(list(zip(b,a)))
return d
I was running this code through python tutor, and was just confused as to how the keys and values get switched around. I also was confused as to what value myDict[d[key]] would correspond to as I'm not sure what the d in [d[key]] actually does.
def dict_invert(d):
'''
d: dict
Returns an inverted dictionary according to the instructions above
'''
myDict = {}
for key in d.keys():
if d[key] in myDict:
myDict[d[key]].append(key)
else:
myDict[d[key]] = [key]
for val in myDict.values():
val.sort()
return myDict
print(dict_invert({8: 6, 2: 6, 4: 6, 6: 6}))
In your function d is the dictionary being passed in. Your code is creating a new dictionary, mapping the other direction (from the original dictionary's values to its keys). Since there may not be a one to one mapping (since values can be repeated in a dictionary), the new mapping actually goes from value to a list of keys.
When the code loops over the keys in d, it then uses d[key] to look up the corresponding value. As I commented above, this is not really the most efficient way to go about this. Instead of getting the key first and indexing to get the value, you can instead iterate over the items() of the dictionary and get key, value 2-tuples in the loop.
Here's how I'd rewrite the function, in what I think is a more clear fashion (as well as perhaps a little bit more efficient):
def dict_invert(d):
myDict = {}
for key, value in d.items(): # Get both key and value in the iteration.
if value in myDict: # That change makes these later lines more clear,
myDict[value].append(key) # as they can use value instead of d[key].
else:
myDict[value] = [key] # here too
for val in myDict.values():
val.sort()
return myDict
The function you are showing inverts a dictionary d. A dictionary is a collection of unique keys that map to values which are not necessarily unique. That means that when you swap keys and values, you may get multiple keys that have the same value. Your function handles this by adding keys in the input to a list in the inverse, instead of storing them directly as values. This avoids any possibility of conflict.
Let's look at a sample conceptually first before digging in. Let's say you have
d = {
'a': 1,
'b': 1,
'c': 2
}
When you invert that, you will have the keys 1 and 2. Key 1 will have two values: 'a' and 'b'. Key 2 will only have one value: 'c'. I used different types for the keys and values so you can tell immediately when you're looking at the input vs the output. The output should look like this:
myDict = {
1: ['a', 'b'],
2: ['c']
}
Now let's look at the code. First you initialize an empty output:
myDict = {}
Then you step through every key in the input d. Remember that these keys will become the values of the output:
for key in d.keys():
The value in d for key is d[key]. You need to check if that's a key in myDict since values become keys in the inverse:
if d[key] in myDict:
If the input's value is already a key in myDict, then it maps to a list of keys from d, and you need to append another one to the list. Specifically, d[key] represents the value in d for the key key. This value becomes a key in myDict, which is why it's being indexed like that:
myDict[d[key]].append(key)
Otherwise, create a new list with the single inverse recorded in it:
else:
myDict[d[key]] = [key]
The final step is to sort the values of the inverse. This is not necessarily a good idea. The values were keys in the input, so they are guaranteed to be hashable, but not necessarily comparable to each other:
for val in myDict.values():
val.sort()
The following should raise an error in Python 3:
dict_invert({(1, 2): 'a', 3: 'b'})
myDict[d[key]] takes value of d[key] and uses it as a key in myDict, for example
d = {'a': 'alpha', 'b': 'beta'}
D = {'alpha': 1, 'beta': 2}
D[d['a']] = 3
D[d['b']] = 4
now when contents of d and D should be as following
d = {'a': 'alpha', 'b': 'beta'}
D = {'alpha': 3, 'beta': 4}
d is the dictionary you are passing into the function
def dict_invert(d)
When you create
myDict[d[key]] = d
Its meaning is
myDict[value of d] = key of d
Resulting in
myDict = {'value of d': 'key of d'}
I have a list of dictionaries, of the form:
neighbour_list = [{1:4}, {3:5}, {4:9}, {5:2}]
I need to sort the list in order of the dictionary with the largest value. So, for the above code the sorted list would look like:
sorted_list = [{4:9}, {3:5}, {1:4}, {5:2}]
Each dictionary within the list only has one mapping.
Is there an efficient way to do this? Currently I am looping through the list to get the biggest value, then remembering where it was found to return the largest value, but I'm not sure how to extend this to be able to sort the entire list.
Would it just be easier to implement my own dict class?
EDIT: here is my code for returning the dictionary which should come 'first' in an ideally sorted list.
temp = 0
element = 0
for d in list_of_similarities:
for k in d:
if (d[k] > temp):
temp = d[k]
element = k
dictionary = d
first = dictionary[element]
You can use an anonymous function as your sorting key to pull out the dict value (not sure if i've done this the most efficient way though:
sorted(neighbour_list, key = lambda x: tuple(x.values()), reverse=True)
[{4: 9}, {3: 5}, {1: 4}, {5: 2}]
Note we need to coerce x.values() to a tuple, since in Python 3, x.values() is of type "dict_values" which is unorderable. I guess the idea is that a dict is more like a set than a list (hence the curly braces), and there's no (well-) ordering on sets; you can't use the usual lexicographic ordering since there's no notion of "first element", "second element", etc.
You could list.sort using the dict values as the key.
neighbour_list.sort(key=lambda x: x.values(), reverse=1)
Considering you only have one value, for python2 you can just call next on itervalues to get the first and only value:
neighbour_list.sort(key=lambda x: next(x.itervalues()), reverse=1)
print(neighbour_list)
For python3, you cannot call next on dict.values, it would have to be:
neighbour_list.sort(key=lambda x: next(iter(x.values())), reverse=1)
And have to call list on dict.values:
neighbour_list.sort(key=lambda x: list(x.values()), reverse=1)
I have two lists:
a=[{"aaa":10},{"bbb":20}]
b=[{"aaa":2},{"bbb":5}]
I want to subtract the dictionaries present inside the lists and subtract the value if their key matches and store the results into variables.
Expected result
var_1=8 i.e (aaa(10-2))
var_2=15 i.e (bbb(20-5))
I am able to achieve the result by iterating thorough both the lists, then the dictionaries inside the lists and comparing their keys. If the keys matches then subtracting the values and storing the result into a variable
Can anybody suggest if there is any predefined function to achieve the result in one or two statements, rather than writing many nested loops.
Here you go...by using list comprehension.
final_result = [{key:dic_a[key]- dic_b[key]} for dic_a in a for dic_b in b for key in dic_a if key in dic_b]
result:
[{'aaa': 8}, {'bbb': 15}]
Is this the u r desired output??
One option is to use collections.Counter to convert your lists to Counters, and substract them the way you like. Following code will threat missing values as zeros:
import collections
def substract_lists(a, b):
def sum_dicts(*dicts):
rv = collections.Counter()
for d in dicts: rv.update(**d)
return rv
a2c = sum_dicts(*a)
b2c = sum_dicts(*b)
return {k: a2c[k] - b2c[k] for k in set(a2c) | set(b2c)}
Demo
>>> substract_lists([{'aaa': 10}, {'ccc': -20}], [{'aaa': 2}, {'bbb': 5}])
{'aaa': 8, 'bbb': -5, 'ccc': -20}
I have a dict and a string here, with the dict containing char-count as key value pair. I want to check if the all the characters in the string are completely contained in the dict.
This means that the dict should contain all the chars of the string, with their counts less than or equal to their corresponding values in the dict.
def isValidWord(strng, dct):
"""
Returns True if strng is entirely
composed of letters in the dct.
Otherwise, returns False.
Does not mutate hand or dct.
"""
d={}
for x in strng:
d[x]=d.get(x,0)
for x in d:
if d[x]> dct.get(x,0):
return False
return True
It seems to work well for most cases, but for some cases it doesn't. For example -
isValidWord('chayote', {'a': 1, 'c': 2, 'u': 2, 't': 2, 'y': 1, 'h': 1, 'z': 1,
'o': 2})
This gives output True, however the correct output is False.
This is because there is no e in the dict.
Where is the bug here ? And how can I check if all the pairs in a dict also exist in another dict, possibly with equal or lower corresponding values (of keys).
You meant for the line
d[x]=d.get(x,0)
to be
d[x]=d.get(x,0) + 1
otherwise, all the values in the dictionary would be 0, and the function would always return True (unless the string were empty or any values in the given dictionary were 0.
Also note that it would be easier to use collections.Counter for your first loop:
d = collections.Counter(strng)
As for your question of testing whether one dict is in another, you can do:
all(k in dct and v < dct[k] for k, v in d.items())