How do I swap two random values from a dictionary? - python

I need to swap two random values from a dicitonary
def alphabetcreator():
letters = random.sample(range(97,123), 26)
newalpha = []
engalpha =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
alphasmerged = {}
for i in letters:
newalpha.append(chr(i))
alphasmerged = dict(zip(engalpha, newalpha))
return(alphabetsmerged)
This code gives me my two different alphabets, putting them into a dictionary so I can translate between one and the other. I now need to randomly swap two of the values whilst keeping all the rest the same. How can I do this?

You can first use random.sample to randomly pick two different values from a collection.
From the doc:
Return a k length list of unique elements chosen from the population sequence or set. Used for random sampling without replacement.
Use this function on the keys of your dictionary to have two distinct keys.
In Python 3, you can directly use it on a dict_keys object.
In Python 2, you can either convert d.keys() into a list, or directly pass the dictionary to the sample.
>>> import random
>>> d = {'a': 1, 'b': 2}
>>> k1, k2 = random.sample(d.keys(), 2) # Python 3
>>> k1, k2 = random.sample(d, 2) # Python 2
>>> k1, k2
['a', 'b']
Then, you can in-place-ly swap two values of a collection.
>>> d[k1], d[k2] = d[k2], d[k1]
>>> d
{'b': 1, 'a': 2}

d = {12: 34, 67: 89}
k, v = random.choice(list(d.items()))
d[v] = k
d.pop(k)
which when running, gave the random output of d as:
{12: 34, 89: 67}

You can try this:
import random
engalpha =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
new_dict = {a:b for a, b in zip(engalpha, map(chr, random.sample(range(97,123), 26)))}
key_val = random.choice(list(new_dict.keys()))
final_dict = {b if a == key_val else a:a if a == key_val else b for a, b in new_dict.items()}
Regarding your recent comment:
import random
s = {'a': 'h', 'b': 'd', 'c': 'y'}
random_dict = [(a, b) for a, b in random.sample(list(s.items()), 2)]
new_dict = {a:b for a, b in zip([i[0] for i in sorted(random_dict, key=lambda x:x[0])], [i[-1] for i in sorted(random_dict, key=lambda x:x[-1])][::-1])}
final_dict = {a:new_dict.get(a, b) for a, b in s.items()}
Output (randomly generated):
{'a': 'y', 'c': 'h', 'b': 'd'}

Related

zip two unequal lists into a dictionary. give none if key has no value. ignore value without a key

There are two lists of different lengths. The first contains keys, and the second contains values.
Write a function that creates a dictionary from these keys and values.
If the key did not have enough values, the dictionary should have the value None.
Values that did not have enough keys should be ignored.
a = ['a', 'b', 'c']
b = [1, 2, 3, 4, 5]
dict(zip(a, b))
I need this:
{'a':'1', 'b':'2', 'c':'4'}
for
a = ['a', 'b', 'c']
b = [1, 2]
dict(zip(a, b))
I need this:
{'a':'1', 'b':'2', 'c':'None'}
Build an iterator from b and iterate over a getting the next element from b, defaulting to None, e.g.
it = iter(b)
{k: next(it, None) for k in a}
itertools.zip_longest is what you need:
from itertools import zip_longest
dict(zip_longest(a,b) if len(a)>len(b) else zip(a,b))
if part is to avoid None as keys.
Dict comprehension way:
{key: b[idx] if idx < len(b) else None for idx, key in enumerate(a)}
You can add elements to a list using + operator and create a series of new elements using the * operator. So I would solve your problem like this:
dict( zip( a, b + [None] * (len(a)-len(b)) ) )
Which extends the list b in case it is shorter than the list a with None elements and zips both together.
The output:
>>> a = ['a','b','c']
>>> b = [1,2]
>>> dict( zip( a, b + [None] * (len(a)-len(b)) ) )
{'a': 1, 'b': 2, 'c': None}
>>> b = [1,2,3,4,5]
>>> dict( zip( a, b + [None] * (len(a)-len(b)) ) )
{'a': 1, 'b': 2, 'c': 3}
Try this:
a = ['a','b','c']
b = [1,2,3,4,5]
while len(b) < len(a):
b.append(None)
c = dict(zip(a,b))
print(c)

create a dictionary with incrementing values

