Используйте понимание списка со сглаженными значениями для списка кортежей и переходите к DataFrame
конструктору:
L = [(x, k, v) for x, y in df[['a','b']].values for k, v in y.items()]
df = pd.DataFrame(L, columns=['a','k','v'])
print (df)
a k v
0 1 a 1
1 1 b 2
2 2 k 4
3 2 v 6
4 3 z 3
EDIT: для общего решения, работающего с уникальным индексом, возможно изменение решения с помощью DataFrame.pop
для извлечения b
столбца, добавления нового столбца idx
по значениям индекса, преобразования в индекс и последнего использования DataFrame.join
:
L = [(x, k, v) for x, y in df.pop('b').items() for k, v in y.items()]
df1 = pd.DataFrame(L, columns=['idx','k','v']).set_index('idx').rename_axis(None)
df = df.join(df1).reset_index(drop=True)
print (df)
a k v
0 1 a 1
1 1 b 2
2 2 k 4
3 2 v 6
4 3 z 3