take a list of filters and return all possible combinations

Viewed 89

I'm using pandas. I have a list of filters to be used as part of dataframe.loc[filter, :], eg.

df = pd.DataFrame(np.random.choice([True, False], size=[10,8]))

filter1 = df[1] == True
filter2 = df[5] == False
filter3 = (df[2] == True) & (df[7] == False)

filters = [filter1, filter2, filter3]

How do I generate a list/dictionary of all possible combinations of filters? eg.

{combination_1 : (filter_1),
combination_2 : (filter_1 & filter_2),
combination_3 : (filter_1 & filter_3),
combination_4 : (filter_1 & filter_2, & filter_3),
etc.}

so that I can loop over the list and apply a different function to the dataframe for each combination. I want to do this so I can highlight each row a different colour depending on which combination of the filters was true.

I have written a function to generate a hex string:

def hex_string(filter_1, filter_2, filter_3):
    bool_list = [filter_1, filter_2, filter_3]
    hex_list = ["FF" if value else "00" for value in bool_list]
    string = f"#{''.join(hexlist)}"
    return string

The idea would be that I would style my dataframe with a function that looks something like this

def highlight_rows(dataframe):
    df = dataframe.copy()
    iterable = combination_generator_function(filter_list)

    for combination in iterable:
        df.loc[combination,:] = "background-color:{hex_string(combination)}"
    return df

df.style.apply(highlight_rows, axis=None)

I feel like this should be a lot easier than I'm making it out to be. Is it possible to do this without typing out each individual combination and the hex string that would go with it?

2 Answers

Does this work:

from itertools import combinations
from functools import reduce

filter_combos = [
    reduce(lambda x, y: x & y, combo)
    for i in range(1, len(filters) + 1)
    for combo in combinations(filters, i)
]

To illustrate: combinations(filters, 2) gives you the tuples

(filter1, filter2), (filter1, filter3), (filter2, filter3)

and the reduce(lambda x, y: x & y, combo) "reduces" tuples like (filter1, filter2) via the lambda-function to filter1 & filter2. (reduce works cumulatively from left to right, i.e. it works not only for tuples with 2 elements. And for tuples with only one element it returns just the element.)

I think itertools is a good solution here.

import itertools  
filters = [filter_1, filter_2, filter_3]  

combinationList = []

for i in range(len(filters)):  
    combinationList += list(itertools.permutations(filters,i+1))

print(combinationList) #Probably cant print the filters, but combinationList is the list off all possible combinations from 1-3 filters per combination
Related