I have a list and I want to generate a dictionary d taking out duplicates and excluding a single item, such that the first key has value 0, the second has value 1, and so on.
I have written the following code:
d = {}
i = 0
for l in a_list:
if (l not in d) and (l != '<'):
d[l] = i
i += 1
If a_list = ['a', 'b', '<', 'c', 'b', 'd'], after running the code d contains {'a': 0, 'b': 1, 'c': 2, 'd':3}. Order is not important.
Is there a more elegant way to obtain the same result?
Use dict.fromkeys to get your unique occurrences (minus values you don't want), then .update it to apply the sequence, eg:
a_list = ['a', 'b', '<', 'c', 'b', 'd']
d = dict.fromkeys(el for el in a_list if el != '<')
d.update((k, i) for i, k in enumerate(d))
Gives you:
{'a': 0, 'b': 1, 'd': 2, 'c': 3}
If order is important, then use collections.OrderedDict.fromkeys to retain the ordering of the original values, or sort the unique values if they should be alphabetical instead.
{b: a for a, b in enumerate(set(a_list) - {'<'})}
set(a_list) creates a set from a_list.
That effectively strips duplicate numbers in a_list, as a set can only contain unique values.
What is needed here is an ordereddict and to manually filter the list:
from collections import OrderedDict
d = OrderedDict()
new_list = []
a_list = [1,3,2,3,2,1,3,2,3,1]
for i in a_list:
if i not in new_list:
new_list.append(i)
for i, a in enumerate(new_list):
if a != "<":
d[i] = a
Output:
OrderedDict([(0, 1), (1, 3), (2, 2)])
If original order is not important:
final_d = {i:a for i, a in enumerate(set(a_list)) if a != "<"}
I personally find recursion quite elegant, tail-recursion especially so:
def f( d, a_list ):
if a_list:
if a_list[0] not in d and a_list[0] != '<':
d[a_list[0]] = len(d)
return f( d, a_list[1:] )
else:
return d
So that
f( {}, "acbcbabcbabcb" )
will yield
{'a': 0, 'c': 1, 'b': 2}
just like the original code does on the same input (modulo order of the keys).
If truly:
Order is not important.
{k: i for i, k in enumerate(filter(lambda x: x not in "<", set(a_list)))}
# {'a': 3, 'b': 1, 'c': 0, 'd': 2}
EDIT: #qnnnnez's answer takes advantage of set operations, giving an elegant version of the latter code.
Otherwise you can implement the unique_everseen itertools recipe to preserve order. For convenience, you can import it from a library that implements this recipe for you, i.e. more_itertools.
from more_itertools import unique_everseen
{k: i for i, k in enumerate(filter(lambda x: x not in "<", unique_everseen(a_list)))}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3}

How can I find dict keys for matching values in two dicts?

