Search code examples
pandasboolean-logic

how to combine logically the data on columns of a pandas DataFrame to generate a new DataFrame?


I've made a programm that return a "belonging table" DataFrame of edges of Multigraph that models a Electrical Grid. each line is a a path between a load and a source, the columns are the names of the lines that connect the load to the source.

The program I wrote generates an output df that looks like this one, but much larger.

import pandas as pd

belonging = pd.DataFrame({'A': {0: False, 1: False, 2: True, 3: True},
              'B': {0: True, 1: True, 2: False, 3: False},
              'C': {0: False, 1: True, 2: False, 3: True},
              'D': {0: True, 1: False, 2: True, 3: False}})

>>>
       A      B      C      D      E     F
0  False  False   True  False   True  True
1  False  False   True   True  False  True
2   True   True  False  False   True  True
3   True   True  False   True  False  True

Now I need to generate a "failure modes" table, that give an output that would be like this:

result = pd.DataFrame(
    {'Failure Modes' : {0: 'F', 1: 'A // C', 2: "B // C", 3: "D // E"},
     'Order of Failure' : {0: 1, 1: 2, 2: 2, 3: 2}
    }
)
>>> 
  Failure Modes  Order of Failure
0             F                 1
1        A // C                 2
2        B // C                 2
3        D // E                 2

The failure Modes table is contructed from the boolean value of the columns, if all the items in a column are true, than that is a first order of faliure. The second order of faliure tries to check for the truth values of every two columns, except those already found to be a first order. And so on the nth-order. with n <= len(belonging.columns).

Describing it makes it sound simpler to me than I can write in code. Thank you in advance.


Solution

  • Using the soluting presented by @Cammeron Riddel and @Mozway I got to this solution to the problem:

    bt = pd.DataFrame({
        'a': [False, False, True,True],
        'b': [False, False, True, True],
        'c': [True, True, False, False],
        'd': [False, True, False, True],
        'e': [True, False, True, False],
        'f': [True, True, True, True],
        })
    # bt = belonging_table
    
    def power_set(entities):
        for i in range(len(entities)+1):
            yield from combinations(entities, r=i)
    
    seen = defaultdict(set)
    failures = pd.DataFrame(columns=['Oder'])
    for columns in power_set(bt.columns):
    
        order = len(columns)
        last_order = order - 1
    
        seen[order] = seen[order].union(seen[last_order])
    
        if len(columns) == 0 or set(columns).intersection(seen[last_order]):
            continue
    
        if logical_xor.reduce(bt[list(columns)], axis=1).all():
            failures.loc['//'.join([str(edge) for edge in columns])] = order
            seen[order] = seen[order].union(columns)
    
        if len(seen[order]) == len(bt.columns):
            break
    
    failures
    
        Order
    f       1
    a//c    2
    b//c    2
    d//e    2
    

    This changes where made because of errors found in larger origin destiny pairs with up too 500 different paths