Python lists, .txt files - python

I'm a beginner and I have a few questions. I have a .txt file with names + grades, for example:
Emily Burgess 5 4 3 4
James Cook 4 9 5 4
Blergh Blargh 10 7 2 4
I need to write their names, last names and the average of their grades in a new .txt file. Then I need to calculate all of theirs average grade. How do I do that? I have started doing this, but I don't know what to do now:
def stuff():
things = []
file = open(r'stuff2.txt').read()
for line in file:
things.append(line.split(' '))
print(things)
for grade in things:
grades = int(grade[2], grade[3], grade[4], grade[5])
average = grades/4
print(average)
with open('newstuff.txt', 'w') as f:
f.write(things)

It's hard to tell, but it looks like you've got some problems in your for loop. For instance, you can't call the int constructor with 4 arguments:
TypeError: int() takes at most 2 arguments (4 given)
Did you mean:
grades = [int(g) for g in grades[1:]]
average = sum(grades) / len(grades[1:])
instead?

EDIT: since you're a beginning Python student, we'll leave object oriented programming out of it for now, but I'll keep the code below in case you feel like exploring a little!
students = list() # initialize an accumulator list
with open("stuff2.txt") as infile:
for line in infile:
data = line.strip().split(" ")
# strip removes ending and beginning whitespace e.g. the ending \n and etc
datadict = {}
datadict['first'] = data[0]
datadict['last'] = data[1]
datadict['grades'] = data[2:]
students.append(datadict)
# this can all be done in one line, but it's much clearer this way
# after this, all your students are in `students`, each entry in `students` is a
# dictionary with keys `first`, `last`, and `grades`.
# OUTPUT
with open("newstuff.txt","w") as outfile:
for student in students:
outputline = ""
outputline += student['first']
outputline += " "
outputline += student['last']
outputline += ": "
outputline += ", ".join(student['grades'])
# ", ".join(list) gives you a string with every element of list
# separated by a comma and a space, e.g. ','.join(["1","2","3"]) == "1, 2, 3"
outputline += "|| average: "
average = str(sum(map(int,student['grades']))/len(student['grades']))
# since student['grades'] is a list of strings, and we need to add them, you
# have to use map(int, student['grades']) to get their int representations.
# this is equivalent to [int(grade) for grade in student['grades']]
outputline += average
outputline += "\n"
outfile.write(outputline)
# again, this can be done in one line
# outfile.write("{0['first']} {0['last']}: {1}||{2}\n".format(
# student, ', '.join(student['grades']), sum(map(int,student['grades']))/len(student['grades']))
# but, again, this is long and unwieldy.
I'm always a proponent of using classes for these kinds of applications
class Student(object):
def __init__(self,name=None,grades=None,initarray=None):
"""Can be initialized as Student(name="Name",grades=[1,2,3]) or
Student(["First","Last",1,2,3])"""
if not (name and grades) or (initarray):
raise ValueError("You must supply both name and grades, or initarray")
if (name and grades):
self.name = name
self.grades = grades
else:
self.name = ' '.join(initarray[:2])
self.grades = initarray[2:]
#property
def average(self):
return sum(self.grades)/len(self.grades)
Then you can do something like:
students = list()
with open(r"stuff2.txt",'r') as f:
for line in file:
students.append(Student(line.strip().split(" ")))
# students is now a list of Student objects
And you can write them all out to a file with:
with open("students_grades.txt","w") as out_:
for student in students:
out_.write(r"{student.name}: {45:grades}||{student.average}\n".format(
student=student, grades = ', '.join(student.grades)))
Though you'll probably want to pickle your objects if you want to use them later.
import pickle
with open("testpickle.pkl","wb") as pkl:
pickle.dump(students,pkl)
Then use them again with
import pickle # if you haven't already, obviously
with open('testpickle.pkl','rb') as pkl:
students = pickle.load(pkl)

