Python dictionary is not staying in order - python

I created a dictionary of the alphabet with a value starting at 0, and is increased by a certain amount depending on the word file. I hard coded the initial dictionary and I wanted it to stay in alphabetical order but it does not at all. I want it to return the dictionary in alphabetical order, basically staying the same as the initial dictionary.
How can i keep it in order?
from wordData import*
def letterFreq(words):
totalLetters = 0
letterDict = {'a':0,'b':0,'c':0,'d':0,'e':0,'f':0,'g':0,'h':0,'i':0,'j':0,'k':0,'l':0,'m':0,'n':0,'o':0,'p':0,'q':0,
'r':0,'s':0,'t':0,'u':0,'v':0,'w':0,'x':0,'y':0,'z':0}
for word in words:
totalLetters += totalOccurences(word,words)*len(word)
for char in range(0,len(word)):
for letter in letterDict:
if letter == word[char]:
for year in words[word]:
letterDict[letter] += year.count
for letters in letterDict:
letterDict[letters] = float(letterDict[letters] / totalLetters)
print(letterDict)
return letterDict
def main():
filename = input("Enter filename: ")
words = readWordFile(filename)
letterFreq(words)
if __name__ == '__main__':
main()

Update for Python 3.7+:
Dictionaries now officially maintain insertion order for Python 3.7 and above.
Update for Python 3.6:
Dictionaries maintain insertion order in Python 3.6, however, this is considered an implementation detail and should not be relied upon.
Original answer - up to and including Python 3.5:
Dictionaries are not ordered and don't keep any order for you.
You could use an ordered dictionary, which maintains insertion order:
from collections import OrderedDict
letterDict = OrderedDict([('a', 0), ('b', 0), ('c', 0)])
Or you could just return a sorted list of your dictionary contents
letterDict = {'a':0,'b':0,'c':0}
sortedList = sorted([(k, v) for k, v in letterDict.iteritems()])
print sortedList # [('a', 0), ('b', 0), ('c', 0)]

