Pandas dividem a coluna de listas em várias colunas


136

Eu tenho um DataFrame de pandas com uma coluna:

import pandas as pd

df = pd.DataFrame(
    data={
        "teams": [
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
        ]
    }
)

print(df)

Resultado:

       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

Como pode dividir esta coluna de listas em 2 colunas?

Respostas:


243

Você pode usar o DataFrameconstrutor listscriado por to_list:

import pandas as pd

d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

df2[['team1','team2']] = pd.DataFrame(df2.teams.tolist(), index= df2.index)
print (df2)
       teams team1 team2
0  [SF, NYG]    SF   NYG
1  [SF, NYG]    SF   NYG
2  [SF, NYG]    SF   NYG
3  [SF, NYG]    SF   NYG
4  [SF, NYG]    SF   NYG
5  [SF, NYG]    SF   NYG
6  [SF, NYG]    SF   NYG

E para novos DataFrame:

df3 = pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
print (df3)
  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

A solução com apply(pd.Series)é muito lenta:

#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [121]: %timeit df2['teams'].apply(pd.Series)
1.79 s ± 52.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [122]: %timeit pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
1.63 ms ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

4
Advertência secundária, se você o estiver usando no quadro de dados existente, redefina o índice, caso contrário ele não será atribuído corretamente.
user1700890

1
@ user1700890 - sim, ou especificar índice na trama de dados de construtordf2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
Jezrael

1
@ Catbuilts - sim, se existir solução vetorizada, é melhor evitá-la.
Jezrael

1
@ Catbuilts - sim, obviamente. Vetorizado geralmente significa que não há loops, portanto não se aplica, não se aplica, não há compreensão de lista. Mas isso depende exatamente do que precisa. Talvez também ajude isso
jezrael 20/11

2
@Catbuilts Na verdade, apply()pode ser mais lento, mas é o método principal quando a string e os valores de entrada não são iguais nas linhas da série original!
CheTesta

52

Solução muito mais simples:

pd.DataFrame(df2["teams"].to_list(), columns=['team1', 'team2'])

Rendimentos,

  team1 team2
-------------
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
7    SF   NYG

Se você quiser dividir uma coluna de cadeias delimitadas em vez de listas, faça o mesmo:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])

5
e se cada lista tiver um número desigual de elementos?
ikel 03/11/19

Se você deseja dividir uma coluna de seqüências delimitadas em vez de listas, da mesma forma: df["teams"].str.split('<delim>', expand=True) já retorna um DataFrame, portanto, provavelmente seria mais simples renomear as colunas.
AMC

26

Esta solução preserva o índice do df2DataFrame, diferente de qualquer solução que use tolist():

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']

Aqui está o resultado:

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

2
Também um dos mais lentos que applyvocê pode fazer em pandas. Você deve evitar esse método e usar a resposta aceita. Nos tempos de resposta superior, este método é de aproximadamente 1400 xmais lento @rajan
Erfan

2
@ Erfan Sim, mas às vezes o usuário não se importa se uma operação leva 1s ou 1ms e, em vez disso, se preocupa mais em escrever o código mais simples e legível! Reconheço que a legibilidade / simplicidade é subjetiva, mas meu argumento é simplesmente que a velocidade não é uma prioridade para todos os usuários o tempo todo.
Kevin Markham

1
Além disso, descobri que o applymétodo funciona de maneira mais confiável para expandir matrizes grandes (mais de 1.000 itens) em conjuntos de dados grandes. O tolist()método interrompeu meu processo quando o conjunto de dados excedeu 500k linhas.
moritz

2
Esta é uma ótima solução, pois funciona bem com listas de tamanhos diferentes.
dasilvadaniel 16/02

@ KevinMarkham eles se preocupam mais em escrever o código mais simples e legível É pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])realmente muito mais complicado?
AMC

15

Parece haver uma maneira sintaticamente mais simples e, portanto, mais fácil de lembrar, em oposição às soluções propostas. Estou assumindo que a coluna é chamada 'meta' em um dataframe df:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())

1
Ocorreu um erro, mas resolvi removendo o arquivo str.split(). Isso foi muito mais simples e tem a vantagem se você não souber o número de itens em sua lista.
otteheng 11/01/19

Parece haver uma maneira sintaticamente mais simples e, portanto, mais fácil de lembrar, em oposição às soluções propostas. Realmente? Porque isso é praticamente idêntico à resposta principal que foi publicada anos antes. A única diferença é a parte que não está relacionada a essa pergunta específica.
AMC

Funciona para mim !!
EduardoUstarez 27/06

3

Com base nas respostas anteriores, aqui está outra solução que retorna o mesmo resultado que df2.teams.apply (pd.Series) com um tempo de execução muito mais rápido:

pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

Horários:

In [1]:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [2]: %timeit df2['teams'].apply(pd.Series)

8.27 s ± 2.73 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

35.4 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

3

As soluções acima não funcionaram para mim, pois tenho nanobservações no meu dataframe. No meu caso, df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)produz:

object of type 'float' has no len()

Eu resolvo isso usando a compreensão da lista. Aqui o exemplo replicável:

import pandas as pd
import numpy as np
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
            ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2.loc[2,'teams'] = np.nan
df2.loc[4,'teams'] = np.nan
df2

resultado:

        teams
0   [SF, NYG]
1   [SF, NYG]
2   NaN
3   [SF, NYG]
4   NaN
5   [SF, NYG]
6   [SF, NYG]

df2['team1']=np.nan
df2['team2']=np.nan

resolução com compreensão de lista:

for i in [0,1]:
    df2['team{}'.format(str(i+1))]=[k[i] if isinstance(k,list) else k for k in df2['teams']]

df2

rendimentos:

    teams   team1   team2
0   [SF, NYG]   SF  NYG
1   [SF, NYG]   SF  NYG
2   NaN        NaN  NaN
3   [SF, NYG]   SF  NYG
4   NaN        NaN  NaN
5   [SF, NYG]   SF  NYG
6   [SF, NYG]   SF  NYG

1

compreensão da lista

implementação simples com compreensão de lista (o meu favorito)

df = pd.DataFrame([pd.Series(x) for x in df.teams])
df.columns = ['team_{}'.format(x+1) for x in df.columns]

tempo na saída:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.71 ms

resultado:

team_1  team_2
0   SF  NYG
1   SF  NYG
2   SF  NYG
3   SF  NYG
4   SF  NYG
5   SF  NYG
6   SF  NYG

Esse tipo de manipula listas de diferentes comprimentos - o que é uma melhoria em relação a muitas outras respostas, mas resulta em itens que não estão em suas próprias colunas.
Isaac

0

Aqui está outra solução usando df.transforme df.set_index:

>>> (df['teams']
       .transform([lambda x:x[0], lambda x:x[1]])
       .set_axis(['team1','team2'],
                  axis=1,
                  inplace=False)
    )

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.