pythonpandasdataframelist

Parsing Multi-Index Pandas Data Frame for Tuple List Appendage


Problem/Task: create a function that inputs a pandas data frame represented by the markdown in Fig 1 and converts/outputs it to a list with the structure represented in Fig 2.

I look forward to any feedback/support anyone might have!

Fig 1: Pandas Data Frame (Function Input) as Markdown

resources ('Widget A (idx = 0)', 't1') ('Widget A (idx = 0)', 't2') ('Widget A (idx = 0)', 't3') ('Widget A (idx = 0)', 't4') ('Widget A (idx = 0)', 't5') ('Widget A (idx = 0)', 't6') ('Widget A (idx = 0)', 't7') ('Widget A (idx = 0)', 't8') ('Widget A (idx = 0)', 't9') ('Widget A (idx = 0)', 't10') ('Widget A (idx = 0)', 't11') ('Widget A (idx = 0)', 't12') ('Widget A (idx = 0)', 't13') ('Widget A (idx = 0)', 't14') ('Widget A (idx = 0)', 't15') ('Widget B (idx = 1)', 't1') ('Widget B (idx = 1)', 't2') ('Widget B (idx = 1)', 't3') ('Widget B (idx = 1)', 't4') ('Widget B (idx = 1)', 't5') ('Widget B (idx = 1)', 't6') ('Widget B (idx = 1)', 't7') ('Widget B (idx = 1)', 't8') ('Widget B (idx = 1)', 't9') ('Widget B (idx = 1)', 't10') ('Widget B (idx = 1)', 't11') ('Widget B (idx = 1)', 't12') ('Widget B (idx = 1)', 't13') ('Widget B (idx = 1)', 't14') ('Widget B (idx = 1)', 't15') ('Widget C (idx =2)', 't1') ('Widget C (idx =2)', 't2') ('Widget C (idx =2)', 't3') ('Widget C (idx =2)', 't4') ('Widget C (idx =2)', 't5') ('Widget C (idx =2)', 't6') ('Widget C (idx =2)', 't7') ('Widget C (idx =2)', 't8') ('Widget C (idx =2)', 't9') ('Widget C (idx =2)', 't10') ('Widget C (idx =2)', 't11')
m_1 10 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 23 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 17 nan nan nan nan nan nan nan nan nan nan
m_2 nan nan 15 nan nan nan 17 nan nan nan nan nan nan nan nan nan nan 30 nan nan nan 23 nan nan nan nan nan nan nan nan nan nan 24 nan nan nan nan nan nan nan nan
m_3 nan nan 23 nan nan nan 15 nan nan nan nan nan nan nan nan nan nan 26 nan nan nan 21 nan nan nan nan nan nan nan nan nan nan 22 nan nan nan nan nan nan nan nan
m_4 nan nan 27 nan nan nan 19 nan nan nan nan nan nan nan nan nan nan 22 nan nan nan 18 nan nan nan nan nan nan nan nan nan nan 29 nan nan nan nan nan nan nan nan
m_5 nan nan nan nan nan nan nan nan nan nan 15 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 21 nan nan nan nan nan nan nan nan nan nan 23 nan nan nan nan
m_6 nan nan nan nan nan nan nan nan nan nan 16 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 16 nan nan nan nan nan nan nan nan nan nan 25 nan nan nan nan
m_7 nan nan nan nan nan nan nan nan nan nan 23 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 14 nan nan nan nan nan nan nan nan nan nan 30 nan nan nan nan
m_8 nan nan nan nan 10 nan nan nan 10 nan nan nan 10 nan nan nan nan nan nan 15 nan nan nan 15 nan nan nan 15 nan nan nan nan nan nan 13 nan nan nan 13 nan nan
m_9 nan nan nan nan 10 nan nan nan 10 nan nan nan 10 nan nan nan nan nan nan 15 nan nan nan 15 nan nan nan 15 nan nan nan nan nan nan 13 nan nan nan 13 nan nan
m_10 nan nan nan nan 10 nan nan nan 10 nan nan nan 10 nan nan nan nan nan nan 15 nan nan nan 15 nan nan nan 15 nan nan nan nan nan nan 13 nan nan nan 13 nan nan
m_11 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 14 nan nan nan nan nan nan nan nan nan nan nan nan nan nan 12 nan nan nan nan nan nan nan nan nan nan 10
m_12 nan 1 nan 1 nan 1 nan 1 nan 1 nan 1 nan 1 nan nan 1 nan 1 nan 1 nan 1 nan 1 nan 1 nan 1 nan nan 1 nan 1 nan 1 nan 1 nan 1 nan

Fig 2: Example of Target Data Structure (Function Output) for List

