How do I fix the RuntimeError in this program? - python

This program is a function that takes dictionary and it must return a new dictionary with mirror image of the original dictionary (meaning key:value pairs are switched).
However, according to pythontutor.com, for the line of code that says the for loop, it throws a RuntimeError.
I am using the latest version of Python (3.4.1)
#Program purpose: Write a function called rvBirthday that takes
# dictionary birthday as input. It returns a
# mirror image of the dictionary, where birthday
# is the key and name is the value.
def rvBirthday(birthday):
reverseBD = {}
for key in birthday.keys():
date = birthday.get(key)
birthday.pop(key)
reverseBD[date] = key
return reverseBD
birthday = {'Mitsuyuki Washida':'3-29-93', 'Joe Bob':'7-12-96',
'Sam Wilson':'4-1-02'}
print(rvBirthday(birthday))
The error I get is:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in rvBirthday
RuntimeError: dictionary changed size during iteration

You are altering the input dictionary while looping over it by using dict.pop(). This changes the dictionary size, and that breaks iteration.
Your instructions say nothing about removing keys from the input dictionary. Remove the dict.pop() calls altogether.
You don't need to use .keys() or .get() here either. Looping over a dictionary yields keys, so you don't have to use a separate method to extract the keys. You then know that those keys are in the dictionary, so .get() to return default if it is missing is also redundant.
Better loop over the dictionary items; this gives you both the key and the value in one step:
def rvBirthday(birthday):
reverseBD = {}
for key, date in birthday.items():
reverseBD[date] = key
return reverseBD
This can be expressed with a dictionary comprehension too:
def rvBirthday(birthday):
return {date: key for key, date in birthday.items()}
If you still need to clear the input dictionary, simply add a birthday.clear() call after copying across the key-value pairs.

Related

Is there a way to fix appending float values to dictionaries in Python?

I'm currently writing code that's supposed to read a file that has the dates and magnitudes of the major earthquakes in recent years and return a dictionary where the keys are the dates the earthquakes took place, and then the values are the magnitudes of the earthquakes that happened on that date.
My code currently looks like this:
def magnitudedictionary():
earth = open("earthquakes.txt", "r")
magdict = {}
for line in earth:
alist = line.split()
magnitude= float(alist[0])
date = alist[1]
if date in magdict:
magdict[date].append(magnitude)
else:
magdict[date] = magnitude
earth.close()
return magdict
But whenever I try to run the code, I always get a Traceback that says:
Traceback (most recent call last):
File "/Users/MargaretJagger/PycharmProjects/Homework 6/Q2.py", line 18, in <module> magnitudedictionary()
File "/Users/MargaretJagger/PycharmProjects/Homework 6/Q2.py", line 10, in magnitudedictionary
magdict[date].append(magnitude)
AttributeError: 'float' object has no attribute 'append'
Process finished with exit code 1
I'm not quite sure what the issue is exactly, but I know that it has something to do with the float and the dictionary values not matching up.
You probably want a defaultDict for this. Then you can avoid the test and just push into the values.
Here's a simple mockup:
from collections import defaultdict
earth = '''7.6 20190801
8.2 20180201
7.1 20190801
6.5 20190801
4.2 20180201'''
magdict = defaultdict(list) # values will default to new lists
for line in earth.split('\n'):
alist = line.split(' ')
magnitude= float(alist[0])
date = alist[1]
magdict[date].append(magnitude) #magdict[date] will default to a list if the key doesn't already exist
print(magdict['20190801'])
>>> [7.6, 7.1, 6.5]
the values are the magnitudes of the earthquakes that happened on that date.
Since you are talking of “magnitudes”, plural, I assume that you want to be able to store multiple values per date. That means that you should also make sure that your dictionary values are actual lists that store multiple values, instead of just a single value.
Compare the following example dictionaries:
{
"2019-04-17": 2.1,
"2019-04-18": 3.5
}
{
"2019-04-17": [1.7, 2.5],
"2019-04-18": [3.2]
}
The first dictionary only maps the date to a single float. So for every date key, you get a single value. The second dictionary maps to a list of floats. Such a list can only contain a single value, or many (it could also contain none).
When you look at your code that sets the values in the dictionary, you can see that you actually built this with multiple values in mind:
if date in magdict:
magdict[date].append(magnitude)
else:
magdict[date] = magnitude
When there’s already the date in the dictionary, then you want to append it. Otherwise you set the date/value pair directly (which adds the key). It’s just that the way you do it, you are setting a single float value (i.e. the first dictionary type above) instead of a list of floats.
So what you need to do instead is create a list of floats here:
if date in magdict:
magdict[date].append(magnitude)
else:
magdict[date] = [magnitude]
The [magnitude] create a one-element list with magnitude as the first value. Since the value in your dictionary is now a list, calls to append() will succeed and correctly add another value to the list.
The error is in the else clause.
It should be magdict[date] = [magnitude] and not magdict[date] = magnitude.
The python dictionary has a very nice method, setdefault, that should help here:
def magnitudedictionary():
earth = open("earthquakes.txt", "r")
magdict = {}
for line in earth:
alist = line.split()
magnitude= float(alist[0])
date = alist[1]
magdict.setdefault(date, []).append(magnitude)
earth.close()
return magdict
Here is a small bit of documentation on the method in question: https://www.tutorialspoint.com/python/dictionary_setdefault.htm

