Unexpected result when creating a dictionary with lists as values

Hi. I am trying to create a new dictionary by using an existing list of players for its keys and values from an existing dictionary for its values. The values need to be summed up by player and position as explained further below. The issue is firstly that I don’t know why the very below code doesn’t produce the expected result and secondly how to get this kind of an approach working if possible. (I have a code further below that produces the expected result but it’s not optimal in the big picture.)

If I try to create the new dictionary and lists for its values with a loop as in the below code:

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}
support = []
stats_players = {}
run = 0

for plr in players:
    support.append(0)
    stats_players[plr] = support

for key in stats_combinations:
    for unit in key:
        stats_players[unit][run] += stats_combinations[key]

        run += 1

    run = 0

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

I get a different and not the expected result (1.0 for all values). However, if I run the below code where I have defined the dictionary ‘stats_players’ with keys (players) and values (lists of values) differently, I get the expected result:

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}
stats_players = {'a': [0,0,0], 'b': [0,0,0], 'c': [0,0,0]}
run = 0

for key in stats_combinations:

    for unit in key:
        stats_players[unit][run] += stats_combinations[key]
        run += 1

    run = 0

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

For further clarification, each player (key) in the new dictionary 'stats_players’ has a list as its value. The values in those lists represent the sum of player combination values from the existing dictionary ‘stats_combinations’ based on the position: First value in the list for each player is the sum of values where that player is first in the player combinations from the ‘stats_combinations’, for example for player a:
the new dictionary should result as stats_players = {‘a’: [0.25+0.05=0.3, 0.3+0.1=0.4, 0.15+0.15=0.3], ‘b’: …}.

And for all the three players the new dictionary should produce the below key-value combinations (don’t mind the non-rounding):
key: a: [0.3, 0.4, 0.3]
key: b: [0.44999999999999996, 0.4, 0.15000000000000002]
key: c: [0.25, 0.2, 0.55].

Hope this isn’t the worst ever try for the first call for help in this forum. :slight_smile:

Thanks in advance

-T

In your first snippet, you’re assigning the exact same list object to every entry in stats_players. In your second second, you create three separate list objects.

See also

https://nedbatchelder.com/text/names.html

1 Like

If you perchance keep a list of Stack Overflow reference questions for these situations, you might want to update the second link you offered, since it’s been marked as a duplicate. Aside from the how-to question about cloning a list, this is the most authoritative version for the “what’s wrong” question:

Of course, this only describes one possible setup for the underlying problem (which doesn’t happen to match OP’s).

Do you mean delete it? Or should they provide the first link twice?

Update in personal records/database for the future, I meant.

Thanks Brian. I study this a bit now and probably ask later today how to best solve it if I don’t get it.

-Sami

Perhaps one solution for me is then just to change the below parts:

**support = []**
stats_players = {}
run = 0

for plr in players:
    support.append(0)
    **stats_players[plr] = support**

to read in full as below:

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}
stats_players = {}
run = 0

for plr in players:
    stats_players[plr] = **len(players)*[0]**

for key in stats_combinations:

    for unit in key:
        stats_players[unit][run] += stats_combinations[key]

        run += 1

    run = 0

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

-Sami

Yes, you have the right idea. (Except that using the ** for bold doesn’t work in the middle of a code block on the forum, and of course you don’t want to add them to your code.)

Here I will illustrate some more tricks you can use to simplify the code:

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}

stats_players = {player: len(players)*[0] for player in players}

for key in stats_combinations:
    for run, unit in enumerate(key):
        stats_players[unit][run] += stats_combinations[key]

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

I get:

key: a: [0.3, 0.4, 0.3]
key: b: [0.44999999999999996, 0.4, 0.15000000000000002]
key: c: [0.25, 0.2, 0.55]

You should also make sure you understand the issues with floating-point precision:

https://0.30000000000000004.com/

(This website name is clever, but it’s very annoying as a reference. It’s hard to remember how many zeros it has, and it doesn’t autocomplete well in browsers.)

Perfect, thanks a lot Karl for all these tips: nice and concise. :slight_smile:

-Sami

I suggest using package memory_graph to more easily see what is going on:

import memory_graph

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}
support = []
stats_players = {}
run = 0

for plr in players:
    support.append(0)
    stats_players[plr] = support

for key in stats_combinations:
    for unit in key:
        stats_players[unit][run] += stats_combinations[key]

        run += 1

    run = 0

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

memory_graph.d() # show graph

Here ‘stats_players’ a,b, and c share a list, whereas in the second program they each have their own list:

import memory_graph

players = ['a', 'b', 'c']
stats_combinations = {('a', 'b', 'c'): 0.25, ('b', 'a', 'c'): 0.3, ('c', 'a', 'b'): 0.1, ('b', 'c', 'a'): 0.15, ('c', 'b', 'a'): 0.15, ('a', 'c', 'b'): 0.05}
stats_players = {'a': [0,0,0], 'b': [0,0,0], 'c': [0,0,0]}
run = 0

for key in stats_combinations:

    for unit in key:
        stats_players[unit][run] += stats_combinations[key]
        run += 1

    run = 0

for key, value in stats_players.items():
    print(f"key: {key}: {value}")

memory_graph.d() # show graph

Hi Bas and thanks for the very useful and educational tip. For some reason I get error messages "ModuleNotFoundError: No module named ‘memory_graph.Memory_Visitor’. I can see that I have Memory_Graph.py and memory_visitor.py files in the …\Lib\site-packages\memory_graph folder.

What to do?

Hello Sami,

I could reproduce the problem on Windows11, it had to do with upper-case filenames. I’ve now fixed the issue in version 0.2.05, install it with:

pip install --upgrade memory_graph

or more specifically:

pip install --upgrade memory_graph==0.2.05

Thanks for reporting this issue. I hope you find the memory_graph package useful.

Regards,
Bas Terwijn