1

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

],] 

`

1
  • Can you give your input dataframe as code? You dataframe headers are really tuples? Commented Jan 3 at 13:52

2 Answers 2

1

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)
  • Now, use df.groupby with the variable columns (original pd.MultiIndex) with sort=False to preserve order, and get 'tmp' as list (groupby.apply).
.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
  • Chain another df.groupby, now solely with 'variable_0' (level 0 from original pd.MultIndex) and get list again.
.groupby('variable_0').apply(list)

variable_0
A    [[(1, 1)], [(2, 2), (3, 3)]]
B                      [[(2, 2)]]
Name: tmp, dtype: object
3
  • how do I avoid automatic or control the sorting of "variable_1" after executing .groupby(['variable_0', 'variable_1'])['tmp'] ? When there is an index of tasks greater than 10, the sort automatically alphabetically orders the strings t1, t2,...,t11. I need the control authority in your solution architecture to preserve the task order index for a given widget.
    – BP130
    Commented Jan 6 at 14:25
  • @BP130: good point. You need to add sort=False for the groupby statements. Updated accordingly. Let me know if it works as expected now.
    – ouroboros1
    Commented Jan 6 at 14:37
  • 1
    this works perfectly! Thank you for the support.
    – BP130
    Commented Jan 6 at 16:18
0

If df is your multi-index dataframe, you could get the wanted components list by looping over your 3 blocks of widgets and using list comprehension with np.where which gives the rows and columns index where the values are not NaN:

widget = {}
components = []
widgets_col = sorted(list(set(df.columns.get_level_values(0))))  # ['Widget A (idx = 0)', 'Widget B (idx = 1)', 'Widget C (idx =2)']

for col in widgets_col:
    mask = df.loc[:, (col,)].notna().T
    components.append([[(j+1, df.loc["m_"+str(j+1), (col, "t"+str(i+1))])] 
                       for (i, j) in zip(*np.where(mask))])
print(components)

[

[
[(1, 10.0)], [(12, 1.0)], [(2, 15.0)], [(3, 23.0)], [(4, 27.0)], [(12, 1.0)], [(8, 10.0)], [(9, 10.0)], [(10, 10.0)], [(12, 1.0)], [(2, 17.0)], [(3, 15.0)], [(4, 19.0)], [(12, 1.0)], [(8, 10.0)], [(9, 10.0)], [(10, 10.0)], [(12, 1.0)], [(5, 15.0)], [(6, 16.0)], [(7, 23.0)], [(12, 1.0)], [(8, 10.0)], [(9, 10.0)], [(10, 10.0)], [(12, 1.0)], [(11, 14.0)]
], 

[
[(1, 23.0)], [(12, 1.0)], [(2, 30.0)], [(3, 26.0)], [(4, 22.0)], [(12, 1.0)], [(8, 15.0)], [(9, 15.0)], [(10, 15.0)], [(12, 1.0)], [(2, 23.0)], [(3, 21.0)], [(4, 18.0)], [(12, 1.0)], [(8, 15.0)], [(9, 15.0)], [(10, 15.0)], [(12, 1.0)], [(5, 21.0)], [(6, 16.0)], [(7, 14.0)], [(12, 1.0)], [(8, 15.0)], [(9, 15.0)], [(10, 15.0)], [(12, 1.0)], [(11, 12.0)]
], 

[
[(1, 17.0)], [(12, 1.0)], [(2, 24.0)], [(3, 22.0)], [(4, 29.0)], [(12, 1.0)], [(8, 13.0)], [(9, 13.0)], [(10, 13.0)], [(12, 1.0)], [(5, 23.0)], [(6, 25.0)], [(7, 30.0)], [(12, 1.0)], [(8, 13.0)], [(9, 13.0)], [(10, 13.0)], [(12, 1.0)], [(11, 10.0)]
]

]
2
  • Your code converts each second Tuple element to a float because of handling the NaN values. Would be simple to cast to int as this seems to be what the OP expects. Commented Jan 2 at 23:03
  • @BP130 this solution didn't work for you?
    – rehaqds
    Commented Jan 6 at 20:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.