Iteration in a dictionary with lists as values

Question:

A dictionary with lists as values and ascending dates as keys, that I want to understand how many times M in the total past times P, cover some of the current numbers.

For example, for L19981120: [2, 3, 5]: 2 numbers in the [2, 3, 5], appeared 3 times in the past 9 times.

The code looks verbose, and only printing some iterations, but not all.

What is the correct and better way to do so?

data = {
"L19980909":    [11,12,25],
"L19981013":    [19,28,31],
"L19981016":    [4,9,31],
"L19981020":    [8,11,17],
"L19981023":    [5,22,25],
"L19981027":    [5,20,27],
"L19981030":    [12,19,26],
"L19981105":    [31,32,38],
"L19981109":    [2,22,24],
"L19981110":    [2,16,19],
"L19981113":    [9,15,17],
"L19981119":    [2,10,11],
"L19981120":    [2,3,5],
"L19981126":    [4,6,14],
"L19981127":    [5,9,18],
"L19981201":    [1,6,7]}

value_list = list(data.values())

for idx, (k, v) in enumerate(data.items()):
    ever_more_than_times = []
    for how_many_past in [7,8,9]:
        if idx >= how_many_past:
            past_appeared = sum(value_list[idx-how_many_past:idx],[])
        
            for more_than_times in [2,3]:
                if how_many_past > more_than_times:
                
                    for ox in list(range(1,40)):
                        if past_appeared.count(ox) >= more_than_times:
                            ever_more_than_times.append(ox)
    ever_more_than_times = list(set(ever_more_than_times))

    hit = len(set(ever_more_than_times) & set(v))

    if hit != 0:
        print (k,'$',v,'$',how_many_past,'$',more_than_times,'$',hit)

Output:

L19981105 $ [31, 32, 38] $ 9 $ 3 $ 1
L19981110 $ [2, 16, 19] $ 9 $ 3 $ 1
L19981119 $ [2, 10, 11] $ 9 $ 3 $ 1
L19981120 $ [2, 3, 5] $ 9 $ 3 $ 2
L19981127 $ [5, 9, 18] $ 9 $ 3 $ 1
Asked By: Mark K

||

Answers:

You can use a prefix-sum to count how many times a number appears, before a given index:

prefix_counter = [defaultdict(int) for k in data]
data_keys = list(data.keys())
last_key = data_keys[0]
for i, k in enumerate(data_keys[1:], start=1):
    prev_counts = Counter(data[last_key])
    prefix_counter[i] = defaultdict(int, prefix_counter[i-1])
    for count in prev_counts:
        prefix_counter[i][count] += prev_counts[i]
    last_key = k

Now the prefix_counter list looks like:

[
defaultdict(<class 'int'>, {})
defaultdict(<class 'int'>, {11: 1, 12: 1, 25: 1})
defaultdict(<class 'int'>, {11: 1, 12: 1, 25: 1, 19: 1, 28: 1, 31: 1})
defaultdict(<class 'int'>, {11: 1, 12: 1, 25: 1, 19: 1, 28: 1, 31: 2, 4: 1, 9: 1})
defaultdict(<class 'int'>, {11: 2, 12: 1, 25: 1, 19: 1, 28: 1, 31: 2, 4: 1, 9: 1, 8: 1, 17: 1})
defaultdict(<class 'int'>, {11: 2, 12: 1, 25: 2, 19: 1, 28: 1, 31: 2, 4: 1, 9: 1, 8: 1, 17: 1, 5: 1, 22: 1})
defaultdict(<class 'int'>, {11: 2, 12: 1, 25: 2, 19: 1, 28: 1, 31: 2, 4: 1, 9: 1, 8: 1, 17: 1, 5: 2, 22: 1, 20: 1, 27: 1})
defaultdict(<class 'int'>, {11: 2, 12: 2, 25: 2, 19: 2, 28: 1, 31: 2, 4: 1, 9: 1, 8: 1, 17: 1, 5: 2, 22: 1, 20: 1, 27: 1, 26: 1})
defaultdict(<class 'int'>, {11: 2, 12: 2, 25: 2, 19: 2, 28: 1, 31: 3, 4: 1, 9: 1, 8: 1, 17: 1, 5: 2, 22: 1, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1})
defaultdict(<class 'int'>, {11: 2, 12: 2, 25: 2, 19: 2, 28: 1, 31: 3, 4: 1, 9: 1, 8: 1, 17: 1, 5: 2, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 1, 24: 1})
defaultdict(<class 'int'>, {11: 2, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 1, 9: 1, 8: 1, 17: 1, 5: 2, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 2, 24: 1, 16: 1})
defaultdict(<class 'int'>, {11: 2, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 1, 9: 2, 8: 1, 17: 2, 5: 2, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 2, 24: 1, 16: 1, 15: 1})
defaultdict(<class 'int'>, {11: 3, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 1, 9: 2, 8: 1, 17: 2, 5: 2, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 3, 24: 1, 16: 1, 15: 1, 10: 1})
defaultdict(<class 'int'>, {11: 3, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 1, 9: 2, 8: 1, 17: 2, 5: 3, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 4, 24: 1, 16: 1, 15: 1, 10: 1, 3: 1})
defaultdict(<class 'int'>, {11: 3, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 2, 9: 2, 8: 1, 17: 2, 5: 3, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 4, 24: 1, 16: 1, 15: 1, 10: 1, 3: 1, 6: 1, 14: 1})
defaultdict(<class 'int'>, {11: 3, 12: 2, 25: 2, 19: 3, 28: 1, 31: 3, 4: 2, 9: 3, 8: 1, 17: 2, 5: 4, 22: 2, 20: 1, 27: 1, 26: 1, 32: 1, 38: 1, 2: 4, 24: 1, 16: 1, 15: 1, 10: 1, 3: 1, 6: 1, 14: 1, 18: 1})
]

