pythoncsvdictionarykeykeyerror

How do I access specific keys and values from CSV files?


I have a CSV file keeping track of people's score in a game. I read it with csv.DictReader(). How do I create a dictionary with the keys being the names and the value being the scores?

CSV file example:

name,Game_1, Game_2, Game_3
James,3,7,4
Charles,2,3,8
Bob,6,2,4

Note: the game names can change, and amount of games too.

Code Example:

# Dictionary to load scores into
score_sheet = {}

# Read CSV file into memory
with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file)

    list_of_games = reader.fieldnames[1:]

    # Load each row seperately
    for row in reader:
        for game in list_of_games:
            score_sheet[row[0]] = {row[game]:row[game[1]} 
        
# Experiment 1
    # Load each row separately
    for row in reader:
        score_sheet[row[0]] = row[1]
# Experiment 2
for row, value in reader:
            print(row, value)

Desired output:

score_sheet = {
    'James':{'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'}
    'Charles':{'Game_1':'2'}, {'Game_2':'3'}, {'Game_3':'8'}
    'Bob':{'Game_1':'6'}, {'Game_2':'2'}, {'Game_3':'4'}
    # Optionally:
    # name:[{game:score}, {game:score}, {game:score}]
}

Errors:

score_sheet[row[0]] = {row[game]:row[game[1]} 
#           ^^^^^^ KeyError: 0

score_sheet[row[0]] = row[1]
#                     ^^^^^^ KeyError: 1
for row, value in reader:
            print(row, value)
#
# for row, value in reader:
#     ^^^^^^^^^^
# ValueError: too many values to unpack (expected 2)

How do I access specific keys or values for dictionaries from a CSV file? Please help me understand what I am missing or misunderstanding.


Solution

  • You can't create

    'James': {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'}
    

    (if you don't mean tuple of dicts)

    It has to be:

    list of dicts

    'James': [ {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} ]
    

    tuple of dicts

    'James': ( {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} )
    

    or one dict

    'James': {'Game_1':'3', 'Game_2':'7', 'Game_3':'4'}
    

    You use csv.DictReader() so every row is already dict and you should use row['name'] instead of row[0]

    You should first create empty list score_sheet[row['name']] = [] and later use for-loop to append() dictionares with {game: row[game]}

    for row in reader:
    
        score_sheet['name'] = []
    
        for game in list_of_games:
            score_sheet[row[0]].append( {game:row[game]} )
    

    And it should give

    score_sheet = {
        'James': [ {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} ],
        # ...
    }
    

    You may also use list/tuple comprehension in place of normal for-loop.

        # list comprehension
        score_sheet[row['name']] = [{game:row[game]} for game in list_of_games]
    
        # tuple comprehension
        score_sheet[row['name']] = tuple({game:row[game]} for game in list_of_games)
    

    If you want it as one dict

    score_sheet = {
        'James': {'Game_1':'3', 'Game_2':'7', 'Game_3':'4'},
        # ...
    }
    

    then first you have to create empty dict score_sheet[row['name']] = {} and later use for-loop to move games to this dictionary score_sheet[row['name']][game] = row[game]

        for row in reader:
    
            score_sheet[row['name']] = {}
    
            #print(row)
            for game in list_of_games:
                score_sheet[row['name']][game] = row[game]
    

    You may also use dict comprehension

        # dict comprehension
        score_sheet[row['name']] = {game:row[game] for game in list_of_games}
    

    Full working code used for tests.

    I used io.StringIO only to emulate file-like object in memory - so everyonce can it copy and run.

    # 
    text = '''name,Game_1, Game_2, Game_3
    James,3,7,4
    Charles,2,3,8
    Bob,6,2,4
    '''
    
    import csv
    import io
    
    # --- version with list/tuple ---
    
    score_sheet = {}
    
    with io.StringIO(text) as scores_file:
    #with open("scores.txt") as scores_file:
        reader = csv.DictReader(scores_file)
    
        list_of_games = reader.fieldnames[1:]
    
        for row in reader:
    
            #score_sheet[row['name']] = []
    
            #print(row)
            #for game in list_of_games:
            #    score_sheet[row['name']].append( {game:row[game]} )
    
            # if you need `tuple of dicts` instead of `list of dicts`
            #score_sheet[row['name']] = tuple(score_sheet[row['name']])
    
            # ---
    
            # list comprehension
            score_sheet[row['name']] = [{game:row[game]} for game in list_of_games]
    
            # tuple comprehension
            score_sheet[row['name']] = tuple({game:row[game]} for game in list_of_games)
    
    print(score_sheet)
    
    # --- version with dict ---
    
    score_sheet = {}
    
    with io.StringIO(text) as scores_file:
    #with open("scores.txt") as scores_file:
        reader = csv.DictReader(scores_file)
    
        list_of_games = reader.fieldnames[1:]
    
        for row in reader:
    
            #score_sheet[row['name']] = {}
    
            #print(row)
            #for game in list_of_games:
            #    score_sheet[row['name']][game] = row[game]
    
            # ---
    
            # dict comprehension
            score_sheet[row['name']] = {game:row[game] for game in list_of_games}
    
    print(score_sheet)
    

    EDIT:

    Because csv.DictReader() gives dictonary in every row (as @KelluBundy also mentioned in comment) so you can also remove name from row to get dictionary only with games and it doesn't need to create new dict for games. And it doesn't need list_of_games

    with io.StringIO(text) as scores_file:
    #with open("scores.txt") as scores_file:
        reader = csv.DictReader(scores_file)
    
        #list_of_games = reader.fieldnames[1:]  # no need it
    
        for row in reader:
    
            name = row.pop('name')   # get `name` and remove it from `row`
            score_sheet[name] = row