Your code can be made to work as thus:
with open('stuff2.txt') as f1, open('newstuff.txt', 'w') as f2:
for line in f:
raw_data = line.rstrip().split()
average = sum(int(i) for i in raw_data[2:])
new_data = ' '.join(raw_data[:2] + [str(average)])
f2.write(new_data)

Assuming the original txt file is stuff2.txt, and you want the output in newstuff.txt:
def process_line(line):
line = line.split()
first = line[0]
last = line[1]
grades = [int(x) for x in line[2:]]
average = sum(grades) / float(len(grades))
return first, last, average
with open('stuff2.txt') as f:
lines = f.readlines()
with open('newstuff.txt', 'w') as f:
for line in lines:
first, last, avg = process_line(line)
f.write(first + " " + last + " " + str(avg) + "\n")

Use pandas:
import pandas
df = pandas.read_csv("stuff.txt", sep=" ", header=None, names=["first","last","grade1","grade2","grade3","grade4"])
df["average"] = (df["grade1"]+df["grade2"]+df["grade3"]+df["grade4"])/4.0
df.to_csv("newstuff.txt",sep=" ", index=False) #will print a header row, which you can disable with header=None

Related

Syntax error calculating the average of student marks while reading from a text file

f = open('studMarks.txt', 'r')
marks = 0
# Sort out names, split the words then sort which order
for line in f:
words = line.split()
fname = words[0]
lname = words[1]
print(f"{lname},{fname}")
f.close()
f = open('studMarks.txt', 'r')
sum = 0
count = 0
for line in f:
count += 1
sum += float(line.split()[2])
n = []
average = sum/count
print(f"{average}")
When using the for loop it seems to display a value of 64.3, which I believe is for the total of the whole student list and average for all marks.
I need to produce the an output which displays the student names and average on the same line. I can do for the names but I cannot do it for the average as I keep getting errors. I don't know what to input in.
Below is the full solution. The with open line is a context manager and ensures that the file will get closed as soon as you exit the block. You should get used to using this style as it's the safe way to do I/O. The rest is just bog standard Python.
marks=dict()
with open('studMarks.txt', 'r') as f:
for line in f:
words = line.split()
fname = words[0]
lname = words[1]
score = int(words[2])
key = f'{fname} {lname}'
count_key = f'{fname} {lname}_count'
latest_score = score + (marks.get(key)[0] if marks.get(key) else 0)
latest_count = 1 + (marks.get(key)[1] if marks.get(key) else 0)
marks[key] = (latest_score, latest_count )
for name, value in marks.items():
print(f'{name} : {value[0]/value[1]}')
This is an interesting problem.
From what I understand you have a text file that looks like this:
Johnny Ly 90 100 Adam Best 80 30 Tim Smith 10 20 in a file called studentMarks2.txt
and want output like this:
Johnny_Ly 95.0 Adam_Best 55.0 Tim_Smith 15.0
if that is true then it can be done using code like this without pandas or csv
though those would make this a lot easier.
fileContents = []
with open('studMarks2.txt','r') as f:
fileContents = f.read().split()
students = dict()
names = []
for content in fileContents:
if content.isnumeric():
studentKey = '_'.join(names)
currentScore = students.get(studentKey,[])
newScore = currentScore + [float(content)]
students.update({studentKey:newScore})
else:
if len(names) == 2:
names.clear()
names.append(content)
else:
names.append(content)
for student,scores in students.items():
avg = sum(scores)/len(scores)
print(student,avg,end=' ')
Broken down
This part reads the contents and splits on white space
fileContents = []
with open('studMarks2.txt','r') as f:
fileContents = f.read().split()
this part then iterates through the contents
storing the names as keys in a dictionary and putting the scores in a list
students = dict()
names = []
for content in fileContents:
if content.isnumeric():
studentKey = '_'.join(names)
currentScore = students.get(studentKey,[])
newScore = currentScore + [float(content)]
students.update({studentKey:newScore})
else:
if len(names) == 2:
names.clear()
names.append(content)
else:
names.append(content)
Lastly it iterates over the dictionary and output the avg on one line
for student,scores in students.items():
avg = sum(scores)/len(scores)
print(student,avg,end=' ')