We can now know how many numbers are within a range by subtracting the values in the dictionary of one index, by the values in the dictionary of another:

i2 = 3
i1 = i2 - 3
num =  31
prev_count = prefix_counter[i2][num] - prefix_counter[i1][num]
print(prev_count)  # 2

Another example:

i2 = data_keys.index('L19981120')
i1 = i2 - 9
num = 2
prev_count = prefix_counter[i2][num] - prefix_counter[i1][num]
print(prev_count) # 3
Answered By: Tom McLean

Don’t you have to sort the keys first?

from sortedcontainers import SortedDict

data = SortedDict(
    {
        "L19980909": [11, 12, 25],
        "L19981013": [19, 28, 31],
        "L19981016": [4, 9, 31],
        "L19981020": [8, 11, 17],
        "L19981023": [5, 22, 25],
        "L19981027": [5, 20, 27],
        "L19981030": [12, 19, 26],
        "L19981105": [31, 32, 38],
        "L19981109": [2, 22, 24],
        "L19981110": [2, 16, 19],
        "L19981113": [9, 15, 17],
        "L19981119": [2, 10, 11],
        "L19981120": [2, 3, 5],
        "L19981126": [4, 6, 14],
        "L19981127": [5, 9, 18],
        "L19981201": [1, 6, 7],
    }
)

for target_date, tlist in data.items():

    # count previous occurances
    counts = {}
    for date, nlist in data.items():
        if date == target_date:
            break
        for n in nlist:
            if n in counts:
                counts[n] += 1
            else:
                counts[n] = 1
    for n in tlist:
        if n not in counts:
            counts[n] = 0

    if counts[tlist[0]] + counts[tlist[1]] + counts[tlist[2]] > 0:
        print(
            target_date,
            "$",
            tlist,
            "$",
            counts[tlist[0]],
            "$",
            counts[tlist[1]],
            "$",
            counts[tlist[2]],
        )

Produces:

L19981016 $ [4, 9, 31] $ 0 $ 0 $ 1
L19981020 $ [8, 11, 17] $ 0 $ 1 $ 0
L19981023 $ [5, 22, 25] $ 0 $ 0 $ 1
L19981027 $ [5, 20, 27] $ 1 $ 0 $ 0
L19981030 $ [12, 19, 26] $ 1 $ 1 $ 0
L19981105 $ [31, 32, 38] $ 2 $ 0 $ 0
L19981109 $ [2, 22, 24] $ 0 $ 1 $ 0
L19981110 $ [2, 16, 19] $ 1 $ 0 $ 2
L19981113 $ [9, 15, 17] $ 1 $ 0 $ 1
L19981119 $ [2, 10, 11] $ 2 $ 0 $ 2
L19981120 $ [2, 3, 5] $ 3 $ 0 $ 2
L19981126 $ [4, 6, 14] $ 1 $ 0 $ 0
L19981127 $ [5, 9, 18] $ 3 $ 2 $ 0
L19981201 $ [1, 6, 7] $ 0 $ 1 $ 0
Answered By: DobbyTheElf
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.