simply use stack and unstack from pandas.
with my understanding, use unstack
to turn the inner index's contents to columns.
As using stack
to turn the columns to the inner index's contents.
# sample data
data = [{'key1': 'k1', 'key2': 'k2', 'keyn': 'kn', 'type': 'p1', 'val1': 1, 'val2': 2, 'valn': 7}, {'key1': 'k1', 'key2': 'k2', 'keyn': 'kn', 'type': 'p2', 'val1': 6, 'val2': 1, 'valn': 5}, {'key1': 'k1', 'key2': 'k2', 'keyn': 'kn', 'type': 'p3', 'val1': 8, 'val2': 4, 'valn': 1}, {'key1': 'k3', 'key2': 'k2', 'keyn': 'kn', 'type': 'p1', 'val1': 4, 'val2': 6, 'valn': 9}, {'key1': 'k3', 'key2': 'k2', 'keyn': 'kn', 'type': 'p2', 'val1': 6, 'val2': 1, 'valn': 0}, {'key1': 'k3', 'key2': 'k2', 'keyn': 'kn', 'type': 'p3', 'val1': 1, 'val2': 2, 'valn': 8}]
df = pd.DataFrame(data)
# stack the df, with index `['key1', 'key2', 'keyn', 'type']`
cols = df.columns[df.columns.str.startswith('key')].tolist()
cols.append('type')
# print(cols) # ['key1', 'key2', 'keyn', 'type']
dfn = df.set_index(cols).loc[:, 'val1':'valn'].stack().reset_index()
# print(dfn)
# create col_name as `valn_typep1`
dfn['col_name'] = dfn.iloc[:,-2] + '_type' + dfn.iloc[:,-3]
# set index with `['key1', 'key2', 'keyn', 'col_name']`, value is column 0, and unstack, transfer index col_name to columns
cols = df.columns[df.columns.str.startswith('key')].tolist()
cols.append('col_name')
# print(cols) # ['key1', 'key2', 'keyn', 'col_name']
df_result = dfn.set_index(cols)[0].unstack().reset_index()
# print(df_result)
result:
print(dfn)
key1 key2 keyn type level_4 0
0 k1 k2 kn p1 val1 1
1 k1 k2 kn p1 val2 2
2 k1 k2 kn p1 valn 7
3 k1 k2 kn p2 val1 6
4 k1 k2 kn p2 val2 1
5 k1 k2 kn p2 valn 5
6 k1 k2 kn p3 val1 8
7 k1 k2 kn p3 val2 4
8 k1 k2 kn p3 valn 1
9 k3 k2 kn p1 val1 4
10 k3 k2 kn p1 val2 6
11 k3 k2 kn p1 valn 9
12 k3 k2 kn p2 val1 6
13 k3 k2 kn p2 val2 1
14 k3 k2 kn p2 valn 0
15 k3 k2 kn p3 val1 1
16 k3 k2 kn p3 val2 2
17 k3 k2 kn p3 valn 8
print(df_result)
col_name key1 key2 keyn val1_typep1 val1_typep2 val1_typep3 val2_typep1 \
0 k1 k2 kn 1 6 8 2
1 k3 k2 kn 4 6 1 6
col_name val2_typep2 val2_typep3 valn_typep1 valn_typep2 valn_typep3
0 1 4 7 5 1
1 1 2 9 0 8