Python - Get key of specific tuple index minimum in dictionary of tuples - python

I have a dict of tuples such as:
d = {'a': (3, 5), 'b': (5, 8), 'c': (9, 3)}
I want to return the key of the minimum of the tuple values based on the tuple index. For example, if using tuple index = 0, then 'a' would be returned. if index = 1, then 'c' would be returned. I have tried using min(), for example
min(d, key=d.get)
but am not sure how to manipulate it to select the tuple index to use. Although there are similar questions, I have not found an answer to this. Apologies in advance if this is a duped question, and please link to the answer. Thanks

You can write a lambda function to get the elements from the value by their index:
min(d, key=lambda k: d[k][0])
# 'a'
min(d, key=lambda k: d[k][1])
# 'c'

Since multiple keys could have the same value, you might want to return a list of matching keys, not just a single key.
def min_keys(d, index):
# Initialize lists
values = []
matches = []
# Append tuple items to list based on index
for t in list(d.values()):
values.append(t[index])
# If the item matches the min, append the key to list
for key in d:
if d[key][index] == min(values):
matches.append(key)
# Return a list of all keys with min value at index
return matches

Dictionaries are unsorted and have no index.
If you want the return the key alphabetically first you could use the ascii order:
print(chr(min([ord(key) for key in d.keys()])))

Here's a portable method you can use for dicts with a structure like yours, and feel free to choose the index of interest in the tuple:
def extract_min_key_by_index(cache, index):
min_val = float('inf')
min_key = 0
for k, v in d.iteritems():
if v[index] < min_val:
min_key, min_val = k, v[index]
return min_key
d = {'a': (3, 5), 'b': (5, 8), 'c': (9, 3)}
INDEX = 0
print extract_min_key_by_index(d, INDEX)

Related

Looping through a list adding values to dictionaries

I have a list of 4 dictionaries. I also have a list of 36 values. I'm trying to loop through the list of dictionaries, filling each dictionary with the list values in ascending order having item 1 as the key, 2 & 3 as the value attached and so on. The final result being 4 dictionaries, each with three keys and each key having two values attached.
The end result would be
dict1={x:(7,4),y:(7,8),z:(7:22)}
dict2={x: (111,4),y:(111,8),z:(111:22)}
...
Currently I have the following which does not work.
my_list = ['x',7,4,'y',7,8,'z',7,22,'x',111,4,'y',111,8,'z',111,22 and so on]
dict1={}
dict2={}
dict3={}
dict4={}
my_dict_list=[dict1,dict2,dict3,dict4]
for dicts in my_dict_list:
for x in range (0,len(my_list),3):
dicts[my_list[x]] = my_list[x+1],my_list[x+2]
break
the output of that code being the first 3 items in my list, in all four of the dictionaries. As so:
>>> dict_1
{'x': (7, 4)}
>>> dict_2
{'x': (7, 4)}
>>> dict_3
{'x': (7, 4)}
>>> dict_4
{'x': (7, 4)}
I think this is the closest I've got so far as it is actually filling each dictionary, previously I've only managed to fill the first dictionary and other similar wrong scenarios. Can anyone help or point me in the right direction?
Try this:
my_list = ['x',7,4,'y',7,8,'z',7,22,'x',111,4,'y',111,8,'z',111,22]
my_dict_list = []
idx = 0
current_dict = {}
while idx < len(my_list):
key, *vals = my_list[idx : idx + 3]
# If key repeats, starting a new current_dict, old one adding to my_dict_list
if key in current_dict:
my_dict_list.append(current_dict)
current_dict = {}
current_dict[key] = vals
idx += 3
if current_dict:
my_dict_list.append(current_dict)
print(my_dict_list)
In the solution above we loop my_list reading 3 values at a time. And in case if key of a dictionary is repeated this is interpreted and a starting of a new dictionary so the old one must be stored in my_dict_list.

How to get a set of keys with largest values?

