List to create a nested dictionary from a list of tuples

I have data (indexes) indexed user_idand analysis_type_idretrieved from a database. This is a list of 3 tuples. Sample data:

counts  = [(4, 1, 4), (3, 5, 4), (2, 10, 4), (2, 10, 5)]

where the first element of each tuple is count, the second - analysis_type_idand the last - user_id.

I would like to put this in a dictionary, so I can quickly find the score: given user_idand analysis_type_id. This should be a two-level dictionary. Is there a better structure?

To build a two-level dictionary “manually”, I would encode:

dict = {4:{1:4,5:3,10:2},5:{10:2}}

Where user_idis the first level of the key key, analysis_type_idis the second (sub) key, and countis the value inside the dict.

How do I create a "double depth" in dict keys through list comprehension? Or do I need to resort to a nested for loop where I first iterate over the unique values user_id, then find a match analysis_type_idand populate the counters ... once in a while in a dict?

+6
source share
5 answers

Two dial keys

I would suggest abandoning the idea of ​​embedding dictionaries and just use the two tuples as keys directly. For instance:

d = { (user_id, analysis_type_id): count for count, analysis_type_id, user_id in counts}

- -. python ( ), () . ( 2 , ), ( user_id, analysis_type_id).

. , dict . , , .. , , , .

namedtuple

. :

from collections import namedtuple
IdPair = namedtuple("IdPair", "user_id, analysis_type_id")

:

d = { IdPair(user_id, analysis_type_id): count for count, analysis_type_id, user_id in counts}

:

somepair = IdPair(user_id = 4, analysis_type_id = 1)
d[somepair]

, , - :

user_id = somepair.user_id # very nice syntax

- , . :

>>> d[IdPair(0,0)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: IdPair(user_id=0, analysis_type_id=0)

; user_id , analysis_type_id, ?

, dict, . :

class CountsDict(dict):
    """A dict for storing IdPair keys and count values as integers.

    Provides more detailed traceback information than a regular dict.
    """
    def __getitem__(self, k):
        try:
            return super().__getitem__(k)
        except KeyError as exc:
            raise self._handle_bad_key(k, exc) from exc
    def _handle_bad_key(self, k, exc):
        """Provides a custom exception when a bad key is given."""
        try:
            user_id, analysis_type_id = k
        except:
            return exc
        has_u_id = next((True for u_id, _ in self if u_id==user_id), False)
        has_at_id  = next((True for _, at_id in self if at_id==analysis_type_id), False)
        exc_lookup = {(False, False):KeyError(f"CountsDict missing pair: {k}"),
                      (True, False):KeyError(f"CountsDict missing analysis_type_id: "
                                             f"{analysis_type_id}"),
                      (False, True):KeyError(f"CountsDict missing user_id: {user_id}")}
        return exc_lookup[(user_id, analysis_type_id)]

, dict.

, dict ( ). , defaultdict ( int factory) . :

from collections import defaultdict
my_dict = defaultdict(default_factory=int, 
                      ((user_id, analysis_type_id), count) for count, analysis_type_id, user_id in counts))

, , . , :

value = my_dict['I'm not a two tuple, sucka!!!!'] # <-- will be added to my_dict

, CountsDict, , dict defaultdict. , defaultdict, , . , , , IdPair.

from collections import defaultdict

class CountsDict(defaultdict):
    """A dict for storing IdPair keys and count values as integers.

    Missing two-tuple keys are converted to an IdPair. Invalid keys raise a KeyError.
    """
    def __getitem__(self, k):
        try:
            user_id, analysis_type_id = k
        except:
            raise KeyError(f"The provided key {k!r} is not a valid key.")
        else:
            # convert two tuple to an IdPair if it was not already
            k = IdPair(user_id, analysis_type_id)
        return super().__getitem__(k)

, defaultdict:

my_dict = CountsDict(default_factory=int, 
                     ((user_id, analysis_type_id), count) for count, analysis_type_id, user_id in counts))

. , IdPair ( __setitem__ ). , __init__.

.

+6

defaultdict, , :

from collections import defaultdict
dct = defaultdict(dict)  # do not shadow the built-in 'dict'
for x, y, z in counts:
    dct[z][y] = x
dct
# defaultdict(dict, {4: {1: 4, 5: 3, 10: 2}, 5: {10: 2}})

, itertools.groupby :

from itertools import groupby
dct = {k: {y: x for x, y, _ in g} for k, g in groupby(sorted(counts, key=lambda c: c[2]), key=lambda c: c[2])}

user_id, .

+2

defaultdict. defaultdict, dicts. , :

from collections import defaultdict

counts  = [(4, 1, 4), (3, 5, 4), (2, 10, 4), (2, 10, 5)]
dct = defaultdict(dict)
for count, analysis_type_id, user_id in counts:
    dct[user_id][analysis_type_id]=count

dct
# defaultdict(dict, {4: {1: 4, 5: 3, 10: 2}, 5: {10: 2}})

# if you want a 'normal' dict, you can finish with this:
dct = dict(dct)

dicts setdefault:

counts  = [(4, 1, 4), (3, 5, 4), (2, 10, 4), (2, 10, 5)]
dct = dict()
for count, analysis_type_id, user_id in counts:
    dct.setdefault(user_id, dict())
    dct[user_id][analysis_type_id]=count

dct
# {4: {1: 4, 5: 3, 10: 2}, 5: {10: 2}}

, , for .

0
source

you can use the following logic. There is no need to import any package, we just need to use it correctly for loops.

counts = [(4, 1, 4), (3, 5, 4), (2, 10, 4), (2, 10, 5)] dct = {x[2]:{y[1]:y[0] for y in counts if x[2] == y[2]} for x in counts }

"" will be {4: {1: 4, 5: 3, 10: 2}, 5: {10: 2}} "" "

0
source

You can list the understanding for nested loops with a condition and use one or more of them to select items:

# create dict with tuples
line_dict = {str(nest_list[0]) : nest_list[1:] for nest_list in nest_lists for elem in nest_list if elem== nest_list[0]}
print(line_dict)

 # create dict with list 
line_dict1 = {str(nest_list[0]) list(nest_list[1:]) for nest_list in nest_lists for elem in nest_list if elem== nest_list[0]}
print(line_dict1)

Example: nest_lists = [("a","aa","aaa","aaaa"), ("b","bb","bbb","bbbb") ("c","cc","ccc","cccc"), ("d","dd","ddd","dddd")]

Output: {'a': ('aa', 'aaa', 'aaaa'), 'b': ('bb', 'bbb', 'bbbb'), 'c': ('cc', 'ccc', 'cccc'), 'd': ('dd', 'ddd', 'dddd')}, {'a': ['aa', 'aaa', 'aaaa'], 'b': ['bb', 'bbb', 'bbbb'], 'c': ['cc', 'ccc', 'cccc'], 'd': ['dd', 'ddd', 'dddd']}
0
source

Source: https://habr.com/ru/post/1688495/


All Articles