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!
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 |
`
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
],]
`
Here's one approach:
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
to apply df.melt
on the previous index (now: ('resources', '')
) + df.dropna
on 'value'
column.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
df.assign
to add a column ('tmp'
) as a tuple (list
+ zip
) containing the digits from 'resources'
(via Series.str.split
+ Series.astype
) and values from 'value'
..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)
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
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
Series.to_list
.