Get value that from another defaultdict and update the original dict - python-3.x

Basically, I am trying to extract the values from one dictionary and update the value in another dictionary. I have four lists as follows:
a = [1,1,2,3,4,5]
b = [0,3,0,5,6,0]
c = [2,3,4,5,6,5]
d = [20,30,40,50,60,70]
So I use a defaultdict to store key,value pairs for a,b like:
one = defaultdict(list)
for k, v in zip(a, b):
one[k].append(v)
two = defaultdict(list)
for k, v in zip(c, d):
two[k].append(v)
Essentially, b is linked to c so I am trying to extract the values in the two dictionary and then update
the values in the one dictionary
So in the end one would look like {1: 30, 3: 50, 4: 60}
This is my code:
three = defaultdict(list)
for k, v in one.items():
if v in two.keys():
newvalue = two[v].values()
three[k].append(newvalue)
But I am now getting an error at line if v in two.keys(): as unhashable type: 'list'. I'm so lost, all
I want to do is use the values from one dictionary and then use those values to find the keys (which are the values
from the other table) and then get those corressponding values.

You are creating a dictionary of list in the beginning:
one = defaultdict(list)
for k, v in zip(a, b):
one[k].append(v)
[output] : defaultdict(list, {1: [0, 3], 2: [0], 3: [5], 4: [6], 5: [0]})
two = defaultdict(list)
for k, v in zip(c, d):
two[k].append(v)
[output] : defaultdict(list, {2: [20], 3: [30], 4: [40], 5: [50, 70], 6: [60]})
Therefore when calling k,v in one.items(), you are getting a key and a list.
Simply switch to iterate through the list , and you should be good to go
three = defaultdict(list)
for k, v in one.items():
for value in v:
if value in two.keys():
newvalue = two[value]
three[k].append(newvalue)
However I'm getting this output :
defaultdict(list, {1: [[30]], 3: [[50, 70]], 4: [[60]]})
Which sounds reasonable to me, but it is not your expected one, can you please explain ?
Let's try know with dic comprehension
output = { k : two[v_2] for k,v in one.items() for v_2 in v}
[output] : {1: [30], 2: [], 3: [50, 70], 4: [60], 5: []}
Request to sum :
Of course, multiple ways of doing it , the quickest is again with dict_comprehension and sum
output_sum = {k: sum(v) for k,v in output.items()}

Related

The problem of using {}.fromkey(['k1','k2'],[]) and {'k1':[],'k2':[]}

list1 = [99,55]
dict1 = {'k1':[],'k2':[]}
for num in list1:
if num > 77:
dict1['k1'].append(num)
else:
dict1['k2'].append(num)
print(dict1)
{'k1':[99],'k2':[55]}
But when I replaced dict1 = {'k1':[],'k2':[]} to {}.fromkeys(['k1','k2'],[]) , the result became {'k1': [99, 55], 'k2': [99, 55]}
why this happens? I really have no idea.
This happens because you are passing the same list object to both keys. This is the same situation as when you create an alias for a variable:
a = []
b = a
a.append(55)
b.append(99)
print(b)
prints [55, 99] because it is the same list instance.
If you want to make it more concise from a list of keys to initialize with empty list, you can do this:
dict1 = {k: [] for k in ('k1', 'k2')}
This will create a new list instance for every key.
Alternatively, you can use defaultdict
from collections import defaultdict
list1 = [99,55]
dict1 = defaultdict(list)
for num in list1:
if num > 77:
dict1['k1'].append(num)
else:
dict1['k2'].append(num)
print(dict1)
Also works.
The fromKeys() can also be supplied with a mutable object as the default value.
if we append value in the original list, the append takes place in all the values of keys.
example:
list1 = ['a', 'b', 'c', 'd']
list2 = ['SALIO']
dict1 = dict.fromkeys(list1, list2)
print(dict1)
output:
{'a': ['SALIO'], 'b': ['SALIO'], 'c': ['SALIO'], 'd': ['SALIO']}
then you can use this:
list1 = ['k1', 'k2']
dict1 = {'k1':[],'k2':[]}
list2 =[99,55]
for num in list2:
if num > 77:
a = ['k1']
dict1 = dict.fromkeys(a, [num])
else:
b = ['k2']
dict2 = dict.fromkeys(b,[num] )
res = {**dict1, **dict2}
print(res)
output:
{'k1': [99], 'k2': [55]}
You can also use the python code to merge dict code:
this function:
def Merge(dict1, dict2):
return(dict2.update(dict1))
then:
print(Merge(dict1, dict2)) #This return None
print(dict2) # changes made in dict2

swap the keys and values in a dictionary by storing the user input in dic

