Search code examples
pythonloopsiteration

Iteration in a dictionary with lists as values


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

Solution

  • 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