TL; versione DR:
Per il semplice caso di:
- Ho una colonna di testo con un delimitatore e voglio due colonne
La soluzione più semplice è:
df['A'], df['B'] = df['AB'].str.split(' ', 1).str
Oppure puoi creare creare un DataFrame con una colonna per ogni voce della divisione automaticamente con:
df['AB'].str.split(' ', 1, expand=True)
È necessario utilizzare expand=True
se le stringhe hanno un numero non uniforme di divisioni e si desidera None
sostituire i valori mancanti.
Notare come, in entrambi i casi, il .tolist()
metodo non sia necessario. Nemmeno lo è zip()
.
In dettaglio:
La soluzione di Andy Hayden è eccellente nel dimostrare la potenza del str.extract()
metodo.
Ma per una semplice suddivisione su un separatore noto (come, divisione per trattini o divisione per spazi bianchi), il .str.split()
metodo è sufficiente 1 . Funziona su una colonna (Serie) di stringhe e restituisce una colonna (Serie) di elenchi:
>>> import pandas as pd
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2']})
>>> df
AB
0 A1-B1
1 A2-B2
>>> df['AB_split'] = df['AB'].str.split('-')
>>> df
AB AB_split
0 A1-B1 [A1, B1]
1 A2-B2 [A2, B2]
1: Se non sei sicuro di cosa .str.split()
facciano i primi due parametri , consiglio i documenti per la semplice versione del metodo Python .
Ma come vai da:
- una colonna contenente elenchi di due elementi
per:
- due colonne, ognuna contenente il rispettivo elemento delle liste?
Bene, dobbiamo dare un'occhiata più da vicino .str
all'attributo di una colonna.
È un oggetto magico che viene utilizzato per raccogliere metodi che trattano ogni elemento di una colonna come una stringa e quindi applicare il rispettivo metodo in ciascun elemento nel modo più efficiente possibile:
>>> upper_lower_df = pd.DataFrame({"U": ["A", "B", "C"]})
>>> upper_lower_df
U
0 A
1 B
2 C
>>> upper_lower_df["L"] = upper_lower_df["U"].str.lower()
>>> upper_lower_df
U L
0 A a
1 B b
2 C c
Ma ha anche un'interfaccia di "indicizzazione" per ottenere ogni elemento di una stringa dal suo indice:
>>> df['AB'].str[0]
0 A
1 A
Name: AB, dtype: object
>>> df['AB'].str[1]
0 1
1 2
Name: AB, dtype: object
Naturalmente, questa interfaccia di indicizzazione di .str
non importa davvero se ogni elemento che sta indicizzando è in realtà una stringa, purché possa essere indicizzata, quindi:
>>> df['AB'].str.split('-', 1).str[0]
0 A1
1 A2
Name: AB, dtype: object
>>> df['AB'].str.split('-', 1).str[1]
0 B1
1 B2
Name: AB, dtype: object
Quindi, si tratta semplicemente di trarre vantaggio dalla tupla di Python che consente di decomprimere iterable
>>> df['A'], df['B'] = df['AB'].str.split('-', 1).str
>>> df
AB AB_split A B
0 A1-B1 [A1, B1] A1 B1
1 A2-B2 [A2, B2] A2 B2
Naturalmente, ottenere un DataFrame dalla divisione di una colonna di stringhe è così utile che il .str.split()
metodo può farlo per te con il expand=True
parametro:
>>> df['AB'].str.split('-', 1, expand=True)
0 1
0 A1 B1
1 A2 B2
Quindi, un altro modo di realizzare ciò che volevamo è fare:
>>> df = df[['AB']]
>>> df
AB
0 A1-B1
1 A2-B2
>>> df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
AB A B
0 A1-B1 A1 B1
1 A2-B2 A2 B2
La expand=True
versione, sebbene più lunga, presenta un netto vantaggio rispetto al metodo di decompressione delle tuple. Il disimballaggio di tuple non si occupa bene di divisioni di diverse lunghezze:
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2', 'A3-B3-C3']})
>>> df
AB
0 A1-B1
1 A2-B2
2 A3-B3-C3
>>> df['A'], df['B'], df['C'] = df['AB'].str.split('-')
Traceback (most recent call last):
[...]
ValueError: Length of values does not match length of index
>>>
Ma lo expand=True
gestisce bene posizionandolo None
nelle colonne per le quali non ci sono abbastanza "divisioni":
>>> df.join(
... df['AB'].str.split('-', expand=True).rename(
... columns={0:'A', 1:'B', 2:'C'}
... )
... )
AB A B C
0 A1-B1 A1 B1 None
1 A2-B2 A2 B2 None
2 A3-B3-C3 A3 B3 C3
read_table()
oread_fwf()