python call function in get method of dictionary instead default value

Need some help in order to understand some things in Python and get dictionary method.
Let's suppose that we have some list of dictionaries and we need to make some data transformation (e.g. get all names from all dictionaries by key 'name'). Also I what to call some specific function func(data) if key 'name' was not found in specific dict.
def func(data):
# do smth with data that doesn't contain 'name' key in dict
return some_data
def retrieve_data(value):
return ', '.join([v.get('name', func(v)) for v in value])
This approach works rather well, but as far a I can see function func (from retrieve_data) call each time, even key 'name' is present in dictionary.
If you want to avoid calling func if the dictionary contains the value, you can use this:
def retrieve_data(value):
return ', '.join([v['name'] if 'name' in v else func(v) for v in value])
The reason func is called each time in your example is because it gets evaluated before get even gets called.

Add multiple values to dictionary

Here is my code:
for response in responses["result"]:
ids = {}
key = response['_id'].encode('ascii')
print key
for value in response['docs']:
ids[key].append(value)
Traceback:
File "people.py", line 47, in <module>
ids[key].append(value)
KeyError: 'deanna'
I am trying to add multiple values to a key. Throws an error like above
Check out setdefault:
ids.setdefault(key, []).append(value)
It looks to see if key is in ids, and if not, sets that to be an empty list. Then it returns that list for you to inline call append on.
Docs:
http://docs.python.org/2/library/stdtypes.html#dict.setdefault
If I'm reading this correctly your intention is to map the _id of a response to its docs. In that case you can bring down everything you have above to a dict comprehension:
ids = {response['_id'].encode('ascii'): response['docs']
for response in responses['result']}
This also assumes you meant to have id = {} outside of the outermost loop, but I can't see any other reasonable interpretation.
If the above is not correct,
You can use collections.defaultdict
import collections # at top level
#then in your loop:
ids = collections.defaultdict(list) #instead of ids = {}
A dictionary whose default value will be created by calling the init argument, in this case calling list() will produce an empty list which can then be appended to.
To traverse the dictionary you can iterate over it's items()
for key, val in ids.items():
print(key, val)
The reason you're getting a KeyError is this: In the first iteration of your for loop, you look up the key in an empty dictionary. There is no such key, hence the KeyError.
The code you gave will work, if you first insert an empty list into the dictionary under to appropriate key. Then append the values to the list. Like so:
for response in responses["result"]:
ids = {}
key = response['_id'].encode('ascii')
print key
if key not in ids: ## <-- if we haven't seen key yet
ids[key] = [] ## <-- insert an empty list into the dictionary
for value in response['docs']:
ids[key].append(value)
The previous answers are correct. Both defaultdict and dictionary.setdefault are automatic ways of inserting the empty list.

Accessing nested values in nested dictionaries in Python 3.3

