Разделить элемент списка в кадре данных на несколько строк - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть df.

df=pd.DataFrame(data=[[301,301,302,303],[['a'],['b','c'],['e','f',33,'Z'],42],index=['id','foo']).T

Я хотел бы перейти на второй фрейм данных, только с скалярными значениями в foo.Если и только если исходные значения были списком, я хотел бы распределить его по нескольким новым строкам с дублированием других значений.

например, от:

 id     foo
0  301     [a]
1  301  [b, c]
2  302  [e, f,33,'Z']
3  303   42

до:

 id     foo
0  301     a
1  301     b
1  301     c 
2  302     e
2  302     f
2  302     33
2  302     Z
3  303     42

В Разделение значений набора из ячейки данных Pandas на несколько строк , я узнал, как сделать это для одного столбца, но как справиться со случаем, когда df имеет несколько столбцов, которые должныдублироваться как id?

Ответы [ 2 ]

1 голос
/ 24 апреля 2019

Если вы хотите избежать использования 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)
1 голос
/ 24 апреля 2019
df.set_index('id')['foo'].apply(pd.Series).stack().reset_index(name='foo').drop('level_1', axis=1)

Выход

    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

Сценарий с несколькими столбцами

    id test            foo
0  301    1            [a]
1  301    2         [b, c]
2  302    3  [e, f, 33, Z]
3  303    4             42

с использованием pd.set_index

df.set_index(['id','test'])['foo'].apply(pd.Series).stack().reset_index(name='foo').drop('level_2', axis=1)

Выход

    id  test foo
0  301     1   a
1  301     2   b
2  301     2   c
3  302     3   e
4  302     3   f
5  302     3  33
6  302     3   Z
7  303     4  42
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...