I'm working on a program to search through a dictionaries value set and perform a method on values that match a user input. I have to compare and sort these values.
This is the code I'm working with right now
Code for value search and compare (very rough)
import nation
import pickle
KK = 1000000
pickle_in = open("nationsDict.dat","rb")
d = pickle.load(pickle_in)
k = raw_input("Enter a continent: ")
for value in d.values():
if k in d.values()[0]:
print d.values()[0]
Code for Nation class
class Nations:
KK = 1000000
def __init__(self, ctry, cont, pop, area):
self.country = ctry
self.continent = cont
self.population = float(pop)
self.area = float(area)
def popDensity(self):
popDensity = (self.population*self.KK) / self.area
popDensity = str(round(popDensity, 2))
return popDensity
Code for creating pickle dictionary
import nation
import pickle
i=0
dictUN = {}
with open('UN.txt') as f:
for line in f:
"""Data get from file"""
elements = line.strip().split(",")
n = nation.Nations(elements[0],elements[1],elements[2],elements[3])
"""Density"""
n.popDensity()
print "The density of", n.country, "is",n.popDensity(),"people per square mile."
"""Dictionary creation"""
dictVal = (n.continent, n.population, n.area)
dictUN.update({n.country: dictVal})
pickle_out = open("nationsDict.dat", "wb")
pickle.dump(dictUN, pickle_out)
pickle_out.close()
Here's a snippet from UN.txt
Mauritania,Africa,3.5,397954
Mauritius,Africa,1.3,787
Mexico,North America,120.3,761606
Micronesia,Australia/Oceania,.11,271
Monaco,Europe,.031,0.76
Mongolia,Asia,3.0,603909
Montenegro,Europe,.65,5019
Morocco,Africa,33.0,172414
My problems at this point are pretty contained to the value search and compare. Specifically, my program has to
Allow the user to search a continent (first element in value list)
Perform the method, Nations.popDensity (contained in nation class) on all matching countries
Compare the nations and return the top 5 density values per continent
I would say my one big question is how to handle the search of a dictionary by an element in a value. I've also considered about making a temp dictionary with the continent element as the key, but I'm not sure that would make my life any easier as I have to perform the popDensity method on it.
Any help is appreciated, Thanks!
Initialize pandas series object
Iterate through the dictionary.
If the continent matches:
a. calculate the population density.
b. if the value is larger than the smallest value in the pandas series:
i. remove the last entry
ii.append the value into the pandas series values and the country to the index
iii. sort the pandas series object ascending = False
If you're going to do this repeatedly, then creating a continent->country dictionary definitely will save time.
Glad it was helpful. I'll add it as an answer, so you can accept it, if you like.
Just as there is list comprehension, there is dictionary comprehension... It's pretty cool stuff! d2 = {k:d[k] for k in d.keys() if <some_elem> in d[k]} would give you a dict with a subset of the original dict that satisfies your requirements. You would have to fill in the <some_elem> in d[k] portion, because I haven't gone through all your code. You said that this is the main Q you have. Hopefully this gives you enough to solve it.
This question already has answers here:
convert tuple keys of dict into a new dict
(3 answers)
Closed 6 years ago.
Apologies it appears my original explanation wasn't clear so I've updated the below as best I can.
I have created a dictionary from a list of fields and a fixed width file of data by slicing the data file up.
data_format_keys = {
("CURRENT-RATIO", 120, 127),
("ACID-TEST", 127, 134),
("NOTES", 134, 154
}
When printing this out I get the following...
Current Ratio = 1234
Acid Test = 5678
Notes = These are my notes
For the data in Current Ratio (1234) and Acid test (5678) I need to convert the strings to numerics and insert decimal points for use in calculations (these fields are from a mainframe file so are so need to be converted to the correct formats).
The expected output is...
Current Ratio = 12.34 #This is an integer/float
Acid Test = 5.678 #This is an integer/float
Notes = These are my notes #This is still a string
I've created a list of fields that need to be converted from the original list but I'm struggling with how to apply the conversion
for k,v in data_format_keys.items():
if k in data_format_keys == headerdict[i[1]]:
line = line.replace(k, v)
fo.write(line)
print(headerdict)
Where headerdict is the initial dictionary created and data_format_keys is the list of fields for converting.
Any thoughts?
Thanks
You can use formatted output if you like.
Here is an example:
#Inputs
meal_cost = float(input("Enter meal price: "))
drink_cost = float(input("Enter drinks cost: "))
#Calculation
total_cost = meal_cost + drink_cost
#Output
print("Your total bill is {:.2f} USD".format(total_cost))
Your output will look like this:
Enter meal price: 7.49
Enter drinks cost: 2.99
Your total bill is 10.48 USD
Let me know if this helped. :)
Try this:
#createlist from current output
thelist = [('Current Ratio', 1234), ('Acid Test', 5678), ('Notes', 'These are my notes') ]
#create dict with just notes
notes = dict([i for i in thelist if i[0] == "Notes"])
#create list without notes
thelist = [i for i in thelist if i[0] != "Notes"]
#create list of keys
thelist1 = [i[0] for i in thelist]
#create list of formatted numbers
thelist2 = [float(i[1]/100) for i in thelist]
#merge into dict
thelist = dict(zip(thelist1, thelist2))
#create empty dict
desired = {}
#update dict with previously created dicts
desired.update(thelist)
desired.update(notes)
print (desired)
Someone better at python may be able to write more efficient code, but this should be a good starting point.
I run into a problem when attempting to solve this task so I'm here after failing a few times, I was wondering how could I only print the highest value(score) for a key (name) when a key stores multipile values such as:
Rob Scored: 3,5,6,2,8
Martin Scored: 4,3,1,5,6,2
Tom Scored: 7,2,8
The name being the key and the scores being the values. Now I wish to get an output of
Martin Scored: 6
Rob Scored: 8
Tom Scored: 8
However when I attempted the max function it would ignore the alphabetical order. Just as a side not that is a requirement as well as the fact that the other scores must be kept stored for later stages.
from collections import OrderedDict
dictionary = {}
for line in f:
firstpart, secondpart = line.strip().split(':')
dictionary[firstpart.strip()] = secondpart.strip()
columns = line.split(": ")
letters = columns[0]
numbers = columns[1].strip()
if d.get(letters):
d[letters].append(numbers)
else:
d[letters] = list(numbers)
sorted_dict = OrderedDict(
sorted((key, list(sorted(vals, reverse=True)))
for key, vals in d.items()))
print (sorted_dict)
This does what you want:
# You don't need to use an OrderedDict if you only want to display in
# sorted order once
score_dict = {} # try using a more descriptive variable name
with open('score_file.txt') as infile:
for line in infile:
name_field, scores = line.split(':') # split the line
name = name_field.split()[0] # split the name field and keep
# just the name
# grab all the scores, strip off whitespace, convert to int
scores = [int(score.strip()) for score in scores.split(',')]
# store name and scores in dictionary
score_dict[name] = scores
# if names can appear multiple times in the input file,
# use this instead of your current if statement:
#
# score_dict.setdefault(name, []).extend(scores)
# now we sort the dictionary keys alphabetically and print the corresponding
# values
for name in sorted(score_dict.keys()):
print("{} Scored: {}".format(name, max(score_dict[name])))
Please give this document a read: Code Like a Pythonista. It has a lot of suggestions for how to write better code, and it is where I learned the dict.setdefault() method for dealing with dictionaries where the values are lists.
On another note, in your question, you referred to an attempt to use the max function, but that function isn't anywhere in the code you provided. If you refer to failed attempts to accomplish something in your question, you should include the failed code as well so we can help you debug it. I was able to give you some code that accomplishes your task along with some other suggestions, but I can't debug your original code if you don't provide it. Since this is obviously a homework question, you should definitely spend some time figuring out why it didn't work in the first place.
I am a newbie to python programming. I am working on a class homework and got the code below so far. The next step that am struggling with is to write a function the would show / print the lowest score and average score. Any direction would be much appreciated.
scores = """Aturing:Mark$86:
Inewton:Mark$67.5:
Cdarwin:Mark$90:
Fnightingale:Mark$99:
Cvraman:Mark$10:"""
students = {}
for studentdata in scores.split('\n'):
data = studentdata.split(':')
name = data[0]
students[name] = {}
for class_data in data[1:]:
if class_data:
Mark,class_score = class_data.split('$')
students[name][Mark] = class_score
def Grade_Show(student,Mark):
if student in students:
if Mark in students[student]:
print "Student %s got %s in the assignment %s" % (student,students[student][Mark],Mark)
else:
print "subject %s not found for student %s" % (Mark,student)
else:
print "student %s not found" % (student)
#do some testing
Grade_Show("Inewton","Mark")
Testing with: scores = {'alex': 1, 'dave': 1, 'mike': 2};
Firstly, to find the lowest score, use the min() function.
So:
min_keys = [k for k, x in scores.items() if not any(y < x for y in scores.values())]
print('Lowest score:', str(min(scores.values())) + '.', 'Achieved by: ')
for student in min_keys:
print(student)
Output:
Lowest score: 1. Achieved by:
alex
dave
Secondly, assuming you are looking for the mean average, you would do this:
print('The average score was:', str(sum(scores.values()) / len(scores)))
Output:
The average score was: 1.3333333333333333
Hope I helped!- All you need to do now is create a function containing that code, with a parameter called data. That way you can have multiple dictionaries to represent different classes or tests. You would replace all instances of score in the code with data.
Also, the 'minimum score' code could be easily modified to give the maximum score. Finally, depending on the size of your program you could store the output in a variable rather than using a print statement so you can recall it later. This would also mean that you should return the result, not print it.
The next step that am struggling with is to write a function the would
show / print the lowest score and average score.
Step 1:
Can you iterate through your data structure (students) and print only the scores? If you can do that, then you should be able to run through and find the lowest score.
To find the lowest score, start with some imagined maximum possible value (set some variable equal to 100, for example, if that's the highest possible) and iterate through all the scores (for score in score..., etc.), testing to see if each value you get is lower than the variable you created.
If it is lower, make the variable you created equal to that lower value. After that, it will continue iterating to see if any new value is less than this new 'lowest' value. By the time it reaches the end, it should have provided you with the lowest value.
One tricky part is making sure to print both the name and lowest value, if that's what the question requires.
Step: 2
To solve the average problem, you'll do to something similar where you iterate over the scores, add them to a new data structure and then figure out how to take an average of them.
First: you don't have to code this for me, unless you're a super awesome nice guy. But since you're all great at programming and understand it so much better than me and all, it might just be easier (since it's probably not too many lines of code) than writing paragraph after paragraph trying to make me understand it.
So - I need to make a list of high scores that updates itself upon new entries. So here it goes:
First step - done
I have player-entered input, which has been taken as a data for a few calculations:
import time
import datetime
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
a = raw_input("Enter weight: ")
b = raw_input("Enter height: ")
c = a/b
Second step - making high score list
Here, I would need some sort of a dictionary or a thing that would read the previous entries and check if the score (c) is (at least) better than the score of the last one in "high scores", and if it is, it would prompt you to enter your name.
After you entered your name, it would post your name, your a, b, c, and time in a high score list.
This is what I came up with, and it definitely doesn't work:
list = [("CPU", 200, 100, 2, time1)]
player = "CPU"
a = 200
b = 100
c = 2
time1 = "20.12.2012, 21:38"
list.append((player, a, b, c, time1))
list.sort()
import pickle
scores = open("scores", "w")
pickle.dump(list[-5:], scores)
scores.close()
scores = open("scores", "r")
oldscores = pickle.load(scores)
scores.close()
print oldscores()
I know I did something terribly stupid, but anyways, thanks for reading this and I hope you can help me out with this one. :-)
First, don't use list as a variable name. It shadows the built-in list object. Second, avoid using just plain date strings, since it is much easier to work with datetime objects, which support proper comparisons and easy conversions.
Here is a full example of your code, with individual functions to help divide up the steps. I am trying not to use any more advanced modules or functionality, since you are obviously just learning:
import os
import datetime
import cPickle
# just a constants we can use to define our score file location
SCORES_FILE = "scores.pickle"
def get_user_data():
time1 = datetime.datetime.now()
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
a = None
while True:
a = raw_input("Enter weight: ")
try:
a = float(a)
except:
continue
else:
break
b = None
while True:
b = raw_input("Enter height: ")
try:
b = float(b)
except:
continue
else:
break
c = a/b
return ['', a, b, c, time1]
def read_high_scores():
# initialize an empty score file if it does
# not exist already, and return an empty list
if not os.path.isfile(SCORES_FILE):
write_high_scores([])
return []
with open(SCORES_FILE, 'r') as f:
scores = cPickle.load(f)
return scores
def write_high_scores(scores):
with open(SCORES_FILE, 'w') as f:
cPickle.dump(scores, f)
def update_scores(newScore, highScores):
# reuse an anonymous function for looking
# up the `c` (4th item) score from the object
key = lambda item: item[3]
# make a local copy of the scores
highScores = highScores[:]
lowest = None
if highScores:
lowest = min(highScores, key=key)
# only add the new score if the high scores
# are empty, or it beats the lowest one
if lowest is None or (newScore[3] > lowest[3]):
newScore[0] = raw_input("Enter name: ")
highScores.append(newScore)
# take only the highest 5 scores and return them
highScores.sort(key=key, reverse=True)
return highScores[:5]
def print_high_scores(scores):
# loop over scores using enumerate to also
# get an int counter for printing
for i, score in enumerate(scores):
name, a, b, c, time1 = score
# #1 50.0 jdi (20.12.2012, 15:02)
print "#%d\t%s\t%s\t(%s)" % \
(i+1, c, name, time1.strftime("%d.%m.%Y, %H:%M"))
def main():
score = get_user_data()
highScores = read_high_scores()
highScores = update_scores(score, highScores)
write_high_scores(highScores)
print_high_scores(highScores)
if __name__ == "__main__":
main()
What it does now is only add new scores if there were no high scores or it beats the lowest. You could modify it to always add a new score if there are less than 5 previous scores, instead of requiring it to beat the lowest one. And then just perform the lowest check after the size of highscores >= 5
The first thing I noticed is that you did not tell list.sort() that the sorting should be based on the last element of each entry. By default, list.sort() will use Python's default sorting order, which will sort entries based on the first element of each entry (i.e. the name), then mode on to the second element, the third element and so on. So, you have to tell list.sort() which item to use for sorting:
from operator import itemgetter
[...]
list.sort(key=itemgetter(3))
This will sort entries based on the item with index 3 in each tuple, i.e. the fourth item.
Also, print oldscores() will definitely not work since oldscores is not a function, hence you cannot call it with the () operator. print oldscores is probably better.
Here are the things I notice.
These lines seem to be in the wrong order:
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
When the user enters the height and weight, they are going to be read in as strings, not integers, so you will get a TypeError on this line:
c = a/b
You could solve this by casting a and b to float like so:
a = float(raw_input("Enter weight: "))
But you'll probably need to wrap this in a try/catch block, in case the user puts in garbage, basically anything that can't be cast to a float. Put the whole thing in a while block until they get it right.
So, something like this:
b = None
while b == None:
try:
b = float(raw_input("Enter height: "))
except:
print "Weight should be entered using only digits, like '187'"
So, on to the second part, you shouldn't use list as a variable name, since it's a builtin, I'll use high_scores.
# Add one default entry to the list
high_scores = [("CPU", 200, 100, 2, "20.12.2012, 4:20")]
You say you want to check the player score against the high score, to see if it's best, but if that's the case, why a list? Why not just a single entry? Anyhow, that's confusing me, not sure if you really want a high score list, or just one high score.
So, let's just add the score, no matter what:
Assume you've gotten their name into the name variable.
high_score.append((name, a, b, c, time1))
Then apply the other answer from #Tamás
You definitely don't want a dictionary here. The whole point of a dictionary is to be able to map keys to values, without any sorting. What you want is a sorted list. And you've already got that.
Well, as Tamás points out, you've actually got a list sorted by the player name, not the score. On top of that, you want to sort in downward order, not upward. You could use the decorate-sort-undecorate pattern, or a key function, or whatever, but you need to do something. Also, you've put it in a variable named list, which is a very bad idea, because that's already the name of the list type.
Anyway, you can find out whether to add something into a sorted list, and where to insert it if so, using the bisect module in the standard library. But it's probably simpler to just use something like SortedCollection or blist.
Here's an example:
highscores = SortedCollection(scores, key=lambda x: -x[3])
Now, when you finish the game:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[-1]
That's it. If you were actually not in the top 10, you'll be added at #11, then removed. If you were in the top 10, you'll be added, and the old #10 will now be #11 and be removed.
If you don't want to prepopulate the list with 10 fake scores the way old arcade games used to, just change it to this:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[10:]
Now, if there were already 10 scores, when you get added, #11 will get deleted, but if there were only 3, nothing gets deleted, and now there are 4.
Meanwhile, I'm not sure why you're writing the new scores out to a pickle file, and then reading the same thing back in. You probably want to do the reading before adding the highscore to the list, and then do the writing after adding it.
You also asked how to "beautify the list". Well, there are three sides to that.
First of all, in the code, (player, a, b, c, time1) isn't very meaningful. Giving the variables better names would help, of course, but ultimately you still come down to the fact that when accessing list, you have to do entry[3] to get the score or entry[4] to get the time.
There are at least three ways to solve this:
Store a list (or SortedCollection) of dicts instead of tuples. The code gets a bit more verbose, but a lot more readable. You write {'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1}, and then when accessing the list, you do entry['score'] instead of entry[3].
Use a collection of namedtuples. Now you can actually just insert ScoreEntry(player, a, b, c, time1), or you can insert ScoreEntry(player=player, height=a, weight=b, score=c, time=time1), whichever is more readable in a given case, and they both work the same way. And you can access entry.score or as entry[3], again using whichever is more readable.
Write an explicit class for score entries. This is pretty similar to the previous one, but there's more code to write, and you can't do indexed access anymore, but on the plus side you don't have to understand namedtuple.
Second, if you just print the entries, they look like a mess. The way to deal with that is string formatting. Instead of print scores, you do something like this:
print '\n'.join("{}: height {}, weight {}, score {} at {}".format(entry)
for entry in highscores)
If you're using a class or namedtuple instead of just a tuple, you can even format by name instead of by position, making the code much more readable.
Finally, the highscore file itself is an unreadable mess, because pickle is not meant for human consumption. If you want it to be human-readable, you have to pick a format, and write the code to serialize that format. Fortunately, the CSV format is pretty human-readable, and most of the code is already written for you in the csv module. (You may want to look at the DictReader and DictWriter classes, especially if you want to write a header line. Again, there's the tradeoff of a bit more code for a lot more readability.)