Related
I'm making a program in school where users are quizzed on certain topics and their results are saved into a csv file. I've managed to print off the row with the highest score, but this doesn't look very neat.
with open ('reportForFergusTwo.csv', 'r') as highScore:
highScoreFinder=highScore
valid3=False
for row in highScoreFinder:
if subjectInput in row:
if difficultyInput in row:
if ('10' or '9' or '8' or '7' or '6' or '5' or '4' or '3' or '2' or '1') in row:
valid3=True
print("The highest score for this quiz is:",row)
For example: it says, "The highest score for this quiz is: chemistry,easy,10,Luc16" but I would prefer it to say something like "The highest score for this quiz is: 10" and "This score was achieved by: Luc16", rather than just printing the whole row off, with unnecessary details like what the quiz was on.
My CSV file looks like this:
Subject,Difficulty,Score,Username
language,easy,10,Luc16
chemistry,easy,10,Luc16
maths,easy,9,Luc16
chemistry,easy,5,Eri15
chemistry,easy,6,Waf1
chemistry,easy,0,Eri15
I thought that maybe if I could find a way to take the individual results (the score and username) and put them into their own individual variables, then it would be much easier to present it the way I want, and be able to reference them later on in the function if I need them to be displayed again.
I'm just fairly new to coding and curious if this can be done, so I can improve the appearance of my code.
Edit: To solve the issue, I used str.split() to break up the indivudal fields in the rows of my CSV, so that they could be selected and held by a variable. The accepted answer shows the solution I used, but this is my final code in case this wasn't clear
with open ('details.csv', 'r') as stalking:
stalkingReader=csv.reader(stalking)
valid4=False
for column in stalkingReader:
if user in column[3]:
valid4=True
print("Here are the details for user {}... ".format(user))
splitter=row.split(',')
name=splitter[0]
age=splitter[1]
year=splitter[2]
print("Name: {}".format(name))
print("Age: {}".format(age))
print("Year Group: {}".format(year))
postReport()
if valid4==False:
print("Sorry Fergus, this user doesn't seem to be in our records.")
with open("reportForFergusTwo.csv", "r") as highScore:
subject = []
difficulty = []
score = []
name = []
for line in highScore:
subject.append(line.split(',')[0])
difficulty.append(line.split(',')[1])
score.append(line.split(',')[2])
name.append(line.split(',')[3])
ind = score.index(max(score)
print("The highest score for this quiz is: ", max(score))
print("This was achieved by ", name[ind])
with opens (and will close) the .csv file.
Then, four empty lists are created.
Next, I loop through every line in the file, and I split every line using a comma as the delimiter. This produces a list of four elements, which are appended to each list.
You can use str.split() to break up the rows of your CSV so that you can individually reference the fields:
split_row = row.split(',')
score = split_row[2]
user = split_row[3]
print("The highest score for this quiz is: " + score)
print("This score was achieved by: " + user)
You can use csv library
import csv
with open("data", "r") as f:
reader = csv.reader(f)
# skip header
reader.next()
# organize data in 2D array
data = [ [ sub, dif, int(score), name ] for sub, dif, score, name in reader ]
# sort by score
data.sort(key=lambda x: x[2], reverse=True)
# pretty print
print "The highest score for this quiz is:", data[0][2]
print "This score was achieved by:", data[0][3]
(Posted solution on behalf of the OP).
To solve the issue, I used str.split() to break up the indivudal fields in the rows of my CSV, so that they could be selected and held by a variable. The accepted answer shows the solution I used, but this is my final code in case this wasn't clear
with open ('details.csv', 'r') as stalking:
stalkingReader=csv.reader(stalking)
valid4=False
for column in stalkingReader:
if user in column[3]:
valid4=True
print("Here are the details for user {}... ".format(user))
splitter=row.split(',')
name=splitter[0]
age=splitter[1]
year=splitter[2]
print("Name: {}".format(name))
print("Age: {}".format(age))
print("Year Group: {}".format(year))
postReport()
if valid4==False:
print("Sorry Fergus, this user doesn't seem to be in our records.")
I've been struggling with this for two days now and I can't seem to find any help. I need to search the file for a student ID (1001 is my test ID being used) and then add the numbers in each line that takes place below each occurrence of the student ID together in order to get an average.
filename = input("Enter file name: \n"
"Example: Grade Data.txt \n")
myFile = open(filename, "r")
selectSID = input("Enter SID: \n")
gradesNum = myFile.read().count(selectSID)
grades = myFile.read()
gradetotal = sum()
average = (gradetotal/gradesNum)
print(average)
The text file that is being opened looks like this:
1001
95
1002
99
1001
96
1002
0
1001
84
1002
25
1001
65
1002
19
This looks like homework so I don't want to write the code for you but here is a pseudo code (there are multiple ways to achieve what you want, this is just a simple beginner level code):
Open file to read
get two lines from the file
is the line1 interesting to me?
yes -> store value from line2 in an array
no -> ignore line2
close file
get average
Some useful references:
Python I/O
Powerful things in python to help with I/O
Built-in functions to help with basic operations like sum
from collections import defaultdict
# with open('filename') as f:
# file = [for i in f]
# in this case, it's the list below
file = [1001,95,1002,99,1001,96,1002,0,1001,84,1002,25,1001,65,1002,19]
infos = defaultdict(list)
sids = file[::2] # select sid info
grades = file[1::2] # select grade info
for sid,grade in zip(sids,grades):
infos[sid].append(grade)
print(infos[1001])
print(infos[1002])
out:
[95, 96, 84, 65]
[99, 0, 25, 19]
in this point, you can sum, average, max or min whatever you want.
Please don't use this code for your homework (use #Aditya's method); you need to learn the basics before using fancy libraries. However, I just learned about collections.defaultdict and I wanted to use it. Watch this video for a great demo on defaultdict.
import collections
import statistics
# This little guy will hold all of our grades
# https://youtu.be/lyDLAutA88s is a great video using it
grades = collections.defaultdict(list)
def get_next_num(file):
"""get the next line of a file,
remove any whitespace surrounding it,
and turn it into an integer"""
return int(next(file).strip())
with open('tmp.txt') as myfile:
while True:
try:
# seriously, watch the video
grades[get_next_num(myfile)].append(get_next_num(myfile))
except StopIteration: # end of file
break
student_id = int(input('Enter student ID. Choices: {} : '.format(list(grades.keys()))))
print(statistics.mean(grades[student_id]))
Updated Answer:
Okay, so I think I understand your question now... Same thing, except I suggest using a list, and as long as the file stays in the same format (SID, Score, so on...), this should work, and requires minimal understanding of Python (i.e No weird libraries like glob):
filename = input("Enter file name: \n"
"Example: Grade Data.txt \n")
myFile = open(filename, "r")
selectSID = input("Enter SID: \n")
raw = myFile.read() ## Raw contents of file.
val = raw.count( selectSID ) ## Returns number of occurences
print( "Occurrences: ", val ) ## Or do something else...
lines = raw.split("\n") ## Create a list containing each new line
scores = [] ## A list that will contain all your scores
while selectSID in lines:
val = lines.index( selectSID ) ## Returns where it is in the list,
score = lines[ val+1 ] ## Gets the item at that position (index) Because the score is one line after the SID
scores.append( int(score) ) ## Adds the score to the list. --Suggest you look into how to safely capturing "int"s (try, except, etc) so the program doesn't crash if the score isn't a number (Advance)
lines.remove( selectSID ) ## automatically removes first occurrence of the SID (cause that's the one we just used)
avg = sum(scores) / len(scores) ## sum() function is self explanatory (takes a list or tuple [a sequence] and adds all values (must be all numbers), THEN len() is just length.
This will return an integer, or with your file, will print:
Occurrences: 4
Regardless if this answered your question, my tip for learning basics is understanding file types and what they can do.
In your case, you will mainly need to focus on strings (text) and integers (whole numbers). Using Pythons IDLE, declare a variable, and type the name and a dot, and use tab to scroll through each functions available.
Example:
>>> myString = "Hello World"
>>> myString.[TAB] #--> [Context Menu Here]
Once you pick one form the list, enter an opening parenthesis "(", and it will give a brief description of what it does.
Hope that helps, and sorry for the lengthy reply (I was trying to explain and give pointers (tips) since you said you were a noob)
I'm having trouble with a problem I'm trying to do. My goal is to import a file that contains football teams names, and then the number of wins and losses, and then if a team's average is greater than .500, than I have to write that teams name, and average to a new file. And then I have to write the teams under .500 to a seperate file. so far I have my code so that it reads eachline of the file, but I can't figure out how to analyize each line of code. I'm really just looking for any advice I could get at this point, and it would be greatly appreciated.
scores = open("fbscores.txt",'r')
eachline = scores.readline()
while eachline != "":
print(eachline)
eachline = scores.readline()
scores.close()
Given an example line of the file like this:
Cowboys 4 1
Then some code might look like this:
line = scores.readline().split()
teamName = line[0]
wins = int(line[1])
losses = int(line[2])
if wins > losses:
print(teamName + "'s record is over .500!")
goodTeams.write(line)
goodTeams.write() #make a new line
else:
print(teamName + "'s record is <= .500.")
badTeams.write(line)
badTeams.write()
To find the average of a team with x wins and y losses, do:
"%0.3f" % (x/(x+y))
Gives:
>>> "%0.3f" % (4/(4+1))
'0.800'
>>>
You are most likely going to end up using the split method, which breaks a string apart into a list element each time it encounters a certain character. Read more here http://www.pythonforbeginners.com/dictionary/python-split.
You could use a high level library to do so, like pandas.
It has many of the functionnalities you want!
import pandas as pd
df = pd.read_csv('file.csv')
print(df)
It's really helpful if you post some lines from your input.
According to your description, I assuming each line in your input has the following layout:
[NAME] X Y
Where, NAME is the team name, X is the number of wins, Y, the number of loses.
You can simply split the line by any delimiter (which is 'space' in this example case),
eachline = eachline.split()
If there was a comma separator, then you do eachline = eachline.split(',') and so on, you get the idea.
Then eachline will store a list of the following structure ['NAME', X, Y]
Then you can access X and Y to average them using: eachline[0] and eachline[1] respectively.
In my program I have a text file that I read from and write to. However, I would like to display the contents of the text file in an aligned and sorted manner. The contents currently read:
Emily, 6
Sarah, 4
Jess, 7
This is my code where the text file in read and printed:
elif userCommand == 'V':
print "High Scores:"
scoresFile = open("scores1.txt", 'r')
scores = scoresFile.read().split("\n")
for score in scores:
print score
scoresFile.close()
Would I have to convert this information into lists in order to be able to do this? If so, how do I go about doing this?
When writing to the file, I have added a '\n' character to the end, as each record should be printed on a new line.
Thank you
You could use csv module, and then could use sorted to sort.
Let's says, scores1.txt have following
Richard,100
Michael,200
Ricky,150
Chaung,100
Test
import csv
reader=csv.reader(open("scores1.txt"),dialect='excel')
items=sorted(reader)
for x in items:
print x[0],x[1]
...
Emily 6
Jess 7
Sarah 4
Looks like nobody's answered the "aligned" part of your request. Also, it's not clear whether you want the results sorted alphabetically by name, or rather by score. In the first case, alphabetical order (assuming Python 2.6):
with open("scores1.txt", 'r') as scoresFile:
names_scores = [[x.strip() for x in l.split(',', 1)] for l in scoresFile]
# compute column widths
name_width = max(len(name) for name, score in names_scores)
score_width = max(len(score) for name, score in names_scores)
# sort and print
names_scores.sort()
for name, score in names_scores:
print "%*s %*s" % (name_width, name, score_width, score)
If you want descending order by score, just change the names_scores.sort() line to two:
def getscore_int(name_score): return int(name_score[1])
names_scores.sort(key=getscore_int, reverse=True)
to sort stuff in Python, you can use sort()/sorted().
to print, you can use print with format specifiers, str.rjust/str.ljust, pprint etc
I'm making a stupid little game that saves your score in a highscores.txt file.
My problem is sorting the lines. Here's what I have so far.
Maybe an alphanumeric sorter for python would help? Thanks.
import os.path
import string
def main():
#Check if the file exists
file_exists = os.path.exists("highscores.txt")
score = 500
name = "Nicholas"
#If the file doesn't exist, create one with the high scores format.
if file_exists == False:
f = open("highscores.txt", "w")
f.write('Guppies High Scores\n1000..........Name\n750..........Name\n600..........Name\n450..........Name\n300..........Name')
new_score = str(score) + ".........." + name
f = open("highscores.txt", "r+")
words = f.readlines()
print words
main()
after words = f.readlines(), try something like:
headers = words.pop(0)
def myway(aline):
i = 0
while aline[i].isdigit():
i += 1
score = int(aline[:i])
return score
words.sort(key=myway, reverse=True)
words.insert(0, headers)
The key (;-) idea is to make a function that returns the "sorting key" from each item (here, a line). I'm trying to write it in the simplest possible way: see how many leading digits there are, then turn them all into an int, and return that.
I'd like to encourage you to store your high scores in a more robust format. In particular I suggest JSON.
import simplejson as json # Python 2.x
# import json # Python 3.x
d = {}
d["version"] = 1
d["highscores"] = [[100, "Steve"], [200, "Ken"], [400, "Denise"]]
s = json.dumps(d)
print s
# prints:
# {"version": 1, "highscores": [[100, "Steve"], [200, "Ken"], [400, "Denise"]]}
d2 = json.loads(s)
for score, name in sorted(d2["highscores"], reverse=True):
print "%5d\t%s" % (score, name)
# prints:
# 400 Denise
# 200 Ken
# 100 Steve
Using JSON will keep you from having to write your own parser to recover data from saved files such as high score tables. You can just tuck everything into a dictionary and trivially get it all back.
Note that I tucked in a version number, the version number of your high score save format. If you ever change the save format of your data, having a version number in there will be a very good thing.
I guess something went wrong when you pasted from Alex's answer, so here is your code with a sort in there
import os.path
def main():
#Check if the file exists
file_exists = os.path.exists("highscores.txt")
score = 500
name = "Nicholas"
#If the file doesn't exist, create one with the high scores format.
if file_exists == False:
f = open("highscores.txt", "w")
f.write('Guppies High Scores\n1000..........Name\n750..........Name\n600..........Name\n450..........Name\n300..........Name')
new_score = str(score) + ".........." + name +"\n"
f = open("highscores.txt", "r+")
words = f.readlines()
headers = words.pop(0)
def anotherway(aline):
score=""
for c in aline:
if c.isdigit():
score+=c
else:
break
return int(score)
words.append(new_score)
words.sort(key=anotherway, reverse=True)
words.insert(0, headers)
print "".join(words)
main()
What you want is probably what's generally known as a "Natural Sort". Searching for "natural sort python" gives many results, but there's some good discussion on ASPN.
Doing a simple string sort on your
new_score = str(score) + ".........." + name
items isn't going to work since, for example str(1000) < str(500). In other words, 1000 will come before 500 in an alphanumeric sort.
Alex's answer is good in that it demonstrates the use of a sort key function, but here is another solution which is a bit simpler and has the added advantage of visuallaly aligning the high score displays.
What you need to do is right align your numbers in a fixed field of the maximum size of the scores, thus (assuming 5 digits max and ver < 3.0):
new_score = "%5d........%s" % (score, name)
or for Python ver 3.x:
new_score = "{0:5d}........{1}".format(score, name)
For each new_score append it to the words list (you could use a better name here) and sort it reversed before printing. Or you could use the bisect.insort library function rather than doing a list.append.
Also, a more Pythonic form than
if file_exists == False:
is:
if not file_exists: