0

I have a list consisting of 4 attributes: subject, test, score, and result. I need to calculate the total score for each subject, by adding up the test scores for each subject. I currently have that. But I need to calculate the total test score of passed tests, and then divide that number by the total test score of all tests.

This is the first part of the code that works correctly:

from collections import defaultdict

d = defaultdict(float)
dc = defaultdict(float) 

subject = ['Math', 'Math', 'Math', 'Math', 'Biology', 'Biology', 'Chemistry']
test = ['Test 1','Test 2','Test 3','Test 4','Test 1','Test 2','Test 1']
score = ['1.0', '0.0', '4.0', '0.0', '4.0', '6.0', '2.0']
result = ['fail', 'fail', 'pass', 'fail', 'fail', 'pass', 'pass']

points = [float(x) for x in score]

mylist = list(zip(subject, test, points, result))

for subject, test, points, completion, in mylist:
    d[subject] += points
    dc[(subject, test)] += points
print(d)

Expected result & actual result is:

{'Math': 5.0, 'Biology': 10.0, 'Chemistry': 2.0}

Now the issue I'm having is I need to add up the total number of points for each subject on only the tests that have been passed. And then divide that number from the total number of all tests (passed and failed) in a subject. So something like, 'if result == "passed" then do 'rest of calculations'.

This is the remaining code:

dc = {f"{subject} {test}" : round(points / d[subject], 2)
       if d[subject]!=0 else 'division by zero'  
       for (subject, test), points in dc.items()}
      
print(dc)

Expected result:

Math: 4/5, Biology: 6/10, Chemistry: 2/2

Actual result:

'Math Test 1': 0.2, 'Math Test 2': 0.0, 'Math Test 3': 0.8, 'Math Test 4': 0.0, 'Biology Test 1': 0.4, 'Biology Test 2': 0.6, 'Chemistry Test 1': 1.0
  • _"divide that number from the total number of all tests (passed and failed) in a subject"_: Your expected result doesn't make sense. In your example, there are only 4 Math tests, 2 Biology tests, and one Chemistry test. Where do you get 5, 10, and 2 from? Did you mean divide that number from the total _score_ for all tests? – Pranav Hosangadi Nov 22 '22 at 23:17

3 Answers3

1

You want to add to the total in dc only if the test is passed, so why not do that in the first place?

for sub, scr, completion, in zip(subject, score, result):
    points = float(scr)
    d[sub] += points
    if completion == "pass":
        dc[sub] += points

Now, you have

d = defaultdict(float, {'Math': 5.0, 'Biology': 10.0, 'Chemistry': 2.0}) 

dc = defaultdict(float, {'Math': 4.0, 'Biology': 6.0, 'Chemistry': 2.0})

To get your required output from here, just loop over the keys of d, format the corresponding values from d and dc into a string, and print:

for sub, total_score in d.items():
    print(f"{sub}: {dc[sub]} / {total_score}")

which gives:

Math: 4.0 / 5.0
Biology: 6.0 / 10.0
Chemistry: 2.0 / 2.0

To remove the decimal point, see Formatting floats without trailing zeros

Note: I renamed your loop variables to avoid redefining the original lists. Also, there's no need for pre-calculating points or list()ing out the zip(...) into mylist. A for loop can iterate over a zip just fine, and you can convert the score to a float inside the loop.

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
  • I still want the total test scores, of failed and passed tests per subject. But I want the total of passed tests divided by the total score per subject. – Marcus Availo Nov 22 '22 at 23:22
  • @MarcusAvailo _"total of passed tests"_ is `dc`, and _"total score per subject"_ is `d`. Looping over both dictionaries to create the string you want is simple enough, as I show in my edits. – Pranav Hosangadi Nov 22 '22 at 23:27
  • thank you so much for your help. Can I ask you a follow up question, some test scores have a 'n/a' string. How can I get the formula to ignore those strings, when trying to convert them to floats? – Marcus Availo Nov 23 '22 at 02:53
  • 1
    @MarcusAvailo I'd use a `try-except` block. I suggest you look up a tutorial on how to use these, but the basics are that if an error is thrown by the code inside the `try` block, the program jumps to the `except` block. This would handle _all_ inputs that can't be converted to a float, instead of you hard-coding an `if scr == "n/a"` around the `points = float(scr)` statement. – Pranav Hosangadi Nov 23 '22 at 04:00
1

based on your code, I have made some changes. I am not a big fan of comprehensions, I am an old guy who prefers simple code:

from collections import defaultdict

d = defaultdict(float)
dc = defaultdict(float) 


subject = ['Math', 'Math', 'Math', 'Math', 'Biology', 'Biology', 'Chemistry']
test = ['Test 1','Test 2','Test 3','Test 4','Test 1','Test 2','Test 1']
score = ['1.0', '0.0', '4.0', '0.0', '4.0', '6.0', '2.0']
result = ['fail', 'fail', 'pass', 'fail', 'fail', 'pass', 'pass']

points = [float(x) for x in score]

mylist = list(zip(subject, test, points, result))

for subject, test, points, completion, in mylist:
    d[subject] += points
    dc[(subject, test)] += points
print(d)

# Here are my changes
# result dict will hold your final data
result = defaultdict(float) 

for subject, test, points, completion, in mylist:
    if completion == 'pass' and int(d[subject]) > 0:
        print( float(points/d[subject]) )
        result[subject] = float(points/d[subject])
print(result)

and the result is:

{'Math': 0.8, 'Biology': 0.6, 'Chemistry': 1.0}
Mike
  • 229
  • 1
  • 4
0
dapssed=defaultdic(float)
for subject, test, points, completion, in mylist:
    d[subject] += points # this is the total passed and not passed
    dc[(subject, test)] += points
    if completition == 'pass':
        dpassed[subject]=+=points   # Only pass point
wrbp
  • 870
  • 1
  • 3
  • 9