You're only needing the keys in order once, so:
# create letterDict as in your question
keys = list(letterDict)
keys.sort()
for key in keys:
# do whatever with letterDict[key]
If you needed them in order more than once, you could use the standard library's collections.OrderedDict. Sometimes that's all you need. It preserves dictionary key order by order of addition.
If you truly need an ordered-by-keys dictionary type, and you don't need it just once (where list_.sort() is better), you could try one of these:
http://stromberg.dnsalias.org/~dstromberg/datastructures/
With regard to the above link, if your keys are getting added in an already-sorted order, you're probably best off with a treap or red-black tree (a treap is better on average, but red-black trees have a lower standard deviation). If your keys are (always) getting added in a randomized order, then the simple binary tree is better.
BTW, current fashion seems to favor sorted(list_) over list_.sort(), but sorted(list_) is a relatively recent addition to the language that we got along fine without before it was added, and it's a little slower. Also, list_.sort() doesn't give rise to one-liner-abuse the way sorted(list_) does.
Oh, and vanilla dictionaries are unordered - that's why they're fast for accessing arbitrary elements (they're built on a hash table). Some of the types at datastructures URL I gave above are good at dict_.find_min() and dict_.find_max() and obviate keys.sort(), but they're slower (logn) at accessing arbitrary elements.

You can sort your dictionary's keys and iterate over your dict.
>>> for key in sorted(letterDict.keys()):
... print ('{}: {}').format(key, letterDict.get(key))
...
a: 0
b: 0
c: 0
d: 0
e: 0
...
OR
This can be a possible solution in your case. We can have all your dictionary's keys in list whose sequence doesn't change and then we can get values in that order from your dictionary.
>>> import string
>>> keys = list(string.ascii_lowercase)
>>> letterDict = {'a':0,'b':0,'c':0,'d':0,'e':0,'f':0,'g':0,'h':0,'i':0,'j':0,'k':0,'l':0,'m':0,'n':0,'o':0,'p':0,'q':0,
... 'r':0,'s':0,'t':0,'u':0,'v':0,'w':0,'x':0,'y':0,'z':0}
>>> for key in keys:
... if key in letterDict:
... print ('{}: {}').format(key, letterDict.get(key))
...
a: 0
b: 0
c: 0
d: 0
e: 0
f: 0
g: 0
h: 0
i: 0
j: 0
k: 0
l: 0
m: 0
....

I wouldn't implement it that way. It's pretty hard to read. Something more like this:
# Make sure that division always gives you a float
from __future__ import division
from collections import defaultdict, OrderedDict
from string import ascii_lowercase
...
letterDict = defaultdict(int)
...
# Replace the for char in range(0,len(word)): loop with this
# Shorter, easier to understand, should be equivalent
for year in words[word]:
for char in word:
letterDict[char] += year.count
...
# Filter out any non-letters at this point
# Note that this is the OrderedDict constructor given a generator that creates tuples
# Already in order since ascii_lowercase is
letterRatio = OrderedDict((letter, letterDict[letter] / totalLetters) for letter in ascii_lowercase)
print(letterRatio)
return letterRatio
...
Now that you're returning an OrderedDict, the order will be preserved. I do caution you, though. If you really need it to be in order at some point, I would just sort it when you need it in the right order. Don't depend on functions that compute new data to return things in a specific sort order. Sort it when you need it sorted, and not before.

Related

Is there anyway to put numbers in a dictionary and randomly pick them and complete random math problems in python? [duplicate]

How can I get a random pair from a dict? I'm making a game where you need to guess a capital of a country and I need questions to appear randomly.
The dict looks like {'VENEZUELA':'CARACAS'}
How can I do this?
One way would be:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'OTTAWA'}
random.choice(list(d.values()))
EDIT: The question was changed a couple years after the original post, and now asks for a pair, rather than a single item. The final line should now be:
country, capital = random.choice(list(d.items()))
I wrote this trying to solve the same problem:
https://github.com/robtandy/randomdict
It has O(1) random access to keys, values, and items.
If you don't want to use the random module, you can also try popitem():
>> d = {'a': 1, 'b': 5, 'c': 7}
>>> d.popitem()
('a', 1)
>>> d
{'c': 7, 'b': 5}
>>> d.popitem()
('c', 7)
Since the dict doesn't preserve order, by using popitem you get items in an arbitrary (but not strictly random) order from it.
Also keep in mind that popitem removes the key-value pair from dictionary, as stated in the docs.
popitem() is useful to destructively iterate over a dictionary
>>> import random
>>> d = dict(Venezuela = 1, Spain = 2, USA = 3, Italy = 4)
>>> random.choice(d.keys())
'Venezuela'
>>> random.choice(d.keys())
'USA'
By calling random.choice on the keys of the dictionary (the countries).
Try this:
import random
a = dict(....) # a is some dictionary
random_key = random.sample(a, 1)[0]
This definitely works.
This works in Python 2 and Python 3:
A random key:
random.choice(list(d.keys()))
A random value
random.choice(list(d.values()))
A random key and value
random.choice(list(d.items()))
Since the original post wanted the pair:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'}
country, capital = random.choice(list(d.items()))
(python 3 style)
If you don't want to use random.choice() you can try this way:
>>> list(myDictionary)[i]
'VENEZUELA'
>>> myDictionary = {'VENEZUELA':'CARACAS', 'IRAN' : 'TEHRAN'}
>>> import random
>>> i = random.randint(0, len(myDictionary) - 1)
>>> myDictionary[list(myDictionary)[i]]
'TEHRAN'
>>> list(myDictionary)[i]
'IRAN'
When they ask for a random pair here they mean a key and value.
For such a dict where the key:values are country:city,
use random.choice().
Pass the dictionary keys to this function as follows:
import random
keys = list(my_dict)
country = random.choice(keys)
You may wish to track the keys that were already called in a round and when getting a fresh country, loop until the random selection is not in the list of those already "drawn"... as long as the drawn list is shorter than the keys list.
Since this is homework:
Check out random.sample() which will select and return a random element from an list. You can get a list of dictionary keys with dict.keys() and a list of dictionary values with dict.values().
I am assuming that you are making a quiz kind of application. For this kind of application I have written a function which is as follows:
def shuffle(q):
"""
The input of the function will
be the dictionary of the question
and answers. The output will
be a random question with answer
"""
selected_keys = []
i = 0
while i < len(q):
current_selection = random.choice(q.keys())
if current_selection not in selected_keys:
selected_keys.append(current_selection)
i = i+1
print(current_selection+'? '+str(q[current_selection]))
If I will give the input of questions = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'} and call the function shuffle(questions) Then the output will be as follows:
VENEZUELA? CARACAS
CANADA? TORONTO
You can extend this further more by shuffling the options also
With modern versions of Python(since 3), the objects returned by methods dict.keys(), dict.values() and dict.items() are view objects*. And hey can be iterated, so using directly random.choice is not possible as now they are not a list or set.
One option is to use list comprehension to do the job with random.choice:
import random
colors = {
'purple': '#7A4198',
'turquoise':'#9ACBC9',
'orange': '#EF5C35',
'blue': '#19457D',
'green': '#5AF9B5',
'red': ' #E04160',
'yellow': '#F9F985'
}
color=random.choice([hex_color for color_value in colors.values()]
print(f'The new color is: {color}')
References:
*Python 3.8: Standard Library Documentation - Built-in types: Dictionary view objects
Python 3.8: Data Structures - List Comprehensions:
I just stumbled across a similar problem and designed the following solution (relevant function is pick_random_item_from_dict; other functions are just for completeness).
import random
def pick_random_key_from_dict(d: dict):
"""Grab a random key from a dictionary."""
keys = list(d.keys())
random_key = random.choice(keys)
return random_key
def pick_random_item_from_dict(d: dict):
"""Grab a random item from a dictionary."""
random_key = pick_random_key_from_dict(d)
random_item = random_key, d[random_key]
return random_item
def pick_random_value_from_dict(d: dict):
"""Grab a random value from a dictionary."""
_, random_value = pick_random_item_from_dict(d)
return random_value
# Usage
d = {...}
random_item = pick_random_item_from_dict(d)
The main difference from previous answers is in the way we handle the dictionary copy with list(d.items()). We can partially circumvent that by only making a copy of d.keys() and using the random key to pick its associated value and create our random item.
Try this (using random.choice from items)
import random
a={ "str" : "sda" , "number" : 123, 55 : "num"}
random.choice(list(a.items()))
# ('str', 'sda')
random.choice(list(a.items()))[1] # getting a value
# 'num'
To select 50 random key values from a dictionary set dict_data:
sample = random.sample(set(dict_data.keys()), 50)
I needed to iterate through ranges of keys in a dict without sorting it each time and found the Sorted Containers library. I discovered that this library enables random access to dictionary items by index which solves this problem intuitively and without iterating through the entire dict each time:
>>> import sortedcontainers
>>> import random
>>> d = sortedcontainers.SortedDict({1: 'a', 2: 'b', 3: 'c'})
>>> random.choice(d.items())
(1, 'a')
>>> random.sample(d.keys(), k=2)
[1, 3]
I found this post by looking for a rather comparable solution. For picking multiple elements out of a dict, this can be used:
idx_picks = np.random.choice(len(d), num_of_picks, replace=False) #(Don't pick the same element twice)
result = dict ()
c_keys = [d.keys()] #not so efficient - unfortunately .keys() returns a non-indexable object because dicts are unordered
for i in idx_picks:
result[c_keys[i]] = d[i]
Here is a little Python code for a dictionary class that can return random keys in O(1) time. (I included MyPy types in this code for readability):
from typing import TypeVar, Generic, Dict, List
import random
K = TypeVar('K')
V = TypeVar('V')
class IndexableDict(Generic[K, V]):
def __init__(self) -> None:
self.keys: List[K] = []
self.vals: List[V] = []
self.dict: Dict[K, int] = {}
def __getitem__(self, key: K) -> V:
return self.vals[self.dict[key]]
def __setitem__(self, key: K, val: V) -> None:
if key in self.dict:
index = self.dict[key]
self.vals[index] = val
else:
self.dict[key] = len(self.keys)
self.keys.append(key)
self.vals.append(val)
def __contains__(self, key: K) -> bool:
return key in self.dict
def __len__(self) -> int:
return len(self.keys)
def random_key(self) -> K:
return self.keys[random.randrange(len(self.keys))]
b = { 'video':0, 'music':23,"picture":12 }
random.choice(tuple(b.items())) ('music', 23)
random.choice(tuple(b.items())) ('music', 23)
random.choice(tuple(b.items())) ('picture', 12)
random.choice(tuple(b.items())) ('video', 0)

Finding if there are distinct elements in a python dictionary

I have a python dictionary containing n key-value pairs, out of which n-1 values are identical and 1 is not. I need to find the key of the distinct element.
For example: consider a python list [{a:1},{b:1},{c:2},{d:1}]. I need the to get 'c' as the output.
I can use a for loop to compare consecutive elements and then use two more for loops to compare those elements with the other elements. But is there a more efficient way to go about it or perhaps a built-in function which I am unaware of?
If you have a dictionary you can quickly check and find the first value which is different from the next two values cycling around the keys of your dictionary.
Here's an example:
def find_different(d):
k = d.keys()
for i in xrange(0, len(k)):
if d[k[i]] != d[k[(i+1)%len(k)]] and d[k[i]] != d[k[(i+2)%len(k)]]:
return k[i]
>>> mydict = {'a':1, 'b':1, 'c':2, 'd':1}
>>> find_different(mydict)
'c'
Otherwise, if what you have is a list of single-key dictionaries, then you can do it quite nicely mapping your list with a function which "extracts" the values from your elements, then check each one using the same logic.
Here's another working example:
def find_different(l):
mask = map(lambda x: x[x.keys()[0]], l)
for i in xrange(0, len(l)):
if mask[i] != mask[(i+1)%len(l)] and mask[i] != mask[(i+2)%len(l)]:
return l[i].keys()[0]
>>> mylist = [{'a':1},{'b':1},{'c':2},{'d':1}]
>>> find_different(mylist)
'c'
NOTE: these solutions do not work in Python 3 as the map function doesn't return a list and neither does the .keys() method of dictionaries.
Assuming that your "list of pairs" (actually list of dictionaries, sigh) cannot be changed:
from collections import defaultdict
def get_pair(d):
return (d.keys()[0], d.values()[0])
def extract_unique(l):
d = defaultdict(list)
for key, value in map(get_pair, l):
d[value].append(key)
return filter(lambda (v,l): len(l) == 1, d.items())[0][1]
If you already have your dictionary, then you make a list of all of the keys: key_list = yourDic.keys(). Using that list, you can then loop through your dictionary. This is easier if you know one of the values, but below I assume that you do not.
yourDic = {'a':1, 'b':4, 'c':1, 'd':1, }
key_list = yourDic.keys()
previous_value = yourDic[key_list[0]] # Making it so loop gets past first test
count = 0
for key in key_list:
test_value = yourDic[key]
if (test_value != previous_value) and count == 1: # Checks first key
print key_list[count - 1]
break
elif (test_value != previous_value):
print key
break
else:
previous_value = test_value
count += 1
So, once you find the value that is different, it will print the key. If you want it to print the value, too, you just need a print test_value statement

Returning unique elements from values in a dictionary

I have a dictionary like this :
d = {'v03':["elem_A","elem_B","elem_C"],'v02':["elem_A","elem_D","elem_C"],'v01':["elem_A","elem_E"]}
How would you return a new dictionary with the elements that are not contained in the key of the highest value ?
In this case :
d2 = {'v02':['elem_D'],'v01':["elem_E"]}
Thank you,
I prefer to do differences with the builtin data type designed for it: sets.
It is also preferable to write loops rather than elaborate comprehensions. One-liners are clever, but understandable code that you can return to and understand is even better.
d = {'v03':["elem_A","elem_B","elem_C"],'v02':["elem_A","elem_D","elem_C"],'v01':["elem_A","elem_E"]}
last = None
d2 = {}
for key in sorted(d.keys()):
if last:
if set(d[last]) - set(d[key]):
d2[last] = sorted(set(d[last]) - set(d[key]))
last = key
print d2
{'v01': ['elem_E'], 'v02': ['elem_D']}
from collections import defaultdict
myNewDict = defaultdict(list)
all_keys = d.keys()
all_keys.sort()
max_value = all_keys[-1]
for key in d:
if key != max_value:
for value in d[key]:
if value not in d[max_value]:
myNewDict[key].append(value)
You can get fancier with set operations by taking the set difference between the values in d[max_value] and each of the other keys but first I think you should get comfortable working with dictionaries and lists.
defaultdict(<type 'list'>, {'v01': ['elem_E'], 'v02': ['elem_D']})
one reason not to use sets is that the solution does not generalize enough because sets can only have hashable objects. If your values are lists of lists the members (sublists) are not hashable so you can't use a set operation
Depending on your python version, you may be able to get this done with only one line, using dict comprehension:
>>> d2 = {k:[v for v in values if not v in d.get(max(d.keys()))] for k, values in d.items()}
>>> d2
{'v01': ['elem_E'], 'v02': ['elem_D'], 'v03': []}
This puts together a copy of dict d with containing lists being stripped off all items stored at the max key. The resulting dict looks more or less like what you are going for.
If you don't want the empty list at key v03, wrap the result itself in another dict:
>>> {k:v for k,v in d2.items() if len(v) > 0}
{'v01': ['elem_E'], 'v02': ['elem_D']}
EDIT:
In case your original dict has a very large keyset [or said operation is required frequently], you might also want to substitute the expression d.get(max(d.keys())) by some previously assigned list variable for performance [but I ain't sure if it doesn't in fact get pre-computed anyway]. This speeds up the whole thing by almost 100%. The following runs 100,000 times in 1.5 secs on my machine, whereas the unsubstituted expression takes more than 3 seconds.
>>> bl = d.get(max(d.keys()))
>>> d2 = {k:v for k,v in {k:[v for v in values if not v in bl] for k, values in d.items()}.items() if len(v) > 0}

In Python, count unique key/value pairs in a dictionary

I have a dictionary that is made with a list of values. Some of these values are also keys or values in other key/value pairs in the dictionary. I would simply like to count how many of these unique pairs there are in the dictionary.
Ex.
dict = {'dog':['milo','otis','laurel','hardy'],'cat':['bob','joe'],'milo':['otis','laurel','hardy','dog'],'bob':['cat','joe'],'hardy':['dog']}
I need to count the number of key/value pairs that do not have share a key/value with another in the dict. For example the above should count to only 2, those connected to dog and cat. Even though milo is unique to dog, dog is also in the key/value pair 'hardy' and both of these should therefore be counted together (ie, only 1). (See comments below)
I have tried to go about it by replacing a key (key A) that exists in the values of another key (key B) with 'key B', without success however as I cannot specify key B correctly.
for keys, values in dict.iteritems():
for key,value in dict.iteriterms():
if key in values:
dict[keys] = dict.pop(key)
Is there an easier method?
Thanks in advance...
If I understand the problem correctly, your dictionary is the adjacency map of a graph and you're trying to find the sets of connected components. The regular algorithm (using a depth- or breadth-first search) may not work correctly since your graph is not undirected (e.g. you have edges from "bob" and "cat" to "joe", but none coming out from "joe").
Instead, I suggest using a disjoint set data structure. It's not hard to build one using a dictionary to handle the mapping of values to parents. Here's an implementation I wrote for a previous question:
class DisjointSet:
def __init__(self):
self.parent = {}
self.rank = {}
def find(self, element):
if element not in self.parent: # leader elements are not in `parent` dict
return element
leader = self.find(self.parent[element]) # search recursively
self.parent[element] = leader # compress path by saving leader as parent
return leader
def union(self, leader1, leader2):
rank1 = self.rank.get(leader1,0)
rank2 = self.rank.get(leader2,0)
if rank1 > rank2: # union by rank
self.parent[leader2] = leader1
elif rank2 > rank1:
self.parent[leader1] = leader2
else: # ranks are equal
self.parent[leader2] = leader1 # favor leader1 arbitrarily
self.rank[leader1] = rank1+1 # increment rank
And here's how you could use it to solve your problem:
djs = DisjointSet()
all_values = set()
for key, values in my_dict.items():
all_values.add(key)
all_values.update(values)
for val in values:
l1 = djs.find(key)
l2 = djs.find(val)
if l1 != l2:
djs.union(l1, l2)
roots = {djs.find(x) for x in all_values}
print("The number of disjoint sets is:", len(roots))
The first part of this code does two things. First it builds a set with all the unique nodes found anywhere in the graph. Secondly, it combines the nodes into disjoint sets by doing a union wherever there's an edge.
The second step is to build up a set of "root" elements from the disjoint set.
Here is one possible solution:
values = {'dog':['milo','otis','laurel','hardy'],
'cat':['bob','joe'],
'milo':['otis','laurel','hardy','dog'],
'bob':['cat','joe'],
'hardy':['dog']}
result = []
for x in values.iteritems():
y = set([x[0]] + x[1])
if not any([z for z in result if z.intersection(y)]):
result.append(y)
print len(result)
Note that you shouldn't call a variable dict because you're shadowing the built-in type dict.
Your goal is unclear, but you can modify the construction of the y set to meet your needs.
If I understand your question correctly, you are trying to describe a graph-like structure, and you're looking at whether the keys appear in a value list. Since you are only interested in count, you don't have to worry about future value lists, when iterating through the dict, so this should work:
d = {'dog': ['milo','otis','laurel','hardy'],'cat': ['bob','joe'],'milo': 'otis','laurel','hardy','dog'], 'bob': ['cat','joe'], 'hardy': ['dog']}
seen = set()
unique = []
for key, values in d.iteritems():
if key not in seen:
unique.append(key)
seen = seen.union(values)
print(len(unique))
Note that the actual values contained in unique are dependent on dict ordering, are are only keys, not values. If you are actually trying to some sort of network or graph analysis, I suggest you make use of a library such as networkx