Overwrite Text File

Here is the code:
import random
import sys
name = input("What is your name: ")
tri = 0
def rep():
score = random.randint(1,10)
total = 0
print ("score is",score,)
total = +score
file = open("1.txt","a")
file.write(str(name + " = "))
file.write(str(total))
file.write("\n")
file.close()
tri = +1
rep()
while tri > 2:
sys.exit
else:
print(rep())
So what this code does, is generates a random score for the user 2 times and then that score is saved into a .txt file under the users name which is inputted as 'name'. What I want to do is, if the same person did the game again and another 2 scores where generated it would overwrite the previous 2 results with the new two.
Here is what the text file would look like:
Tom = 2
Tom = 7
Chrissy = 3
Chirssy = 10
John = 4
John = 9
If the the user 'Tom' did the game again this time getting 5 and 3, the text file should look like the following:
Chrissy = 3
Chirssy = 10
John = 4
John = 9
Tom = 5
Tom = 3
In this current situation it just keeps adding on the scores like this:
Tom = 2
Tom = 7
Chrissy = 3
Chirssy = 10
John = 4
John = 9
Tom = 5
Tom = 3
A first comment, it's a really good idea to use the context managers for file operations, this will ensure that file resources are properly handled. For this reason I use it in the code here, I suggest you do the same.
If you are going to approach this in such a way that you want to use plain text files you have to remove the lines that contained the name then update. A function such as the following is likely going to help here:
def remove_old_names(name, filename):
"""Remove a line containing a specific name"""
with open(filename, 'r') as old_file:
lines = old_file.readlines()
with open(filename, 'w') as new_file:
for line in lines:
if name not in line:
new_file.write(line)
Then later when you can clear out the old names then append to the text file:
remove_old_names(name, filename)
with open("1.txt","a") as results:
results.write(str(name + " = "))
results.write(str(total))
results.write("\n")
Note the use of "a" here to open the file in append mode. If you open with "w" you can end up truncating the file.
Now if I was to approach this in a more structured way I would create a dictionary that stores the data:
results = dict()
results["bob"] = 2
And so forth for the other user names. I would then serialize this dictionary to a file using pickle or the JSON library.
For example with JSON library you get something like this:
import json
test = {
"bob": 1,
"joe": 2,
"jane": 3,
}
print(json.dumps(test, sort_keys=True, indent=4))
output:
{
"bob": 1,
"jane": 3,
"joe": 2
}
This is an easy file format to just do by hand. The one issue is that text files aren't really stored as lines so you can't just modify just one line. Once you've changed a line, everything after that must be rewritten to the file. If the file isn't too big, you just read everything into a list and work off of that.
import random
def rep(name, score1, score2):
try:
# read entire file into list
with open('1.txt') as fp:
lines = fp.readlines()
except FileNotFoundError:
lines = []
name_lower = name.lower()
for index, line in enumerate(lines):
if line.split('=')[0].strip().lower() == name_lower:
# found our match... next line had better be the same player
if lines[index+1].split('=')[0].strip().lower() != name_lower:
raise RuntimeError("File corrupted")
# replace user in the list
lines[index] = "{} = {}\n".format(name, score1)
lines[index + 1] = "{} = {}\n".format(name, score2)
# no need to process further
break
else:
# add new user
lines.append("{} = {}\n".format(name, score1))
lines.append("{} = {}\n".format(name, score2))
with open('1.txt', 'w') as fp:
fp.writelines(lines)
name = input("what is your name: ")
rep(name, random.choice(range(100)), random.choice(range(100)))
print(open('1.txt').read()) # debug

Python: Using an input to determine what line to save on

