pandas: When cell contents are lists, create a row for each element in the list

I have a dataframe where some cells contain lists of multiple values. Rather than storing multiple values in a cell, I'd like to expand the dataframe so that each item in the list gets its own row (with the same values in all other columns). So if I have:

    import pandas as pd
    import numpy as np

    df = pd.DataFrame(
        {'trial_num': [1, 2, 3, 1, 2, 3],
         'subject': [1, 1, 1, 2, 2, 2],
         'samples': [list(np.random.randn(3).round(2)) for i in range(6)]
        }
    )

    df
    Out[10]: 
                     samples  subject  trial_num
    0    [0.57, -0.83, 1.44]        1          1
    1    [-0.01, 1.13, 0.36]        1          2
    2   [1.18, -1.46, -0.94]        1          3
    3  [-0.08, -4.22, -2.05]        2          1
    4     [0.72, 0.79, 0.53]        2          2
    5    [0.4, -0.32, -0.13]        2          3

How do I convert to long form, e.g.:

       subject  trial_num  sample  sample_num
    0        1          1    0.57           0
    1        1          1   -0.83           1
    2        1          1    1.44           2
    3        1          2   -0.01           0
    4        1          2    1.13           1
    5        1          2    0.36           2
    6        1          3    1.18           0
    # etc.

The index is not important, it's OK to set existing columns as the index and the final ordering isn't important.

A bit longer than I expected:

    >>> df
                    samples  subject  trial_num
    0  [-0.07, -2.9, -2.44]        1          1
    1   [-1.52, -0.35, 0.1]        1          2
    2  [-0.17, 0.57, -0.65]        1          3
    3  [-0.82, -1.06, 0.47]        2          1
    4   [0.79, 1.35, -0.09]        2          2
    5   [1.17, 1.14, -1.79]        2          3
    >>>
    >>> s = df.apply(lambda x: pd.Series(x['samples']),axis=1).stack().reset_index(level=1, drop=True)
    >>> s.name = 'sample'
    >>>
    >>> df.drop('samples', axis=1).join(s)
       subject  trial_num  sample
    0        1          1   -0.07
    0        1          1   -2.90
    0        1          1   -2.44
    1        1          2   -1.52
    1        1          2   -0.35
    1        1          2    0.10
    2        1          3   -0.17
    2        1          3    0.57
    2        1          3   -0.65
    3        2          1   -0.82
    3        2          1   -1.06
    3        2          1    0.47
    4        2          2    0.79
    4        2          2    1.35
    4        2          2   -0.09
    5        2          3    1.17
    5        2          3    1.14
    5        2          3   -1.79

If you want sequential index, you can apply reset_index(drop=True) to the result.

update :

    >>> res = df.set_index(['subject', 'trial_num'])['samples'].apply(pd.Series).stack()
    >>> res = res.reset_index()
    >>> res.columns = ['subject','trial_num','sample_num','sample']
    >>> res
        subject  trial_num  sample_num  sample
    0         1          1           0    1.89
    1         1          1           1   -2.92
    2         1          1           2    0.34
    3         1          2           0    0.85
    4         1          2           1    0.24
    5         1          2           2    0.72
    6         1          3           0   -0.96
    7         1          3           1   -2.72
    8         1          3           2   -0.11
    9         2          1           0   -1.33
    10        2          1           1    3.13
    11        2          1           2   -0.65
    12        2          2           0    0.10
    13        2          2           1    0.65
    14        2          2           2    0.15
    15        2          3           0    0.64
    16        2          3           1   -0.10
    17        2          3           2   -0.76

From: stackoverflow.com/q/27263805