Getting second and third highest value in a dictionary - dictionary

dict = {'a':5 , 'b':4, 'c':3, 'd':3, 'e':1}
Second is 'b' with 4 times. Joint third are 'c' and 'd' with 3 times. As the dictionary changes over time, say, 'f' is added with a value of 3, then third will be 'c', 'd', and 'f' with 3 times.

just create a gencomp of tuple value,key and sort it. Print items
d = {'a':5 , 'b':4, 'c':3, 'd':3, 'e':1}
x = sorted(((v,k) for k,v in d.items()))
print(x[-2][1])
print(x[-3][1])
result:
b
d
(would fail if dict doesn't have at least 3 items)
Or directly with key parameter (avoids data reordering)
x = sorted(d.items(),key=(lambda i: i[1]))
print(x[-2][0])
print(x[-3][0])
result:
b
d
BTW avoid using dict as a variable.
EDIT: since there are several identical values, you may want to get the 2 second best values and the associated letters. You have to do it differently. I'd create a default list using key as value and store in a list, then sort it as done in the above code:
import collections
d = {'a':5 , 'b':4, 'c':3, 'd':3, 'e':1}
dd = collections.defaultdict(list)
for k,v in d.items():
dd[v].append(k)
x = sorted(dd.items())
print(x[-2])
print(x[-3])
result:
(4, ['b'])
(3, ['c', 'd'])

Related

How can I print duplicate items from an OrderedDict?

My OrderedDict contains 5 key value pairs which result in the word, "Belle". Every letter its own key with values starting from 1 through 5, from left to right, B = 1, e = 2, etc. Printing this OrderedDict returns only 3 letters, B, l and e. Notice, it doesn't print the repeated e and ls.
In the code below, I use a for statement to print "Belle" so it can print vertically. My goal is to print the complete word "Belle" vertically, with each letter as a key with its value pair.
text6 = OrderedDict({'B':1, 'e':2, 'l':3, 'l':4, 'e':5})
for key, value in text6.items():
print(key, value)
Code above returns:
B 1
e 5
l 4
Desried output:
B 1
e 2
l 3
l 4
e 5
Asking a dict (including OrderedDict) to do what you are asking it to do is impossible. A dict of any type can only contain unique keys. All subsequently added pre-existing keys will overwrite the key and reset it's value.
In order to get the output you want to see, we can use a different data structure. In this implementation, we utilize a list which will contain a tuple for each (value, index) pair we will get by iterating through "Belle" using enumerate
list((v, i) for i, v in enumerate("Belle", 1))

Groovy map creation, use value assigned to previous key

Is there a way to use the values assigned to the previous key in map, for eg:
def x = [
a: someList.sum(),
b: anotherList.sum(),
c: someList.sum() / anotherList.sum()
]
I want the value of 'c' to be a/b, so is there a shortcut so that I don't have to recompute the sums while computing 'c'
In order to use previously-added key/values to compute new key/values, you must be able to control the order in which the keys/values are added. I know that's obvious, but what may not be obvious is that Groovy Map declarations do not take order into account. For example, if you write this...
def x = [
a: 8,
b: 2,
c: a / b
]
..., when evaluating the expression for the value of key c, Groovy will attempt to access a variable or property named a, which will fail because the variable/property does not exist. However, you can take advantage of that property lookup and do this:
def x = [:].with {
a = 8
b = 2
c = a / b
delegate
}
You start by creating an empty Map. Then, use with(Closure) to execute putAt() and get() against the Map. The example above is the equivalent to...
def x = [:].with {
putAt('a', 8)
putAt('b', 2)
putAt('c', get('a') / get('b'))
delegate
}
Finally, return the Map itself so that it's assigned to x.

Use one dictionary to fetch value in another dictionary

I have this problem where I have 2 dictionaries. One dict has keys representative of names (i.e. sample 1, sample 2, etc.) and the values are coordinates. The second dict has keys that are coordinates, and the values are data for each coordinate.
I need to build a 3rd dict that takes the names (keys), and then retrieves the respective values from the 2nd dict. As seen below:
dict1 = {32.0: [[(1, 7)], [(1, 17)], [(1, 8)], [(1, 18)]]}
dict2 = {(1, 7): 25.746392, (1, 18): 19.4782, (1, 17): 21.7492, (1, 8): 34.492}
dict3 = {32.0: [[25.746392], [21.7492], [34.492], [19.4782]]}
There are many more data points. I'm not sure if this is a simple problem, or if I'm having issues due to how nested the first dict is.
I'm still relatively new to Python, so any help is appreciated!
Two nested for-loops:
dict3 = dict1
for k in dict3.keys():
for i in range(len(dict3[k])):
dict3[k][i] = [dict2[dict3[k][i][0]]]
print dict3
Output:
{32.0: [[25.746392], [21.7492], [34.492], [19.4782]]}
One-liner:
dict3 = {k: [[dict2[dict1[k][i][0]]] for i in range(len(dict1[k]))] for k in dict1.keys()}

Pythonic way to iterate over a collections.Counter() instance in descending order?

In Python 2.7, I want to iterate over a collections.Counter instance in descending count order.
>>> import collections
>>> c = collections.Counter()
>>> c['a'] = 1
>>> c['b'] = 999
>>> c
Counter({'b': 999, 'a': 1})
>>> for x in c:
print x
a
b
In the example above, it appears that the elements are iterated in the order they were added to the Counter instance.
I'd like to iterate over the list from highest to lowest. I see that the string representation of Counter does this, just wondering if there's a recommended way to do it.
You can iterate over c.most_common() to get the items in the desired order. See also the documentation of Counter.most_common().
Example:
>>> c = collections.Counter(a=1, b=999)
>>> c.most_common()
[('b', 999), ('a', 1)]
Here is the example to iterate the Counter in Python collections:
>>>def counterIterator():
... import collections
... counter = collections.Counter()
... counter.update(('u1','u1'))
... counter.update(('u2','u2'))
... counter.update(('u2','u1'))
... for ele in counter:
... print(ele,counter[ele])
>>>counterIterator()
u1 3
u2 3
Your problem was solved for just returning descending order but here is how to do it generically. In case someone else comes here from Google here is how I had to solve it. Basically what you have above returns the keys for the dictionary inside collections.Counter(). To get the values you just need to pass the key back to the dictionary like so:
for x in c:
key = x
value = c[key]
I had a more specific problem where I had word counts and wanted to filter out the low frequency ones. The trick here is to make a copy of the collections.Counter() or you will get "RuntimeError: dictionary changed size during iteration" when you try to remove them from the dictionary.
for word in words.copy():
# remove small instance words
if words[word] <= 3:
del words[word]

Appending data to an AT Field using transmogrifier

I have a CSV file of data like this:
1, [a, b, c]
2, [a, b, d]
3, [a]
and some Plone objects which should be updated like this:
ID, LinesField
a, [1,2,3]
b, [1,2]
c, [1]
d, [2]
So, to clarify, the object with the id a is named on lines 1, 2 and 3 of the CSV, and thus the LinesField property of object a needs to have those line ids (the first number on the line) listed.
Ideally I'd like to use Transmogrifier to import this information (and avoid doing any manipulation in Excel beforehand), and I can see two ways, theoretically of doing this, but I can't work out how to do this in practice. I'd be grateful for some pointers to examples. I think that either I need to transform the entire pipeline so that the items reflect the structure of my Plone objects and then use the ATSchemaUpdater blueprint, but I can't see any examples on how to add items to the pipeline (do I need to write my own blueprint?) Or, alternatively I could loop through the items as they exist and append the value in the left column to the items in the list in the right. For that I need a way of appending values with ATSchemaUpdater rather than overwriting them - again, is there a blueprint for that anywhere?
Here's a few sample csv lines:
"Name","Themes"
"Bessie Brown","cah;cab;cac"
"Fred Blogs","cah;cac"
"Dinah Washington","cah;cab"
The Plone object will be a theme and the lines field a list of names:
cah, ['Bessie Brown', 'Fred Boggs' etc etc]
I'm not pretty sure you want to read the CVS file using transmogrifier, but I think you can create a section to insert these values to the items in the pipeline using a function like this:
def transpose(cvs):
keys = []
[keys.extend(v) for v in cvs.values()]
keys = set(keys)
d = {}
for key in keys:
values = [k for k, v in cvs.iteritems() if key in v]
d[key] = values
return d
In this context, cvs is {1: ['a', 'b', 'c'], 2: ['a', 'b', 'd'], 3: ['a']}; keys will contain all possible values set(['a', 'c', 'b', 'd']); and d will be what you want {'a': [1, 2, 3], 'c': [1], 'b': [1, 2], 'd': [2]}.
Probably there are better ways to do it, but I'm not a Python magician.
The insert section could look like this one:
class Insert(object):
"""Insert new keys into items.
"""
classProvides(ISectionBlueprint)
implements(ISection)
def __init__(self, transmogrifier, name, options, previous):
self.previous = previous
self.new_keys = transpose(cvs)
def __iter__(self):
for item in self.previous:
item.update(self.new_keys)
yield item
After that you can use the SchemaUpdater section.

Resources