Если вы хотите избежать использования apply(pd.Series)
, потому что медленно , здесь есть другое решение - сначала преобразовать не списочные значения в один список элементов, а затем применить решение:
df['foo'] = [x if isinstance(x, list) else [x] for x in df['foo']]
from itertools import chain
df = pd.DataFrame({
'id' : df['id'].values.repeat(df['foo'].str.len()),
'foo' : list(chain.from_iterable(df['foo'].tolist()))
})
Или:
L = [x if isinstance(x, list) else [x] for x in df['foo']]
from itertools import chain
df = pd.DataFrame({
'id' : df['id'].values.repeat([len(x) for x in L]),
'foo' : list(chain.from_iterable(L))
})
print (df)
id foo
0 301 a
1 301 b
2 301 c
3 302 e
4 302 f
5 302 33
6 302 Z
7 303 42
Если небольшие данные или производительность не важны - решение с pop
для столбца извлечения foo
:
s = df.pop('foo').apply(pd.Series).stack().reset_index(level=1, drop=True).rename('foo')
df = df.join(s).reset_index(drop=True)
Или решение с drop
:
s = df['foo'].apply(pd.Series).stack().reset_index(level=1, drop=True).rename('foo')
df = df.drop('foo', axis=1).join(s).reset_index(drop=True)
print (df)
id foo
0 301 a
1 301 b
2 301 c
3 302 e
4 302 f
5 302 33
6 302 Z
7 303 42
df=pd.DataFrame(data=[[301,301,302,303],[['a'],['b','c'],['e','f',33,'Z'],42]],index=['id','foo']).T
df = pd.concat([df] * 1000, ignore_index=True)
def f(df):
s = df['foo'].apply(pd.Series).stack().reset_index(level=1, drop=True).rename('foo')
return df.drop('foo', axis=1).join(s).reset_index(drop=True)
In [241]: %timeit (f(df))
814 ms ± 11.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [242]: %%timeit
...: L = [x if isinstance(x, list) else [x] for x in df['foo']]
...:
...: from itertools import chain
...:
...: pd.DataFrame({
...: 'id' : df['id'].values.repeat([len(x) for x in L]),
...: 'foo' : list(chain.from_iterable(L))
...:
...: })
...:
2.6 ms ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)