I'm writing in Python 3.3.
I have a set of nested dictionaries (shown below) and am trying to search using a key at the lowest level and return each of the values that correspond to the second level.
Patients = {}
Patients['PatA'] = {'c101':'AT', 'c367':'CA', 'c542':'GA'}
Patients['PatB'] = {'c101':'AC', 'c367':'CA', 'c573':'GA'}
Patients['PatC'] = {'c101':'AT', 'c367':'CA', 'c581':'GA'}
I'm trying to use a set of 'for loops' to search pull out the value attached to the c101 key in each Pat* dictionary nested under the main Patients dictionary.
This is what I have so far:
pat = 'PatA'
mutations = Patients[pat]
for Pat in Patients.values(): #iterate over the Pat* dictionaries
for mut in Pat.keys(): #iterate over the keys in the Pat* dictionaries
if mut == 'c101': #when the key in a Pat* dictionary matches 'c101'
print(Pat[mut].values()) #print the value attached to the 'c101' key
I get the following error, suggesting that my for loop returns each value as a string and that this can't then be used as a dictionary key to pull out the value.
Traceback (most recent call last):
File "filename", line 13, in
for mut in Pat.keys():
AttributeError: 'str' object has no attribute 'keys'
I think I'm missing something obvious to do with the dictionaries class, but I can't quite tell what it is. I've had a look through this question, but I don't think its quite what I'm asking.
Any advice would be greatly appreciated.
Patients.keys() gives you the list of keys in Patients dictionary (['PatA', 'PatC', 'PatB']) not the list of values hence the error. You can use dict.items to iterate over key: value pairs like this:
for patient, mutations in Patients.items():
if 'c101' in mutations.keys():
print(mutations['c101'])
To make your code working:
# Replace keys by value
for Pat in Patients.values():
# Iterate over keys from Pat dictionary
for mut in Pat.keys():
if mut == 'c101':
# Take value of Pat dictionary using
# 'c101' as a key
print(Pat['c101'])
If you want you can create list of mutations in simple one-liner:
[mutations['c101'] for p, mutations in Patients.items() if mutations.get('c101')]
Patients = {}
Patients['PatA'] = {'c101':'AT', 'c367':'CA', 'c542':'GA'}
Patients['PatB'] = {'c101':'AC', 'c367':'CA', 'c573':'GA'}
Patients['PatC'] = {'c101':'AT', 'c367':'CA', 'c581':'GA'}
for keys,values in Patients.iteritems():
# print keys,values
for keys1,values1 in values.iteritems():
if keys1 is 'c101':
print keys1,values1
#print values1

Adding a new key to a nested dictionary in python

I need to add a key with a value that increases by one for every item in the nested dictionary. I have been trying to use the dict['key']='value' syntax but can't get it to work for a nested dictionary. I'm sure it's a very simple.
My Dictionary:
mydict={'a':{'result':[{'key1':'value1','key2':'value2'},
{'key1':'value3','key2':'value4'}]}}
This is the code that will add the key to the main part of the dictionary:
for x in range(len(mydict)):
number = 1+x
str(number)
mydict[d'index']=number
print mydict
#out: {d'index':d'1',d'a'{d'result':[...]}}
I want to add the new key and value to the small dictionaries inside the square parentheses:
{'a':{'result':[{'key1':'value1',...,'index':'number'}]}}
If I try adding more layers to the last line of the for loop I get a traceback error:
Traceback (most recent call last):
File "C:\Python27\program.py", line 34, in <module>
main()
File "C:\Python27\program.py", line 23, in main
mydict['a']['result']['index']=number
TypeError: list indices must be integers, not unicode
I've tried various different ways of listing the nested items but no joy. Can anyone help me out here?
The problem is that mydict is not simply a collection of nested dictionaries. It contains a list as well. Breaking up the definition helps clarify the internal structure:
dictlist = [{'key1':'value1','key2':'value2'},
{'key1':'value3','key2':'value4'}]
resultdict = {'result':dictlist}
mydict = {'a':resultdict}
So to access the innermost values, we have to do this. Working backwards:
mydict['a']
returns resultdict. Then this:
mydict['a']['result']
returns dictlist. Then this:
mydict['a']['result'][0]
returns the first item in dictlist. Finally, this:
mydict['a']['result'][0]['key1']
returns 'value1'
So now you just have to amend your for loop to iterate correctly over mydict. There are probably better ways, but here's a first approach:
for inner_dict in mydict['a']['result']: # remember that this returns `dictlist`
for key in inner_dict:
do_something(inner_dict, key)
I'm not fully sure what you're trying to do, but I think itertools.count would be able to help here.
>>> c = itertools.count()
>>> c.next()
0
>>> c.next()
1
>>> c.next()
2
>>> c.next()
3
... and so on.
Using this, you can keep incrementing the value that you want to use in your dicts
Hope this helps

Categories