I am working on a function
def common_words(dictionary, N):
if len(dictionary) > N:
max(dictionary, key=dictionary.get)
Description of the function is:
The first parameter is the dictionary of word counts and the second is
a positive integer N. This function should update the dictionary so
that it includes the most common (highest frequency words). At most N
words should be included in the dictionary. If including all words
with some word count would result in a dictionary with more than N
words, then none of the words with that word count should be included.
(i.e., in the case of a tie for the N+1st most common word, omit all
of the words in the tie.)
So I know that I need to get the N items with the highest values but I am not sure how to do that. I also know that once I get N items that if there are any duplicate values that I need to pop them out.
For example, given
k = {'a':5, 'b':4, 'c':4, 'd':1}
then
common_words(k, 2)
should modify k so that it becomes {'a':5}.
Here's my algorithm for this problem.
Extract the data from the dictionary into a list and sort it in descending order on the dictionary values.
Clear the original dictionary.
Group the sorted data into groups that have the same value.
Re-populate the dictionary with the all (key, value) pairs from each group in the sorted list if that will keep the total dictionary size <= N. If adding a group would make the total dictionary size > N, then return.
The grouping operation can be easily done using the standard itertools.groupby function.
To perform the sorting and grouping we need an appropriate key function, as described in the groupby, list and sorted docs. Since we need the second item of each tuple we could use
def keyfunc(t):
return t[1]
or
keyfunc = lambda t: t[1]
but it's more efficient to use operator.itemgetter.
from operator import itemgetter
from itertools import groupby
def common_words(d, n):
keyfunc = itemgetter(1)
lst = sorted(d.items(), key=keyfunc, reverse=True)
d.clear()
for _, g in groupby(lst, key=keyfunc):
g = list(g)
if len(d) + len(g) <= n:
d.update(g)
else:
break
# test
data = {'a':5, 'b':4, 'c':4, 'd':1}
common_words(data, 4)
print(data)
common_words(data, 2)
print(data)
output
{'c': 4, 'd': 1, 'b': 4, 'a': 5}
{'a': 5}
my algorithm as below
1st build tuple list from dictionary sorted based on value from
largest to smallest
check for if item[N-1] match item[N] value, if yes, drop item[N-1]
(index start from 0, so -1 there)
finally, convert the slice of tuple list up to N element back to
dict, may change to use OrderedDict here if wanna retain the items order
it will just return the dictionary as it is if the dictionary length is less than N
def common_words(dictionary, N):
if len(dictionary) > N:
tmp = [(k,dictionary[k]) for k in sorted(dictionary, key=dictionary.get, reverse=True)]
if tmp[N-1][1] == tmp[N][1]:
N -= 1
return dict(tmp[:N])
# return [i[0] for i in tmp[:N]] # comment line above and uncomment this line to get keys only as your title mention how to get keys
else:
return dictionary
# return dictionary.keys() # comment line above and uncomment this line to get keys only as your title mention how to get keys
>>> common_words({'a':5, 'b':4, 'c':4, 'd':1}, 2)
{'a': 5}
OP wanna modify input dictionary within function and return None, it can be modified as below
def common_words(dictionary, N):
if len(dictionary) > N:
tmp = [(k,dictionary[k]) for k in sorted(dictionary, key=dictionary.get, reverse=True)]
if tmp[N-1][1] == tmp[N][1]:
N -= 1
# return dict(tmp[:N])
for i in tmp[N:]:
dictionary.pop(i[0])
>>> k = {'a':5, 'b':4, 'c':4, 'd':1}
>>> common_words(k, 2)
>>> k
{'a': 5}

comparing list of tuple elements python