How to turn a dictionary "inside-out"

Disclaimer: I am just getting started learning Python
I have a function that counts the number of times a word appears in a text file and sets the word as the key and the count as the value, and stores it in a dictionary "book_index". Here is my code:
alice = open('location of the file', 'r', encoding = "cp1252")
def book_index(alice):
"""Alice is a file reference"""
"""Alice is opened, nothing else is done"""
worddict = {}
line = 0
for ln in alice:
words = ln.split()
for wd in words:
if wd not in worddict:
worddict[wd] = 1 #if wd is not in worddict, increase the count for that word to 1
else:
worddict[wd] = worddict[wd] + 1 #if wd IS in worddict, increase the count for that word BY 1
line = line + 1
return(worddict)
I need to turn that dictionary "inside out" and use the count as the key, and any word that appears x amount of times as the value. For instance: [2, 'hello', 'hi'] where 'hello' and 'hi' appear twice in the text file.
Do I need to loop through my existing dictionary or loop through the text file again?
As a dictionary is a key to value mapping, you cannot efficiently filter by the values. So you will have to loop through all elements in the dictionary to get the keys which values have some specific value.
This will print out all keys in the dictionary d where the value is equal to searchValue:
for k, v in d.items():
if v == searchValue:
print(k)
Regarding your book_index function, note that you can use the built-in Counter for counting things. Counter is essentially a dictionary that works with counts as its values and automatically takes care of nonexistant keys. Using a counter, your code would look like this:
from collections import Counter
def book_index(alice):
worddict = Counter()
for ln in alice:
worddict.update(ln.split())
return worddict
Or, as roippi suggested as a comment to another answer, just worddict = Counter(word for line in alice for word in line.split()).
Personally I would suggest the use of a Counter object here, which is specifically made for this kind of application. For instance:
from collections import Counter
counter = Counter()
for ln in alice:
counter.update(ln.split())
This will give you the relevant dictionary, and if you then read the Counter docs
You can just retrieve the most common results.
This might not work in every case in your proposed problem, but it's slightly nicer than manually iterating through even the first time around.
If you really want to "flip" this dictionary you could do something along these lines:
matching_values = lambda value: (word for word, freq in wordict.items() if freq==value)
{value: matching_values for value in set(worddict.values())}
The above solution has some advantages over other solutions in that the lazy execution means that for very sparse cases where you're not looking to make a lot of calls to this function, or just discover which value actually have corresponding entries, this will be faster as it won't actually iterate through the dictionary.
That said, this solution will usually be worse than the vanilla iteration solution since it actively iterates through the dictionary every time you need a new number.
Not radically different, but I didn't want to just copy the other answers here.
Loop through your existing dictionary, here is an example using dict.setdefault():
countdict = {}
for k, v in worddict.items():
countdict.setdefault(v, []).append(k)
Or with collections.defaultdict:
import collections
countdict = collections.defaultdict(list)
for k, v in worddict.items():
countdict[v].append(k)
Personally I prefer the setdefault() method because the result is a regular dictionary.
Example:
>>> worddict = {"hello": 2, "hi": 2, "world": 4}
>>> countdict = {}
>>> for k, v in worddict.items():
... countdict.setdefault(v, []).append(k)
...
>>> countdict
{2: ['hi', 'hello'], 4: ['world']}
As noted in some of the other answers, you can significantly shorten your book_index function by using collections.Counter.
Without duplicates:
word_by_count_dict = {value: key for key, value in worddict.iteritems()}
See PEP 274 to understand dictionary comprehension with Python: http://www.python.org/dev/peps/pep-0274/
With duplicates:
import collections
words_by_count_dict = collections.defaultdict(list)
for key, value in worddict.iteritems():
words_by_count_dict[value].append(key)
This way:
words_by_count_dict[2] = ["hello", "hi"]

Categories