I have two dictionaries mapping IDs to values. For simplicity, lets say those are the dictionaries:
d_source = {'a': 1, 'b': 2, 'c': 3, '3': 3}
d_target = {'A': 1, 'B': 2, 'C': 3, '1': 1}
As named, the dictionaries are not symmetrical.
I would like to get a dictionary of keys from dictionaries d_source and d_target whose values match. The resulting dictionary would have d_source keys as its own keys, and d_target keys as that keys value (in either a list, tuple or set format).
This would be The expected returned value for the above example should be the following list:
{'a': ('1', 'A'),
'b': ('B',),
'c': ('C',),
'3': ('C',)}
There are two somewhat similar questions, but those solutions can't be easily applied to my question.
Some characteristics of the data:
Source would usually be smaller than target. Having roughly few thousand sources (tops) and a magnitude more targets.
Duplicates in the same dict (both d_source and d_target) are not too likely on values.
matches are expected to be found for (a rough estimate) not more than 50% than d_source items.
All keys are integers.
What is the best (performance wise) solution to this problem?
Modeling data into other datatypes for improved performance is totally ok, even when using third party libraries (i'm thinking numpy)
All answers have O(n^2) efficiency which isn't very good so I thought of answering myself.
I use 2(source_len) + 2(dict_count)(dict_len) memory and I have O(2n) efficiency which is the best you can get here I believe.
Here you go:
from collections import defaultdict
d_source = {'a': 1, 'b': 2, 'c': 3, '3': 3}
d_target = {'A': 1, 'B': 2, 'C': 3, '1': 1}
def merge_dicts(source_dict, *rest):
flipped_rest = defaultdict(list)
for d in rest:
while d:
k, v = d.popitem()
flipped_rest[v].append(k)
return {k: tuple(flipped_rest.get(v, ())) for k, v in source_dict.items()}
new_dict = merge_dicts(d_source, d_target)
By the way, I'm using a tuple in order not to link the resulting lists together.
As you've added specifications for the data, here's a closer matching solution:
d_source = {'a': 1, 'b': 2, 'c': 3, '3': 3}
d_target = {'A': 1, 'B': 2, 'C': 3, '1': 1}
def second_merge_dicts(source_dict, *rest):
"""Optimized for ~50% source match due to if statement addition.
Also uses less memory.
"""
unique_values = set(source_dict.values())
flipped_rest = defaultdict(list)
for d in rest:
while d:
k, v = d.popitem()
if v in unique_values:
flipped_rest[v].append(k)
return {k: tuple(flipped_rest.get(v, ())) for k, v in source_dict.items()}
new_dict = second_merge_dicts(d_source, d_target)
from collections import defaultdict
from pprint import pprint
d_source = {'a': 1, 'b': 2, 'c': 3, '3': 3}
d_target = {'A': 1, 'B': 2, 'C': 3, '1': 1}
d_result = defaultdict(list)
{d_result[a].append(b) for a in d_source for b in d_target if d_source[a] == d_target[b]}
pprint(d_result)
Output:
{'3': ['C'],
'a': ['A', '1'],
'b': ['B'],
'c': ['C']}
Timing results:
from collections import defaultdict
from copy import deepcopy
from random import randint
from timeit import timeit
def Craig_match(source, target):
result = defaultdict(list)
{result[a].append(b) for a in source for b in target if source[a] == target[b]}
return result
def Bharel_match(source_dict, *rest):
flipped_rest = defaultdict(list)
for d in rest:
while d:
k, v = d.popitem()
flipped_rest[v].append(k)
return {k: tuple(flipped_rest.get(v, ())) for k, v in source_dict.items()}
def modified_Bharel_match(source_dict, *rest):
"""Optimized for ~50% source match due to if statement addition.
Also uses less memory.
"""
unique_values = set(source_dict.values())
flipped_rest = defaultdict(list)
for d in rest:
while d:
k, v = d.popitem()
if v in unique_values:
flipped_rest[v].append(k)
return {k: tuple(flipped_rest.get(v, ())) for k, v in source_dict.items()}
# generate source, target such that:
# a) ~10% duplicate values in source and target
# b) 2000 unique source keys, 20000 unique target keys
# c) a little less than 50% matches source value to target value
# d) numeric keys and values
source = {}
for k in range(2000):
source[k] = randint(0, 1800)
target = {}
for k in range(20000):
if k < 1000:
target[k] = randint(0, 2000)
else:
target[k] = randint(2000, 19000)
best_time = {}
approaches = ('Craig', 'Bharel', 'modified_Bharel')
for a in approaches:
best_time[a] = None
for _ in range(3):
for approach in approaches:
test_source = deepcopy(source)
test_target = deepcopy(target)
statement = 'd=' + approach + '_match(test_source,test_target)'
setup = 'from __main__ import test_source, test_target, ' + approach + '_match'
t = timeit(stmt=statement, setup=setup, number=1)
if not best_time[approach] or (t < best_time[approach]):
best_time[approach] = t
for approach in approaches:
print(approach, ':', '%0.5f' % best_time[approach])
Output:
Craig : 7.29259
Bharel : 0.01587
modified_Bharel : 0.00682
Here is another solution. There are a lot of ways to do this
for key1 in d1:
for key2 in d2:
if d1[key1] == d2[key2]:
stuff
Note that you can use any name for key1 and key2.
This maybe "cheating" in some regards, although if you are looking for the matching values of the keys regardless of the case sensitivity then you might be able to do:
import sets
aa = {'a': 1, 'b': 2, 'c':3}
bb = {'A': 1, 'B': 2, 'd': 3}
bbl = {k.lower():v for k,v in bb.items()}
result = {k:k.upper() for k,v in aa.iteritems() & bbl.viewitems()}
print( result )
Output:
{'a': 'A', 'b': 'B'}
The bbl declaration changes the bb keys into lowercase (it could be either aa, or bb).
* I only tested this on my phone, so just throwing this idea out there I suppose... Also, you've changed your question radically since I began composing my answer, so you get what you get.
It is up to you to determine the best solution. Here is a solution:
def dicts_to_tuples(*dicts):
result = {}
for d in dicts:
for k,v in d.items():
result.setdefault(v, []).append(k)
return [tuple(v) for v in result.values() if len(v) > 1]
d1 = {'a': 1, 'b': 2, 'c':3}
d2 = {'A': 1, 'B': 2}
print dicts_to_tuples(d1, d2)

pythonic way to filter a dictionary of arrays

I have a dictionary that looks something like this:
d = { 'a':['a','b','c','d'],
'b':['a','b','c','d'],
'c':['a','b','c','d'],
'd':['a','b','c','d'], }
I would like to reduce this dictionary into a new one that contains 2 keys randomly selected from the full set of keys, and also only contains values that correspond to those random keys.
Here is the code I wrote that works, but I feel like there is probably a more pythonic way to do it, any suggestions?
import random
d = { 'a':['a','b','c','d'],
'b':['a','b','c','d'],
'c':['a','b','c','d'],
'd':['a','b','c','d'], }
new_d = {}
r = d.keys()
random.shuffle(r)
r = r[:2]
r_dict = dict( (k,True) for k in r)
for k in r_dict:
a = tuple(d[k])
new_a = []
for item in a:
if item in r_dict:
new_a.append(item)
new_d[k] = new_a
"new_d" has filtered dictionary, for example:
{'a': ['a', 'b'], 'b': ['a', 'b']}
If 'a' and 'b' are the two random keys.
Building on FM's, with the underused set type:
>>> ks = set(random.sample(d, 2))
>>> dict((k, list(ks & set(d[k]))) for k in ks)
{'a': ['a', 'c'], 'c': ['a', 'c']}
How about the following:
import random
rk = random.sample(d.keys(),2)
new_d = {}
for k in rk:
new_d[k] = list(set(d[k]).intersection(rk))
ks = set(random.sample(d.keys(), 2))
nd = dict( (k, list(v for v in d[k] if v in ks)) for k in ks )

how join list tuple and dict into a dict?

how join list tuple and dict into a dict?
['f','b','c','d'] (1,2,3) and {'a':'10'}
d excluded for list be compatible with the tuple
output {'f':'1','b':'2','c':'3','a':'10'}
You can make a dict from keys and values like so:
keys = ['a','b','c','d']
values = (1,2,3)
result = dict(zip(keys, values)) # {'a': 1, 'c': 3, 'b': 2}
Then you can update it with another dict
result.update({ 'f' : 5 })
print result # {'a': 1, 'c': 3, 'b': 2, 'f': 5}
dict(zip(a_list, a_tuple)).update(a_dictionary)
when a_list is your list, a_tuple is your tuple and a_dictionary is your dictionary.
EDIT:
If you really wanted to turn the numbers in you tuple into strings than first do:
new_tuple = tuple((str(i) for i in a_tuple))
and pass new_tuple to the zip function.
This will accomplish the first part of your question:
dict(zip(['a','b','c','d'], (1,2,3)))
However, the second part of your question would require a second definition of 'a', which the dictionary type does not allow. However, you can always set additional keys manually:
>>> d = {}
>>> d['e'] = 10
>>> d
{'e':10}
The keys in a dictionary must be unique, so this part: {'a':'1','a':'10'} is impossible.
Here is code for the rest:
l = ['a','b','c','d']
t = (1,2,3)
d = {}
for key, value in zip(l, t):
d[key] = value
Something like this?
>>> dict({'a':'10'}.items() + (zip(['f','b','c','d'],('1','2','3'))))
{'a': '10', 'c': '3', 'b': '2', 'f': '1'}
Since noone has given an answer that converts the tuple items to str yet
>>> L=['f','b','c','d']
>>> T=(1,2,3)
>>> D={'a':'10'}
>>> dict(zip(L,map(str,T)),**D)
{'a': '10', 'c': '3', 'b': '2', 'f': '1'}
>>> l = ['a','b','c','d']
>>> t = (1,2,3)
>>> d = {'a':'10'}
>>> t = map(str, t) # the OP has requested str values, let's do this first
If you are OK with mutating the original dict, then you can just do this:
>>> d.update(zip(l, t))
or in Python 3.9+ (PEP 584):
>>> d |= zip(l, t)
But if you need to keep the original d intact:
>>> new_d = dict(zip(l, t))
>>> new_d |= d

Categories

Resources