I have a two list of tuples
t1 = [ ('a',3,4), ('b',3,4), ('c',4,5) ]
t2 = [ ('a',4,6), ('c',3,4), ('b',3,6), ('d',4,5) ]
Such that
the order of the tuples may not be the same order and
the lists may not contain the same amount of tuple elements.
My goal is to compare the two lists such that if the string element matches, then compare the last integer element in the tuple and return a list containing -1 if t1[2] < t2[2], 0 if they are equal and 1 if they are greater than.
I've tried different variations but the problem i have is finding a way to match the strings to do proper comparison.
return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) ]
Where diff_unique does the aforementioned comparison of the integers, and new_list is t1 and old_list is t2.
I've also tried this:
return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) if(x[0]==y[0]]
What I intend to do is use the returned list and create a new four-tuple list with the original t1 values along with the difference from the matching t2 tuple. i.e
inc_dec_list = compare_list(new,old)
final_list = [ (f,r,u,chge) for (f,r,u), chge in zip(new,inc_dec_list)]
Where new = t1 and old = t2. This may have been an important detail, sorry I missed it.
Any help in the right direction?
Edit: I have added my test case program that mimicks what my original intent is for those who want to help. Thank you all.
import os
import sys
old = [('a',10,1),('b',10,2),('c',100,4),('d',200,4),('f',45,2)]
new = [('a',10,2),('c',10,2),('b',100,2),('d',200,6),('e',233,4),('g',45,66)]
def diff_unique(a,b):
print "a:{} = b:{}".format(a,b)
if a < b:
return -1
elif a==b:
return 0
else:
return 1
def compare_list(new_list, old_list):
a = { t[0]:t[1:] for t in new_list }
b = { t[0]:t[1:] for t in old_list }
common = list( set(a.keys())&set(b.keys()))
return [diff_unique(a[key][1], b[key][1]) for key in common]
#get common tuples
#common = [x for x,y in zip(new_list,old_list) if x[0] == y[0] ]
#compare common to old list
#return [diff_unique(x[2],y[2]) for x,y in zip(new_list,old_list) ]
inc_dec_list = compare_list(new,old)
print inc_dec_list
final_list = [ (f,r,u,chge) for (f,r,u), chge in zip(new,inc_dec_list)]
print final_list
To match the tuples by string from different lists, you can use dict comprehension (order inside the tuples is preserved):
a = {t[0]:t[1:] for t in t1} # {'a': (3, 4), 'c': (4, 5), 'b': (3, 4)}
b = {t[0]:t[1:] for t in t1} # {'a': (4, 6), 'c': (3, 4), 'b': (3, 6), 'd': (4, 5)}
Then you can iterate over the keys of both dictionaries and do the comparison. Assuming you only want to do the comparison for keys/tuples present in t1 and t2, you can join the keys using sets:
common_keys = list(set(a.keys())&set(b.keys()))
And finally compare the dictionary's items and create the list you want like this:
return [diff_unique(a[key][1],b[key][1]) for key in common_keys ]
If you need the output in the order of the alphabetically sorted characters, use the sorted function on the keys:
return [diff_unique(a[key][1],b[key][1]) for key in sorted(common_keys) ]
If you want all keys to be considered, you can do the following:
all_keys = list(set(a.keys()+b.keys()))
l = list()
for key in sorted(all_keys):
try:
l.append(diff_unique(a[key][1],b[key][1]))
except KeyError:
l.append("whatever you want")
return l
With the new information about what values should be returned in what order, the solution would be this:
ordered_keys = [t[0] for t in t1]
a = {t[0]:t[1:] for t in t1} # {'a': (3, 4), 'c': (4, 5), 'b': (3, 4)}
b = {t[0]:t[1:] for t in t1} # {'a': (4, 6), 'c': (3, 4), 'b': (3, 6), 'd': (4, 5)}
l = list()
for key in sorted(ordered_keys):
try:
l.append(diff_unique(a[key][1],b[key][1]))
except KeyError:
l.append(0) # default value
return l
First, build a default dictionary from each list, with the default value for a nonexistent key being a tuple whose last element is the smallest possible value for a comparison.
SMALL = (-float['inf'],)
from collections import defaultdict
d1 = defaultdict(lambda: SMALL, [(t[0], t[1:]) for t in t1])
d2 = defaultdict(lambda: SMALL, [(t[0], t[1:]) for t in t2])
Next, iterate over the keys in each dictionary (which can be created easily with itertools.chain). You probably want to sort the keys for the resulting list to have any meaning (otherwise, how do you know which keys produced which of -1/0/1?)
from itertools import chain
all_keys = set(chain(d1, d2))
result = [cmp(d1[k][-1], d2[k][-1]) for k in sorted(all_keys)]
Here is a simple solution of your problem,
It is not one line as you tried. I hope it will still help you
for a in t1:
for b in t2:
if a[0] != b[0]:
continue
return cmp(a[-1], b[-1])
In python 3.x, you can compare two lists of tuples
a and b thus:
import operator
a = [(1,2),(3,4)]
b = [(3,4),(1,2)]
# convert both lists to sets before calling the eq function
print(operator.eq(set(a),set(b))) #True

