I panda dividono la colonna degli elenchi in più colonne


135

Ho un DataFrame panda con una colonna:

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)

Produzione:

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

Come posso dividere questa colonna di elenchi in 2 colonne?

Risposte:


243

Puoi usare il DataFramecostruttore con listscreato da 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 per i nuovi 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

La soluzione con apply(pd.Series)è molto 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
Piccolo avvertimento, se lo stai usando su dataframe esistente, assicurati di reimpostare l'indice, altrimenti non verrà assegnato correttamente.
user1700890

1
@ user1700890 - sì, o specifica l'indice nel costruttore df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
DataFrame

1
@ Catbuilts - sì, se esiste la soluzione vettorializzare meglio evitarla.
jezrael

1
@ Catbuilts - sì, ovviamente. Vettorializzare significa generalmente nessun loop, quindi nessuna comprensione, no per, no list. Ma dipende esattamente da cosa serve. Forse aiuta anche questo
jezrael

2
@Catbuilts Indeed apply()potrebbe essere più lento ma è il metodo da utilizzare quando la stringa di input ei valori non sono uguali tra le righe della serie originale!
CheTesta

52

Soluzione molto più semplice:

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

I rendimenti,

  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 volessi dividere una colonna di stringhe delimitate piuttosto che di elenchi, potresti fare in modo simile:

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

6
cosa succede se ogni lista ha un numero dispari di elementi?
ikel

Se volessi dividere una colonna di stringhe delimitate piuttosto che di elenchi, potresti farlo in modo simile: df["teams"].str.split('<delim>', expand=True) restituisce già un DataFrame, quindi probabilmente sarebbe più semplice rinominare le colonne.
AMC

26

Questa soluzione preserva l'indice del df2DataFrame, a differenza di qualsiasi soluzione che utilizza tolist():

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

Ecco il risultato:

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

2
Anche uno dei più lenti applyche puoi fare con i panda. Dovresti evitare questo metodo e utilizzare la risposta accettata. Nei tempi della risposta migliore, questo metodo è approssimativamente 1400 xpiù lento @rajan
Erfan

2
@Erfan Sì, ma a volte all'utente non interessa se un'operazione richiede 1 o 1 ms, e invece gli interessa di più scrivere il codice più semplice e leggibile! Riconosco che la leggibilità / semplicità è soggettiva, ma il mio punto è semplicemente che la velocità non è una priorità per tutti gli utenti in ogni momento.
Kevin Markham

1
Inoltre, ho scoperto che il applymetodo funziona in modo più affidabile per espandere array di grandi dimensioni (oltre 1000 elementi) su set di dati di grandi dimensioni. Il tolist()metodo ha interrotto il mio processo quando il set di dati ha superato le 500.000 righe.
moritz

2
Questa è un'ottima soluzione perché funziona bene con elenchi di diverse dimensioni.
dasilvadaniel

@KevinMarkham a loro interessa di più scrivere il codice più semplice e leggibile È pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])davvero molto più complicato?
AMC

15

Sembra esserci un modo sintatticamente più semplice, e quindi più facile da ricordare, rispetto alle soluzioni proposte. Suppongo che la colonna si chiami 'meta' in un dataframe df:

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

1
Ho ricevuto un errore ma l'ho risolto rimuovendo il file str.split(). Questo era molto più semplice e ha il vantaggio se non conosci il numero di elementi nella tua lista.
otteheng

Sembra esserci un modo sintatticamente più semplice, e quindi più facile da ricordare, rispetto alle soluzioni proposte. Veramente? Perché questo è praticamente identico alla risposta principale che è stata pubblicata anni prima. L'unica differenza è la parte che non è correlata a questa domanda specifica.
AMC

Per me funziona !!
EduardoUstarez,

3

Sulla base delle risposte precedenti, ecco un'altra soluzione che restituisce lo stesso risultato di df2.teams.apply (pd.Series) con un tempo di esecuzione molto più veloce:

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

Tempi:

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

Le soluzioni di cui sopra non hanno funzionato per me poiché ho nanosservazioni nel mio file dataframe. Nel mio caso df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)produce:

object of type 'float' has no len()

Lo risolvo usando la comprensione delle liste. Ecco l'esempio replicabile:

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

produzione:

        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

risolvere con la comprensione della 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

rendimenti:

    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

comprensione delle liste

implementazione semplice con comprensione della lista (la mia preferita)

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

temporizzazione in uscita:

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

produzione:

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

Questo tipo di gestione di elenchi di lunghezze diverse, il che è un miglioramento rispetto a molte altre risposte, ma fa sì che gli elementi non siano nelle loro colonne.
Isaac

0

Ecco un'altra soluzione che utilizza 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.