Search code examples
pythonpandasstyles

take a list of filters and return all possible combinations


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?


Solution

  • 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.)