2

For my beginners course python I got the following assignment:

In the input file, grades are listed for the geography tests of group 2b. There have been three tests of which the grades will be included in the half-yearly report that is given to the students before the Christmas break. On each line of the input you can find the name of the student, followed by one or more under scores (’_’). These are succeeded by the grades for the tests, for example:

Anne Adema____________6.5 5.5 4.5
Bea de Bruin__________6.7 7.2 7.7
Chris Cohen___________6.8 7.8 7.3
Dirk Dirksen__________1.0 5.0 7.7

The lowest grade possible is a 1, the highest a 10. If somebody missed a test, the grade in the list is a 1. Your assignment is to make the report for the geography course of group 2b, which should look like this:

Report for group 2b

Anne Adema has an average grade of 5.5
Bea de Bruin has an average grade of 7.2
Chris Cohen has an average grade of 7.3
Dirk Dirksen has an average grade of 4.6

End of report

This is my python code so far:

NUMBER_OF_GRADES = 3

file =open('grades1.in.txt').read().split('\n')
for scores in file:
    name_numbers = (scores.split('_'))

def averages ():
    for numbers in file:
        sum=0
        numbers = split("\n")
        for num in numbers:
            sum = sum + int(num)
        averages = sum/NUMBER_OF_GRADES
    print ('% has an average grade of %.1') %(name, averages)

Where does it go wrong? What am I missing? Am I not splitting the right way?

martineau
  • 119,623
  • 25
  • 170
  • 301
Leslie
  • 31
  • 1
  • 5
  • 3
    What's wrong? Do you get an error? – Joe Patten Jan 23 '19 at 22:36
  • 1
    In all honesty, you should be telling us what's wrong. What's the error you are getting? – hqkhan Jan 23 '19 at 22:36
  • You don't appear to have a `name` variable, and you'll want to also loop your `print` statement by the sound of the question. – Obsidian Age Jan 23 '19 at 22:37
  • You might also be ending up with multiple `''` in `name_numbers` since there are multiple `_` in your text file. – hqkhan Jan 23 '19 at 22:37
  • In the for-loop, you are assigning a new value to `numbers` which is your iteration variable. The line `numbers = split("\n")` will not work. I guess you want to call `split` on a string – GoTN Jan 23 '19 at 22:39
  • I don't really get an error. Just nothing happens. No prints, just: Process finished with exit code 0. – Leslie Jan 23 '19 at 22:43
  • You do not even call your function `averages`. Simply add `averages()` as the last line in your file. – GoTN Jan 23 '19 at 22:45
  • You also have a random `split("\n")` call without any string calling it. – hqkhan Jan 23 '19 at 22:53
  • If any of the answers were helpful, make sure to upvote them! And if one stands out, go ahead and mark it as "accepted" (if you have enough reputation) so that people with the same problem in the future can easily find it. – Engineero Jan 25 '19 at 14:07

4 Answers4

0

First, you are not calling your function averages at all. You should add that as the last line in your code, but you do not really need the function definition at all.

  • Use the with-statement to open and automatically close the file.
  • Next, you can use the re package to split the lines at the underscores.
  • You have to split the list of grades (assuming that they are separated by spaces)
  • You can use the built-in functions sum and len to calculate the sum and the number of grades.

In the end, it could look something like this

import re

with open('grades1.in.txt') as grades_file:
    for line in grades_file:
        name, grades = re.split("_+", line)
        grades = [float(k) for k in grades.split()]
        avg = sum(grades)/len(grades)
        print("{} has an average grade of {:.2f}.".format(name, avg))
GoTN
  • 135
  • 7
  • 1
    This is good. One thing I wanted to point out is that `1.0` denotes a missing grade which I'm not sure if it should be counted or ignored (this would affect average). This is something for OP to address or confirm but I wanted to just leave it here. – hqkhan Jan 23 '19 at 22:57
  • You are right. In the code above, the average is calculated without considering `1.0` as a special grade. – GoTN Jan 23 '19 at 22:59
  • Thank you all for the notes and comments. I think I can make it work now. And you'll probably see me again ;) – Leslie Jan 23 '19 at 23:04
  • Yes 1.0 should be counted btw! But thanks for noticing. – Leslie Jan 23 '19 at 23:05
0

GoTN already answered your question.

To make it clear and improve a little bit you can try:

def averages(line, number_of_grades):
    line_parsed = line.split('_')

    numbers = [float(x) for line_parsed[-1].split(' ')]

    name = line_parsed[0]

    # you can use number_of_grades or len(numbers)
    # Although, if the student only has 2 grades in the file,
    # but it was supposed to be two the average will be wrong.
    avg = sum(numbers)/number_of_grades

    print("{} has an average grade of {:.2f}.".format(name, avg))
    # or print(f'{name} has an average grade of {avg:.2f}')

NUMBER_OF_GRADES = 3

files =open('grades1.in.txt').read().splitlines()

for line in files:
    if len(line) > 0:
        averages(line, NUMBER_OF_GRADES)
kaihami
  • 815
  • 7
  • 18
0

There's a few things wrong with your code. This maybe should be migrated to Code Review, but I'll write my answer here for now.