retrieving keys from dictionaries depending on value in python

I'm trying to find the most efficient way in python to create a dictionary of 'guids' (point ids in rhino) and retrieve them depending on the value(s) I assign them, change that value(s) and restoring them back in the dictionary. One catch is that with Rhinoceros3d program the points have a random generated ID number which I don't know so I can only call them depending on the value I give them.
are dictionaries the correct way? should the guids be the value instead of the keys?
a very basic example :
arrPts=[]
arrPts = rs.GetPoints() # ---> creates a list of point-ids
ptsDict = {}
for ind, pt in enumerate(arrPts):
ptsDict[pt] = ('A'+str(ind))
for i in ptsDict.values():
if '1' in i :
print ptsDict.keys()
how can I make the above code print the key that has the value '1' , instead of all the keys? and then change the key's value from 1 to e.g. 2 ?
any help also on the general question would be appreciated to know I'm in the right direction.
Thanks
Pav
You can use dict.items().
An example:
In [1]: dic={'a':1,'b':5,'c':1,'d':3,'e':1}
In [2]: for x,y in dic.items():
...: if y==1:
...: print x
...: dic[x]=2
...:
a
c
e
In [3]: dic
Out[3]: {'a': 2, 'b': 5, 'c': 2, 'd': 3, 'e': 2}
dict.items() returns a list of tuples containing keys and value pairs in python 2.x:
In [4]: dic.items()
Out[4]: [('a', 2), ('c', 2), ('b', 5), ('e', 2), ('d', 3)]
and in python 3.x it returns an iterable view instead of list.
I think you want the GUID's to be values, not keys, since it looks like you want to look them up by something you assign. ...but it really depends on your use case.
# list of GUID's / Rhinoceros3d point ids
arrPts = ['D20EA4E1-3957-11d2-A40B-0C5020524153',
'1D2680C9-0E2A-469d-B787-065558BC7D43',
'ED7BA470-8E54-465E-825C-99712043E01C']
# reference each of these by a unique key
ptsDict = dict((i, value) for i, value in enumerate(arrPts))
# now `ptsDict` looks like: {0:'D20EA4E1-3957-11d2-A40B-0C5020524153', ...}
print(ptsDict[1]) # easy to "find" the one you want to print
# basically make both keys: `2`, and `1` point to the same guid
# Note: we've just "lost" the previous guid that the `2` key was pointing to
ptsDict[2] = ptsDict[1]
Edit:
If you were to use a tuple as the key to your dict, it would look something like:
ptsDict = {(loc, dist, attr3, attr4): 'D20EA4E1-3957-11d2-A40B-0C5020524153',
(loc2, dist2, attr3, attr4): '1D2680C9-0E2A-469d-B787-065558BC7D43',
...
}
As you know, tuples are immutable, so you can't change the key to your dict, but you can remove one key and insert another:
oldval = ptsDict.pop((loc2, dist2, attr3, attr4)) # remove old key and get value
ptsDict[(locx, disty, attr3, attr4)] = oldval # insert it back in with a new key
In order to have one key point to multiple values, you'd have to use a list or set to contain the guids:
{(loc, dist, attr3, attr4): ['D20E...', '1D2680...']}