`

components = [ 

# widget A -> [task_0...task_i] -> [(machine_id_0, dur_0)...machine_id_i, dur_i]
[   
    [(1, 10)], #t1
    [(12, 1)], #t2
    [(2, 15), (3, 23), (4,27)], #t3
    [(12, 1)], #t4
    [(8,10), (9,10), (10,10)], #t5
    [(12, 1)], #t6
    [(2, 17), (3, 15), (4,19)], #t7
    [(12, 1)], #t8
    [(8,10), (9,10), (10,10)], #t9
    [(12, 1)], #t10
    [(5, 15), (6, 16), (7,23)], #t11
    [(12, 1)], #t12
    [(8,10), (9,10), (10,10)], #t13
    [(12, 1)], #t14
    [(11,14)], #t15

],

# widget B -> [task_0...task_i] -> [(machine_id_0, dur_0)...machine_id_i, dur_i]
[   
    [(1, 23)], #t1
    [(12, 1)], #t2
    [(2, 30), (3, 26), (4,22)], #t2
    [(12, 1)], #t2
    [(8,15), (9,15), (10,15)], #t3
    [(12, 1)], #t2
    [(2, 23), (3, 21), (4,18)], #t4
    [(12, 1)], #t2
    [(8,15), (9,15), (10,15)], #t5
    [(12, 1)], #t2
    [(5, 21), (6, 16), (7,14)], #t6
    [(12, 1)], #t2
    [(8,15), (9,15), (10,15)], #t7
    [(12, 1)], #t2
    [(11,12)], #t8

],

# widget C -> [task_0...task_i] -> [(machine_id_0, dur_0)...machine_id_i, dur_i]
[   
    [(1, 17)], #t1
    [(12, 1)], #t2
    [(2, 24), (3, 22), (4,29)], #t3
    [(12, 1)], #t4
    [(8,13), (9,13), (10,13)], #t5
    [(12, 1)], #t6
    [(2, 23), (3, 25), (4,30)], #t7
    [(12, 1)], #t8
    [(8,13), (9,13), (10,13)], #t9
    [(12, 1)], #t10
    [(11,10)], #t11

],] 

`


Solution

  • Here's one approach:

    Minimal Reproducible Example

    import pandas as pd
    import numpy as np
    
    data = [[1, np.nan, np.nan],
            [np.nan, 2, 2],
            [np.nan, 3, np.nan]]
            
    m_idx = pd.MultiIndex.from_tuples(
        [('A', 't1'),
         ('A', 't2'),
         ('B', 't1')]
        )
    
    idx = pd.Index([f'm_{i}' for i in range(1, 4)], name='resources')
    
    df = pd.DataFrame(data, columns=m_idx, index=idx)
    
                 A         B
                t1   t2   t1
    resources               
    m_1        1.0  NaN  NaN
    m_2        NaN  2.0  2.0
    m_3        NaN  3.0  NaN
    

    Desired output

    components = [
        [ # A
            [(1, 1)], # t1
            [(2, 2), (3, 3)] # t2
        ],
        [ # B
            [(2, 2)] # t1
        ]
    ]
    

    Code

    components = (
        df.reset_index()
        .melt([('resources','')])
        .dropna(subset='value')
        .assign(
            tmp=lambda x: 
                list(
                    zip(
                        x[('resources','')].str.split('_').str[1].astype(int), 
                        x['value'].astype(int))
                    )
                )
        .groupby(['variable_0', 'variable_1'], sort=False)['tmp']
        .apply(list)
        .groupby('variable_0', sort=False)
        .apply(list)
        .to_list()
        )
    

    Output:

    components
    
    [[[(1, 1)], [(2, 2), (3, 3)]], [[(2, 2)]]]
    

    Explanation / Intermediates

    df.reset_index().melt([('resources','')]).dropna(subset='value')
    
      (resources, ) variable_0 variable_1  value
    0           m_1          A         t1    1.0
    4           m_2          A         t2    2.0
    5           m_3          A         t2    3.0
    7           m_2          B         t1    2.0
    
    .assign(...)
    
      (resources, ) variable_0 variable_1  value     tmp
    0           m_1          A         t1    1.0  (1, 1)
    4           m_2          A         t2    2.0  (2, 2)
    5           m_3          A         t2    3.0  (3, 3)
    7           m_2          B         t1    2.0  (2, 2)
    
    .groupby(['variable_0', 'variable_1'])['tmp'].apply(list)
    
    variable_0  variable_1
    A           t1                    [(1, 1)]
                t2            [(2, 2), (3, 3)]
    B           t1                    [(2, 2)]
    Name: tmp, dtype: object
    
    .groupby('variable_0').apply(list)
    
    variable_0
    A    [[(1, 1)], [(2, 2), (3, 3)]]
    B                      [[(2, 2)]]
    Name: tmp, dtype: object