First the input should be dic length consider 3. then the input to a dic is keys and values separated by spaces i,e
"A 1
B 2
C 1"
now dic={A:1, B:2, C:1}
At first the keys and values and should be swapped, and if there are same keys and there values should be merged in a list and assigned to the same key as shown below.(these program should work for any length of dictionary)
the output should be dicout={1:['A','C'], 2:B}.
Thank you.
Define:
from collections import defaultdict
def make_dict(s):
d = defaultdict(list)
xs = s.split(" ")
for k, v in zip(xs[1::2], xs[::2]):
d[k].append(v)
for k, v in d.items():
if len(v) == 1:
d[k] = v[0]
return dict(d)
Example usage:
>>> make_dict("A 1 B 2 C 1")
{'1': ['A', 'C'], '2': 'B'}

How to find highest number from the vector provided?

Say, a dictionary is provided with certain values.
How to find the highest number ?
Input
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector = 5
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector = 5
l1 = list(td.values())
Based on vector value, it should print output.
vector is 5, so sum of the dict-values to form vector is 3,1,1
Corresponding keys are 5,4,1
so, the output should be 541 but slight change here.
Since value '1' is associated with multiple keys, it should pick up highest key,
so, output should be 544 instead of 541 (For above input, to brief about combinations without considering '1+1+1+1+1' to '44444')
Another example
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector = 7
Possible combinations:
3 # --> Key of 7
21 # --> Key of 6 & 1 (6+1 = 7)
24 # --> Key of 6 & 1 (6+1 = 7)
12 # --> Key of 1 & 6 (1+6 = 7)
42 # --> Key of 1 & 6 (1+6 = 7)
Output : 42 (Highest number)
Another
d1 = {1:9,2:4,3:2,4:2,5:6,6:3,7:2,8:2,9:1}
vector = 5
here, it would be 1+2+2 (988).
But, '1' can also be added 5 times to form vector 5,
which would be '99999'
Since #Patrick Artner requested for minimal reproducible example, posting this though doesn't work as expected.
from itertools import combinations
def find_sum_with_index(l1, vector):
index_vals = [iv for iv in enumerate(l1) if iv[1] < target]
for r in range(1, len(index_vals) + 1):
for perm in combinations(index_vals, r):
if sum([p[1] for p in perm]) == target:
yield perm
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector=5
l1=list(d1.values())
for match in find_sum_with_index(l1, vector):
print(dict(match))
Is there any specific algorithm to be chosen for these kind of stuffs ?
Similar to the other answer but allowing repeatedly using the same keys to get the max number of keys which values sum up to vector:
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector = 7
#create a dict that contains value -> max-key for that value
d2 = {}
for k,v in d1.items():
d2[v] = max(d2.get(v,-1), k)
def mod_powerset(iterable,l):
# uses combinations_with_replacement to allow multiple usages of one value
from itertools import chain, combinations_with_replacement
s = list(set(iterable))
return chain.from_iterable(combinations_with_replacement(s, r) for r in range(l))
# create all combinations that sum to vector
p = [ s for s in mod_powerset(d1.values(),vector//min(d1.values())+1) if sum(s) == vector]
print(p)
# sort combinations by length then value descending and take the max one
mp = max( (sorted(y, reverse=True) for y in p), key=lambda x: (len(x),x))
# get the correct keys to be used from d2 dict
rv = [d2[num] for num in mp]
# sort by values, biggest first
rv.sort(reverse=True)
# solution
print(''.join(map(str,rv)))
Original powerset - see itertools-recipes.
There are some steps involved, see documentation in comments in code:
d1 = {1: 1, 2: 6, 3: 7, 4: 1, 5: 3}
vector = 7
# create a dict that contains value -> sorted key-list, used to get final keys
from collections import defaultdict
d2 = defaultdict(list)
for k,v in d1.items():
d2[v].append(k)
for k,v in d2.items():
d2[k] = sorted(v, reverse=True)
from itertools import chain, combinations
def powerset(iterable):
"see itertools: powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
# create all combinations that sum to vector
p = [ s for s in powerset(d1.values()) if sum(s) == vector]
# sort combinations by length then value descending and take the max one
mp = max( (sorted(y, reverse=True) for y in p), key=lambda x: (len(x),x))
# get the correct keys to be used from d2 dict
rv = []
for num in mp:
rv.append(d2[num][0])
# remove used key from list
d2[num][:] = d2[num][1:]
# sort by values, biggest first
rv.sort(reverse=True)
# solution
print(''.join(map(str,rv)))
For powerset - see itertools-recipes.

return dictionary of file names as keys and word lists with words unique to file as values

I am trying to write a function to extract only words unique to each key and list them in a dictionary output like {"key1": "unique words", "key2": "unique words", ... }. I start out with a dictionary. To test with I created a simple dictionary:
d = {1:["one", "two", "three"], 2:["two", "four",
"five"], 3:["one","four", "six"]}
My output should be:
{1:"three",
2:"five",
3:"six"}
I am thinking maybe split in to separate lists
def return_unique(dct):
Klist = list(dct.keys())
Vlist = list(dct.values())
aList = []
for i in range(len(Vlist)):
for j in Vlist[i]:
if
What I'm stuck on is how do I tell Python to do this: if Vlist[i][j] is not in the rest of Vlist then aList.append(Vlist[i][j]).
Thank you.
You can try something like this:
def return_unique(data):
all_values = []
for i in data.values(): # Get all values
all_values = all_values + i
unique_values = set([x for x in all_values if all_values.count(x) == 1]) # Values which are not duplicated
for key, value in data.items(): # For Python 3.x ( For Python 2.x -> data.iteritems())
for item in value: # Comparing values of two lists
for item1 in unique_values:
if item == item1:
data[key] = item
return data
d = {1:["one", "two", "three"], 2:["two", "four", "five"], 3:["one","four", "six"]}
print (return_unique(d))
result >> {1: 'three', 2: 'five', 3: 'six'}
Since a key may have more than one unique word associated with it, it makes sense for the values in the new dictionary to be a container type object to hold the unique words.
The set difference operator returns the difference between 2 sets:
>>> a = set([1, 2, 3])
>>> b = set([2, 4, 6])
>>> a - b
{1, 3}
We can use this to get the values unique to each key. Packaging these into a simple function yields:
def unique_words_dict(data):
res = {}
values = []
for k in data:
for g in data:
if g != k:
values += data[g]
res[k] = set(data[k]) - set(values)
values = []
return res
>>> d = {1:["one", "two", "three"],
2:["two", "four", "five"],
3:["one","four", "six"]}
>>> unique_words_dict(d)
{1: {'three'}, 2: {'five'}, 3: {'six'}}
If you only had to do this once, then you might be interested in the less efficeint but more consice dictionary comprehension:
>>> from functools import reduce
>>> {k: set(d[k]) - set(reduce(lambda a, b: a+b, [d[g] for g in d if g!=k], [])) for k in d}
{1: {'three'}, 2: {'five'}, 3: {'six'}}

Counter class extension

I am having a problem finding an elegant way to create a Counter() class that can:
Feed in arbitrary number of keys and return a nested dictionary based on this list of keys.
Increment for this nested dictionary is arbitrary as well.
For example:
counter = Counter()
for line in fin:
if a:
counter.incr(key1, 1)
else:
counter.incr(key2, key3, 2)
print counter
Ideally I am hoping to get the result looks like: {key1 : 20, {key2 : {key3 : 40}}}. But I am stuck in creating this arbitrary nested dictionary from list of keys. Any help is appreciated.
you can subclass dict and create your own nested structure.
here's my attempt at writing such class :
class Counter(dict):
def incr(self, *args):
if len(args) < 2:
raise TypeError, "incr() takes at least 2 arguments (%d given)" %len(args)
curr = self
keys, count = args[:-1], args[-1]
for depth, key in enumerate(keys, 1):
if depth == len(keys):
curr[key] = curr.setdefault(key, 0) + count
else:
curr = curr.setdefault(key, {})
counter = Counter()
counter.incr('key1', 1)
counter.incr('key2', 'key3', 2)
counter.incr('key1', 7)
print counter #{'key2': {'key3': 2}, 'key1': 8}
There are two possibilities.
First, you can always fake the nested-keys thing by using a flat Counter with a "key path" made of tuples:
counter = Counter()
for line in fin:
if a:
counter.incr((key1,), 1)
else:
counter.incr((key2, key3), 2)
But then you'll need to write a str-replacement—or, better, a wrapper class that implements __str__. And while you're at it, you can easily write an incr wrapper that lets you use exactly the API you wanted:
def incr(self, *args):
super().incr(args[:-1], args[-1])
Alternatively, you can build your own Counter-like class on top of a nested dict. The code for Counter is written in pure Python, and the source is pretty simple and readable.
From, your code, it looks like you don't have any need to access things like counter[key2][key3] anywhere, which means the first is probably going to be simpler and more appropriate.
The only type of value that can exist in a Counter object is an int, you will not be able to represent a nested dictionary with a Counter.
Here is one way to do this with a normal dictionary (counter = {}). First, to update increment the value for a single key:
counter[key1] = counter.setdefault(key1, 0) + 1
Or for an arbitrary list of keys to create the nested structure:
tmp = counter
for key in key_list[:-1]:
tmp = tmp.setdefault(key, {})
tmp[key_list[-1]] = tmp.setdefault(key_list[-1], 0) + 1
I would probably turn this into the following function:
def incr(counter, val, *keys):
tmp = counter
for key in keys[:-1]:
tmp = tmp.setdefault(key, {})
tmp[keys[-1]] = tmp.setdefault(keys[-1], 0) + val
Example:
>>> counter = {}
>>> incr(counter, 1, 'a')
>>> counter
{'a': 1}
>>> incr(counter, 2, 'a')
>>> counter
{'a': 3}
>>> incr(counter, 2, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 2}}}
>>> incr(counter, 3, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 5}}}

Resources