Search and sort through dictionary in Python

I need to sort and search through a dictionary. I know that dictionary cannot be sorted. But all I need to do search through it in a sorted format. The dictionary itself is not needed to be sorted.
There are 2 values. A string, which is a key and associated with the key is an integer value. I need to get a sorted representation based on the integer. I can get that with OrderedDict.
But instead of the whole dictionary I need to print just the top 50 values. And I need to extract some of the keys using RegEx. Say all the keys starting with 'a' and of 5 length.
On a side note can someone tell me how to print in a good format in python? Like:
{'secondly': 2,
'pardon': 6,
'saves': 1,
'knelt': 1}
insdead of a single line. Thank you for your time.
If you want to sort the dictionary based on the integer value you can do the following.
d = {'secondly': 2, 'pardon': 6, 'saves': 1, 'knelt': 1}
a = sorted(d.iteritems(), key=lambda x:x[1], reverse=True)
The a will contain a list of tuples:
[('pardon', 6), ('secondly', 2), ('saves', 1), ('knelt', 1)]
Which you can limit to a top 50 by using a[:50] and then search through the keys, with youre search pattern.
There are a bunch of ways to get a sorted dict, sorted and iteritems()are your friends.
data = {'secondly': 2, 'pardon': 6, 'saves': 1, 'knelt': 1}
The pattern I use most is:
key = sorted(data.iteritems())
print key #[('knelt', 1), ('pardon', 6), ('saves', 1), ('secondly', 2)]
key_desc = sorted(data.iteritems(), reverse=True)
print key_desc #[('secondly', 2), ('saves', 1), ('pardon', 6), ('knelt', 1)]
To sort on the value and not the key you need to override sorted's key function.
value = sorted(data.iteritems(), key=lambda x:x[1])
print value #[('saves', 1), ('knelt', 1), ('secondly', 2), ('pardon', 6)]
value_desc = sorted(data.iteritems(),key=lambda x:x[1], reverse=True)
print value_desc #[('pardon', 6), ('secondly', 2), ('saves', 1), ('knelt', 1)]
For nice formatting check out the pprint module.
If I'm understanding correctly, an OrderedDict isn't really what you want. OrderedDicts remember the order in which keys were added; they don't track the values. You could get what you want using generators to transform the initial data:
import re, operator
thedict = {'secondly':2, 'pardon':6, ....}
pat = re.compile('^a....$') # or whatever
top50 = sorted(((k,v) for (k,v) in thedict.iteritems() if pat.match(k)), reverse=True, key=operator.itemgetter(1))[:50]
As you're using OrderedDict already, you can probably do what you need with a list comprehension. Something like:
[ value for value in d.values()[:50] if re.match('regex', value) ]
Please post your current code if you need something more specific.
For the multi-line pretty print, use pprint with the optional width parameter if needed:
In [1]: import pprint
In [2]: d = {'a': 'a', 'b': 'b' }
In [4]: pprint.pprint(d)
{'a': 'a', 'b': 'b'}
In [6]: pprint.pprint(d,width=20)
{'a': 'a',
'b': 'b'}
There are a few different tools that can help you:
The sorted function takes an iterable and iterates through the elements in order. So you could say something like for key, value in d.iteritems().
The filter function takes an iterable and a function, and returns only those elements for which the function evaluates to True. So, for instance, filter(lambda x: your_condition(x), d.iteritems()) would give you a list of key-value tuples, which you could then sort through as above. (In Python 3, filter returns an iterator, which is even better.)
Generator expressions let you combine all of the above into one. For instance, if you only care about the values, you could write (value for key, value in sorted(d.iteritems()) if condition), which would return an iterator.
you could sort though they keys of the dicionary :
dict = {'secondly': 2,
'pardon': 6,
'saves': 1,
'knelt': 1}
for key in sorted(dict.keys()):
print dict[key]
This will sort your output based on the keys.(in your case the string values alphabetically)

Categories

Resources