I will keep this as close to your version as I can so that you see how to get from where you are to a working example without needing a ton of stuff you might not have learned yet. First let's look at yours one part at a time.

file =open('grades1.in.txt').read().split('\n')

Your file is going to be a list of strings, where each string is a line in your input file. Note that if you have empty lines in your input, some of the lines in this list will be empty strings. This is important later.

for scores in file:
    name_numbers = (scores.split('_'))

Works fine to split the name part of the line from the scores part of the line, so we'll keep it, but what are you doing with it here? Right now you are overwriting name_numbers with each new line in the file and never doing anything with it, so we are going to move this into your function and make better use of it.

def averages():

No arguments? We'll work on that.

    for numbers in file:

Keep in mind your file is a list, where each entry in the list is one line of your input file, so this numbers in file doesn't really make sense. I think this is where you first go wrong. You want to look at the numbers in each line after you split it with scores.split('_'), and for that we need to index the result of the split. When you split your first line, you get something like:

split_line = ['Anne Adema', '', '', '', '', '', '', '', '', '', '', '', '6.5 5.5 4.5']

The first element (split_line[0]) is the name, and the last element (split_line[-1]) are the numbers, but you still have to split those out too! To get a list of numbers, you actually have to split it and then interpret each string as a number. You can do this pretty easily with a list comprehension (best way to loop in Python) like this:

numbers = [float(n) for n in split_line[-1].split(' ')]

This reads something like: first split the last element of the line at spaces to get ['6.5', '5.5', '4.5'] (note they're all strings), and then convert each value in that list into a floating-point number, and finally save this list of floats as numbers. OK, moving on:

        sum=0
        numbers = split("\n")  # already talked about this
        for num in numbers:
            sum = sum + int(num)
        averages = sum/NUMBER_OF_GRADES

sum is a keyword in Python, and we never want to assign something to a keyword, so something's wrong here. You can actually just call sum(my_list) on any list (actually any iterable) my_list to get the sum of all of the values in the list. To take the average, you just want to divide this sum by the length of the list, which you can get with len(my_list).

    print ('% has an average grade of %.1') %(name, averages)

There are some cool newer ways to print formatted text, one of which I will show in the following, but if you are supposed to use this way then I say stick with it. That said, I couldn't get this line to work for me, so I went with something I know better.


Rewriting it into something that works:

def averages(line):
    if line is '':
        return  # skips blank lines!
    name_numbers = line.split('_')  # now we split our line
    name = name_numbers[0]
    numbers = [float(n) for n in name_numbers[-1].split(' ')]
    average = sum(numbers) / len(numbers)
    print('{} has an average grade of {:.2}'.format(name, average))

And running it on your data:

file =open('grades1.in.txt').read().split('\n')
for line in file:
    averages(line)  # call the function on each line
# Anne Adema has an average grade of 5.5
# Bea de Bruin has an average grade of 7.2
# Chris Cohen has an average grade of 7.3
# Dirk Dirksen has an average grade of 4.6

With the result shown in the comments below the function call. One more note, you never close your file, and in fact you never save off the file handle to close it. You can get rid of any headaches around this by using the context manager syntax in Python:

with open('grades1.in.txt', 'r') as a_file:
    for line in a_file:
        averages(line)

This automatically handles closing the file, and will even make sure to do so if you run into an error in the middle of the block of code that executes within the context manager. You can loop through a_file because it basically acts as an iterable that returns the next line in the file each time it is accessed.

Engineero
  • 12,340
  • 5
  • 53
  • 75
  • 1
    You are my hero! Thanks for taking the time to explain everything so clearly and at my starters level. Also thanks for keeping the code 'starters'. – Leslie Jan 23 '19 at 23:22
0

Splitting the lines up is somewhat involved. The code below does it by first replacing all the "_" characters with spaces, then splits the result of that up. Since there can be a variable number of parts making up the full name, the results of this splitting are "sliced" using negative indexing which counts backwards for the end of the sequence of values.

That works by taking advantage of the fact that we know the last three items must be test scores, therefore everything before them must be parts comprising the name. This is the line doing that:

names, test_scores = line[:-NUMBER_OF_GRADES], line[-NUMBER_OF_GRADES:]

Here's the full code:

NUMBER_OF_GRADES = 3

with open('grades1.in.txt') as file:
    for line in file:
        line = line.replace('_', ' ').split()
        names, test_scores = line[:-NUMBER_OF_GRADES], line[-NUMBER_OF_GRADES:]
        name = ' '.join(names)  # Join names together.
        test_scores = [float(score) for score in test_scores]  # Make numeric.
        average_score = sum(test_scores) / len(test_scores)
        print('%s has an average grade of %.1f' % (name, average_score))

Output:

Anne Adema has an average grade of 5.5
Bea de Bruin has an average grade of 7.2
Chris Cohen has an average grade of 7.3
Dirk Dirksen has an average grade of 4.6
martineau
  • 119,623
  • 25
  • 170
  • 301
  • @eda nicole: Please stop trying to change the code in my answer so you can cheat on your homework assignment. – martineau Jan 03 '21 at 05:32