I'm currently working on a task where I must store scores in a text file. This is my code thus far:
def FileHandle():
score = str(Name) + ": " + str(correct)
File = open('Test.txt', 'a')
File.write(score)
File.close()
Name = input("Name: ")
correct = input("Number: ")
FileHandle()
My question is, how would I check already existed names in the text file and only add their score, rather than name and score, to the line it existed on?
This is what the file looks like:
Jonathon: 1
Micky: 5
How it would look after adding a score:
Jonathon: 1, 4
Mickey: 5
# The score added here is Jonathon's 4
Attempt:
# Accept scores
name = input("Name: ")
correct = input("Number: ")
if name in grade_book.keys and "," in grade_book.keys <= 2():
grade_book[name] += ',' + correct
else:
grade_book[name] = correct
If you are entering many scores at a time, I suggest reading the file into memory and working with things there. Doing an open/close on the file for every score update is very inefficient.
# Read the file into a dictionary
grade_book = {}
File = open('Test.txt', 'r')
for line in File:
name, scores = line.split(':')
grade_book[name] = scores.strip()
File.close()
print grade_book
# Accept scores
name = raw_input("Name: ")
while name != "":
correct = raw_input("Number: ")
if name in grade_book.keys():
grade_book[name] += ',' + correct
else:
grade_book[name] = correct
name = raw_input("Name: ")
# Write dictionary back to the file
File = open('Test.txt', 'w')
for name, scores in grade_book.items():
out_line = name + ':' + scores + "\n"
File.write(out_line)
File.close()
Unfortunately you would have to go over the file, find the correct line, edit it and write in a temp file and then move that file to original. Best first use a data structure like dict etc. to update scores and finally when done write or persist them.
def filehandle(name,correct):
temp = open('temp', 'wb')
with open('Test.txt', 'r') as f:
for line in f:
if line.startswith(name):
line = line.strip() + correct +'\n'
temp.write(line)
temp.close()
shutils.move('temp', 'data.txt')
You need to pass in the parameters while calling the functions.
Name = input("Name: ")
correct = input("Number: ")
filehandle(name, correct)

How to find the average of a certain key from a file in comma-separated format in Python?

