3

I have been able to import 3 scores (or numbers) for each person in my CSV file and have it work out the average of the three scores for each person but what I need to do next is sort the list of averages shown in the output from high to low. I have searched everywhere and have received 'Float' errors with everything I try.

from collections import defaultdict, deque
with open("highscores.csv", "r+")as file:
    file.seek(0)
    scores = file.readlines()
user_scores = defaultdict(lambda:deque(maxlen=3))
for line in scores:
    name, score = line.rstrip('\n').split(',')
    score = int(score)
    user_scores[name].append(score)

for name in user_scores:
    avg = (user_scores[name])
    x = sorted(avg)
    avg2 = sum(x) / float(len(x))
    print(name, avg2)

Output:

Odis 22.0
Vance 20.0
John 26.0
Marinda 25.0
Shenita 18.0
Marquitta 24.0
Concepcion 17.0
Robby 23.0
Valda 19.0
Ernesto 21.0
Jack 5.0

My CSV file looks like this :

Jack    4
Jack    5
Jack    6
Concepcion  7
Shenita 8
Valda   9
Vance   10
Ernesto 11
Odis    12
Robby   13
Marquitta   14
Marinda 15
John    16
Concepcion  17
Shenita 18
Valda   19
Vance   20
Ernesto 21
Odis    22
Robby   23
Marquitta   24
Marinda 25
John    26
Concepcion  27
Shenita 28
Valda   29
Vance   30
Ernesto 31
Odis    32
Robby   33
Marquitta   34
Marinda 35
John    36
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • 2
    You're almost there. Create a new dictionary with each name and their average, and sort it: http://stackoverflow.com/q/613183/584846 – Brent Washburne Dec 04 '15 at 00:01

3 Answers3

3

The line where you sort avg is unnecessary - after all these are the scores of a single person and it doesn't matter in which order you sum them. What you want to do is to sort all the entries once all the averages are calculated, so you need to collect those. If you want them to be sorted according to their averages, use a tuple with the average as the first field, that way sorted will do exactly what you want:

# The list that will hold the pairs of
# average score and name
avgs = []

# Your initial loop
for name in user_scores:
    x = user_scores[name]

    # Just collect the pair
    avgs.append((sum(x) / float(len(x)), name)

# Now sort it
avgs = sorted(avgs)

# Print it
for avg, name in avgs:
    print (name, avg)

However, a much more Pythonesque way of doing it is with list comprehensions:

# A function for the average
def average(lst):
    return sum(lst) / float(len(lst))

# The averages as list comprehension    
avgs = sorted((average(scores), name)
              for name, scores in user_scores.items())

# Print it
for avg, name in avgs:
    print (name, avg)

This assumes that you are using Python 3, for Python 2 use iteritems() or viewitems()

Jens Grabarske
  • 735
  • 6
  • 7
  • Thank You The First One Worked Perfectly – user5637300 Dec 04 '15 at 07:59
  • You can use a generator expression instead of a list comprehension. In other words, `sorted((average(scores), name) for name, scores in user_scores.items())` without the square brackets. – 200_success Dec 04 '15 at 08:13
  • You're right, of course, I just used the list comprehension for the sake of continuity towards the example with collected lists. But as the OP used the first version anyway, I will edit the source to use the generator instead. – Jens Grabarske Dec 04 '15 at 11:19
1

Assuming Python 3…

In your second for loop, you can't possibly get them printed in the desired order if you're printing them while you're still calculating the averages. You need to split it up into two phases.

Calculate the average scores:

avg_user_scores = {
    user: sum(map(float, scores))/len(scores)
    for user, scores in user_scores.items()
}

Then print them, sorted in descending order:

for name, score in sorted(avg_user_scores.items(), key=itemgetter(1), reverse=True):    
    print(name, score)

operator.itemgetter(1) is a way to fetch the second element of a tuple (i.e. lambda t: t[1]) — which is the average score.


The entire program:

from collections import defaultdict, deque
from operator import itemgetter

user_scores = defaultdict(lambda: deque(maxlen=3))
with open('highscores.csv') as file:
    for line in file:
        name, score = line.split(',')
        user_scores[name].append(float(score))
avg_user_scores = {
    user: sum(scores) / len(scores)
    for user, scores in user_scores.items()
}
for name, score in sorted(avg_user_scores.items(), key=itemgetter(1), reverse=True):    
    print(name, score)
200_success
  • 7,286
  • 1
  • 43
  • 74
0

Average the three scores for each person. Place the values into a dictionary (the key will be the person). Call the sorted() method.

sikez
  • 85
  • 10