seleccionar - separar por caracter python
¿Cómo dividir una columna en dos columnas? (8)
Tengo un marco de datos con una columna y me gustaría dividirlo en dos columnas, con un encabezado de columna como '' fips''
y la otra ''row''
Mi marco de datos df
ve así:
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
No sé cómo usar df.row.str[:]
para lograr mi objetivo de dividir la celda de fila. Puedo usar df[''fips''] = hello
para agregar una nueva columna y poblarla con hello
. ¿Algunas ideas?
fips row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
TL; DR versión:
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)
Observe cómo, en cualquier caso, el método .tolist()
no es necesario. Tampoco lo es zip()
.
En detalle:
La solución de Andy Hayden es excelente para demostrar el poder del método str.extract()
.
Pero para una simple división sobre un separador conocido (como, dividir por guiones, o dividir por espacios en blanco), el método .str.split()
es suficiente 1 . Opera 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 primeros dos parámetros de .str.split()
, recomiendo los documentos para la versión de Python normal del método .
¿Pero cómo vas desde:
- una columna que contiene listas de dos elementos
a:
- dos columnas, cada una conteniendo el elemento respectivo de las listas?
Bueno, tenemos que echar un vistazo más de cerca al atributo .str
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, esta interfaz de indexación de .str
realidad no le importa si cada elemento que indexa es en realidad una cadena, siempre que pueda indexarse, por lo que:
>>> 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 la tupla de Python desempaquetar 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 para dividir una columna de cadenas es tan útil que el método .str.split()
puede hacerlo por usted con el parámetro expand=True
:
>>> 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
Prefiero exportar la serie de pandas correspondiente (es decir, las columnas que necesito), usando la función de aplicar para dividir el contenido de la columna en series múltiples y luego unir las columnas generadas al DataFrame existente. Por supuesto, la columna fuente debe ser eliminada.
p.ej
col1 = df["<col_name>"].apply(<function>)
col2 = ...
df = df.join(col1.to_frame(name="<name1>"))
df = df.join(col2.toframe(name="<name2>"))
df = df.drop(["<col_name>"], axis=1)
Para dividir dos palabras, la función de cadenas debería ser algo así:
lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element
Puede str.extract() las diferentes partes bastante bien usando un patrón de expresiones regulares:
In [11]: df.row.str.extract(''(?P<fips>/d{5})((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))'')
Out[11]:
fips 1 state county state_code
0 00000 UNITED STATES UNITED STATES NaN NaN
1 01000 ALABAMA ALABAMA NaN NaN
2 01001 Autauga County, AL NaN Autauga County AL
3 01003 Baldwin County, AL NaN Baldwin County AL
4 01005 Barbour County, AL NaN Barbour County AL
[5 rows x 5 columns]
Para explicar la expresión regular algo larga:
(?P<fips>/d{5})
- Coincide con los cinco dígitos (
/d
) y los denomina"fips"
.
La siguiente parte:
((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
Hace cualquiera ( |
) una de dos cosas:
(?P<state>[A-Z ]*$)
- Coincide con cualquier número (
*
) de mayúsculas o espacios ([AZ ]
) y nombra este"state"
antes del final de la cadena ($
),
o
(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
- coincide con cualquier otra cosa (
.*
) luego - una coma y un espacio entonces
- coincide con los dos dígitos
state_code
antes del final de la cadena ($
).
En el ejemplo:
Tenga en cuenta que las dos primeras filas tocan el "estado" (dejando NaN en las columnas county y state_code), mientras que las últimas tres llegan al condado, state_code (dejando NaN en la columna de estado).
Puede haber una mejor manera, pero este es un enfoque:
In [34]: import pandas as pd
In [35]: df
Out[35]:
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
In [36]: df = pd.DataFrame(df.row.str.split('' '',1).tolist(),
columns = [''flips'',''row''])
In [37]: df
Out[37]:
flips row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
Puede usar .str.split() por espacio en blanco (separador predeterminado) y parámetro expand=True
para DataFrame
con asignar a columnas nuevas:
df = pd.DataFrame({''row'': [''00000 UNITED STATES'', ''01000 ALABAMA'',
''01001 Autauga County, AL'', ''01003 Baldwin County, AL'',
''01005 Barbour County, AL'']})
print (df)
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
df[[''a'',''b'']] = df[''row''].str.split(n=1, expand=True)
print (df)
row a b
0 00000 UNITED STATES 00000 UNITED STATES
1 01000 ALABAMA 01000 ALABAMA
2 01001 Autauga County, AL 01001 Autauga County, AL
3 01003 Baldwin County, AL 01003 Baldwin County, AL
4 01005 Barbour County, AL 01005 Barbour County, AL
Modificación si es necesario eliminar la columna original con DataFrame.pop
df[[''a'',''b'']] = df.pop(''row'').str.split(n=1, expand=True)
print (df)
a b
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
¿Qué es lo mismo?
df[[''a'',''b'']] = df[''row''].str.split(n=1, expand=True)
df = df.drop(''row'', axis=1)
print (df)
a b
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
Si obtiene un error:
#remove n=1 for split by all whitespaces
df[[''a'',''b'']] = df[''row''].str.split(expand=True)
ValueError: las columnas deben tener la misma longitud que la clave
Puede verificar y devolver 4 columnas DataFrame
, no solo 2:
print (df[''row''].str.split(expand=True))
0 1 2 3
0 00000 UNITED STATES None
1 01000 ALABAMA None None
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
Luego, la solución DataFrame
nuevo DataFrame
al join
:
df = pd.DataFrame({''row'': [''00000 UNITED STATES'', ''01000 ALABAMA'',
''01001 Autauga County, AL'', ''01003 Baldwin County, AL'',
''01005 Barbour County, AL''],
''a'':range(5)})
print (df)
a row
0 0 00000 UNITED STATES
1 1 01000 ALABAMA
2 2 01001 Autauga County, AL
3 3 01003 Baldwin County, AL
4 4 01005 Barbour County, AL
df = df.join(df[''row''].str.split(expand=True))
print (df)
a row 0 1 2 3
0 0 00000 UNITED STATES 00000 UNITED STATES None
1 1 01000 ALABAMA 01000 ALABAMA None None
2 2 01001 Autauga County, AL 01001 Autauga County, AL
3 3 01003 Baldwin County, AL 01003 Baldwin County, AL
4 4 01005 Barbour County, AL 01005 Barbour County, AL
Con quitar columna original (si también hay otras columnas):
df = df.join(df.pop(''row'').str.split(expand=True))
print (df)
a 0 1 2 3
0 0 00000 UNITED STATES None
1 1 01000 ALABAMA None None
2 2 01001 Autauga County, AL
3 3 01003 Baldwin County, AL
4 4 01005 Barbour County, AL
Si desea dividir una cadena en más de dos columnas basadas en un delimitador, puede omitir el parámetro ''divisiones máximas''.
Puedes usar:
df[''column_name''].str.split(''/'', expand=True)
Esto creará automáticamente tantas columnas como la cantidad máxima de campos incluidos en cualquiera de sus cadenas iniciales.
Si no desea crear un nuevo marco de datos, o si su marco de datos tiene más columnas que las que desea dividir, podría:
df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]
df[[''fips'', ''row'']] = df[''row''].str.split('' '', n=1, expand=True)