TL; versión DR:
Para el caso simple de:
- Tengo una columna de texto con un delimitador y quiero dos columnas.
La solución más simple es:
df['A'], df['B'] = df['AB'].str.split(' ', 1).str
O puede crear crear un DataFrame con una columna para cada entrada de la división automáticamente con:
df['AB'].str.split(' ', 1, expand=True)
Debe usar expand=True
si sus cadenas tienen un número no uniforme de divisiones y desea None
reemplazar los valores faltantes.
Observe cómo, en cualquier caso, el .tolist()
método no es necesario. Tampoco lo es zip()
.
En detalle:
La solución de Andy Hayden es excelente para demostrar el poder del str.extract()
método.
Pero para una división simple sobre un separador conocido (como, división por guiones o división por espacios en blanco), el .str.split()
método es suficiente 1 . Funciona en una columna (Serie) de cadenas y devuelve una columna (Serie) de listas:
>>> 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: Si no está seguro de cuáles son los dos primeros parámetros .str.split()
, recomiendo los documentos para la versión simple de Python del método .
Pero como vas desde:
- una columna que contiene listas de dos elementos
a:
- dos columnas, cada una con el elemento respectivo de las listas?
Bueno, tenemos que echar un vistazo más de cerca al .str
atributo de una columna.
Es un objeto mágico que se utiliza para recopilar métodos que tratan cada elemento de una columna como una cadena, y luego aplica el método respectivo en cada elemento de la manera más eficiente posible:
>>> 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
Pero también tiene una interfaz de "indexación" para obtener cada elemento de una cadena por su índice:
>>> df['AB'].str[0]
0 A
1 A
Name: AB, dtype: object
>>> df['AB'].str[1]
0 1
1 2
Name: AB, dtype: object
Por supuesto, a esta interfaz de indexación .str
realmente no le importa si cada elemento que está indexando es en realidad una cadena, siempre que pueda indexarse, por lo tanto:
>>> 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
Entonces, es una simple cuestión de aprovechar el desempaquetado de tuplas de Python de iterables para hacer
>>> 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
Por supuesto, obtener un DataFrame al dividir una columna de cadenas es tan útil que el .str.split()
método puede hacerlo por usted con el expand=True
parámetro:
>>> df['AB'].str.split('-', 1, expand=True)
0 1
0 A1 B1
1 A2 B2
Entonces, otra forma de lograr lo que queríamos es hacer:
>>> 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
versión, aunque más larga, tiene una clara ventaja sobre el método de desempaquetado de tuplas. El desempaquetado de tuplas no funciona bien con divisiones de diferentes longitudes:
>>> 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
>>>
Pero lo expand=True
maneja bien colocando None
en las columnas para las que no hay suficientes "divisiones":
>>> 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()