So how could I find the average from the file. I am new at python.
My file looks like this:
dog,3
dog,11
cat,3
cat,4
cat,15
How could I make the code find the average of "cat"?
This is not full code:
def quiztime():
score = 0
k_file=open('k_form.txt','a')
l_file=open('l_form.txt','a')
e_file=open('e_form.txt','a')
print('PLEASE TYPE NUMBERS ONLY!!!')
print('Welcome to this quiz :)\n')
name=input('What is your name \n')
print('Welcome ',name,'\n')
form=input("What form are you K - E - L")
for i in range(10): #Asking 10 Questions.
correct = Question()
if correct:
score += 1
print('Correct!\n')
else:
print('Incorrect!\n')
print ('Your score was {}/10'.format(score))
if form=='K':
k_file.writelines(str(name)+','+str(score) +'\n')
k_file.close()
if form=='E':
e_file.writelines(str(name)+','+str(score) +'\n')
e_file.close()
if form=='L':
l_file.writelines(str(name)+','+str(score) +'\n')
l_file.close()
master = Tk()
w = Label(master, text="", fg="green", bg="black", font=("Helvetica", 12)).pack()
print('To view the data in files please type the password\n')
passw=input('Type The Password\n')
kf_file=open('k_form.txt','r')
lf_file=open('l_form.txt','r')
ef_file=open('e_form.txt','r')
if passw=='teacher':
print("Type A Form e.g E, K ,L")
tform=input('=')
if tform=='E':
print('Type In *alphabetical* To Get Alphabetical order.')
print('Type In *highest score* To Get Highest score.')
print('Type In *average score* To Get average score.')
order=input('Type= ')
if order=='alphabetical':
with open('e_form.txt', 'r') as r:
for line in sorted(r):
print(line, end='')
if order=='highest score':
scores = []
with open('e_form.txt','r') as f:
for line in f:
name, score = line.split(',', 1)
score = int(score)
scores.append((name, score))
scores.sort(key=lambda s: s[1])
scores.reverse()
for name, score in scores:
print(name, score)
if order=='average score':
data = []
with open('file.txt', 'r') as a:
for line in a:
field = line.split(',', 1)
field = int(field)
rowdata = map(float, field)
data.extend(rowdata)
print(sum(data)/len(data))
First, move the code that calculates scores to before the if order=='alphabetical': line, so that it's calculated by default and available in the rest of the function.
Now you can do the following:
cat_list = [i[1] for i in scores if i[0]=='cat']
print(sum(cat_list)/len(cat_list))
This parses a scores of [('cat', 3), ('dog', 4), ('cat', 2)] to print an average of 2.5.
Make a dictionary of lists.
def get_dictionary():
handle = open("file_with_cats_n_dogs", "rb")
d = {}
for line in handle:
key, number = line.strip().split(",")
if key in d:
d[key] += [float(number)]
else:
d[key] = [float(number)]
return d
Then you can do:
def get_avg(numbers_dict, key):
avg = sum(numbers_dict[key])/len(numbers_dict[key])
return avg
You can define a function as shown below and use it to find out average for items like dog,cat etc...
import csv
ef_file = open('e_form.txt')
def avg(fileHnadler,avgFor):
csv_rows = csv.reader(fileHnadler)
count=total=0
for row in csv_rows:
if row[0]==avgFor:
total += float(row[1])
count += 1
if count!=0:
return total/count
else:
return 'No Data in File'
Then you can call function as shown below
cat_avg = avg(ef_file,'cat')
In your case,as you are anyway opening file in your code. So you just have call this function with proper arguments/parameters to get required average value....
Using collections.defaultdict a list containing the values for each key can be constructed. csv.reader simplifies processing of the data from the file, splitting it into separate fields and stripping new lines characters. Then the average for each key can be calculated by iterating over the items in the dictionary, summing each list and dividing by the list length. Here is the code:
import csv
from collections import defaultdict
with open('file.txt') as f:
pets = defaultdict(list)
for row in csv.reader(f):
pets[row[0]].append(float(row[1]))
for pet, values in pets.items():
print('{}: {:.2f}'.format(pet, sum(values) / len(values)))
Output
dog: 7.00
cat: 7.33

Copy 'N' lines from one file to another in python?

Essentially what I am attempting to do is read 'n' number of lines from a file and then write them to a separate file. This program essentially should take a file that has 100 lines and separate that file into 50 separate files.
def main():
from itertools import islice
userfile = raw_input("Please enter the file you wish to open\n(must be in this directory): ")
file1 = open(userfile, "r+")
#print "Name: ", file1.name
#print "Closed or not", file1.closed
#print "Opening mode: ", file1.mode
#print "Softspace flag: ", file1.softspace
jcardtop = file1.read(221);
#print jcardtop
n = 2
count = 0
while True:
next_n_lines = list(islice(file1,n))
print next_n_lines
count = count + 1
fileout = open(str(count)+ ".txt", "w+")
fileout.write(str(jcardtop))
fileout.write(str(next_n_lines))
fileout.close()
break
if not next_n_lines:
break
I do have the file printing as well to show what is in the variable next_n_lines.
*['\n', "randomtext' more junk here\n"]
I would like it instead to look like
randomtext' more junk here
Is this a limitatoin of the islice function? Or am I missing a portion of the syntax?
Thanks for your time!
Where you call str() or print, you want to ''.join(next_n_lines) instead:
print ''.join(next_n_lines)
and
fileout.write(''.join(next_n_lines))
You can store the flattened string in a variable if you don't want to call join twice.
Did you mean something like this?
f = open(userfile,"r")
start = 4
n_lines = 100
for line in f.readlines()[start:(start + n_lines)]:
print line
#do stuff with line
or maybe this rough, yet effective code:
f = open(userfile,"r")
start = 4
end = start + 100
count = start
while count != end:
for line in f.readlines()[count:(count + 2)]:
fileout = open(str(count)+ ".txt", "w+")
fileout.write(str(line))
fileout.close()
count = count + 2

Categories