¿Cómo agregar varias columnas al marco de datos de pandas en una asignación?

122

Soy nuevo en los pandas y estoy tratando de descubrir cómo agregar varias columnas a los pandas simultáneamente. Cualquier ayuda aquí será apreciada. Idealmente, me gustaría hacer esto en un paso en lugar de varios pasos repetidos ...

import pandas as pd

df = {'col_1': [0, 1, 2, 3],
        'col_2': [4, 5, 6, 7]}
df = pd.DataFrame(df)

df[[ 'column_new_1', 'column_new_2','column_new_3']] = [np.nan, 'dogs',3]  #thought this would work here...
aves corriendo
fuente
Debe indicar qué error obtuvo. Cuando pruebo esto en pandas 1.0 obtengoKeyError: "None of [Index(['column_new_1', 'column_new_2', 'column_new_3'], dtype='object')] are in the [columns]"
smci

Respuestas:

185

Hubiera esperado que tu sintaxis también funcionara. El problema surge porque cuando crea nuevas columnas con la sintaxis de lista de columnas ( df[[new1, new2]] = ...), pandas requiere que el lado derecho sea un DataFrame (tenga en cuenta que en realidad no importa si las columnas del DataFrame tienen los mismos nombres que las columnas estás creando).

Su sintaxis funciona bien para asignar valores escalares a columnas existentes , y los pandas también están felices de asignar valores escalares a una nueva columna usando la sintaxis de una sola columna ( df[new1] = ...). Entonces, la solución es convertir esto en varias asignaciones de una sola columna o crear un DataFrame adecuado para el lado derecho.

Aquí hay varios enfoques que se trabajarán:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'col_1': [0, 1, 2, 3],
    'col_2': [4, 5, 6, 7]
})

Entonces uno de los siguientes:

1) Tres asignaciones en una, utilizando el desempaquetado de listas:

df['column_new_1'], df['column_new_2'], df['column_new_3'] = [np.nan, 'dogs', 3]

2) DataFrameexpande convenientemente una sola fila para que coincida con el índice, por lo que puede hacer esto:

df[['column_new_1', 'column_new_2', 'column_new_3']] = pd.DataFrame([[np.nan, 'dogs', 3]], index=df.index)

3) Cree un marco de datos temporal con nuevas columnas, luego combínelo con el marco de datos original más tarde:

df = pd.concat(
    [
        df,
        pd.DataFrame(
            [[np.nan, 'dogs', 3]], 
            index=df.index, 
            columns=['column_new_1', 'column_new_2', 'column_new_3']
        )
    ], axis=1
)

4) Similar al anterior, pero usando en joinlugar de concat(puede ser menos eficiente):

df = df.join(pd.DataFrame(
    [[np.nan, 'dogs', 3]], 
    index=df.index, 
    columns=['column_new_1', 'column_new_2', 'column_new_3']
))

5) Usar un dictado es una forma más "natural" de crear el nuevo marco de datos que los dos anteriores, pero las nuevas columnas se ordenarán alfabéticamente (al menos antes de Python 3.6 o 3.7 ):

df = df.join(pd.DataFrame(
    {
        'column_new_1': np.nan,
        'column_new_2': 'dogs',
        'column_new_3': 3
    }, index=df.index
))

6) Úselo .assign()con argumentos de varias columnas.

Me gusta mucho esta variante en la respuesta de @ zero, pero al igual que la anterior, las nuevas columnas siempre se ordenarán alfabéticamente, al menos con las primeras versiones de Python:

df = df.assign(column_new_1=np.nan, column_new_2='dogs', column_new_3=3)

7) Esto es interesante (basado en https://stackoverflow.com/a/44951376/3830997 ), pero no sé cuándo valdría la pena:

new_cols = ['column_new_1', 'column_new_2', 'column_new_3']
new_vals = [np.nan, 'dogs', 3]
df = df.reindex(columns=df.columns.tolist() + new_cols)   # add empty cols
df[new_cols] = new_vals  # multi-column assignment works for existing cols

8) Al final, es difícil superar tres asignaciones distintas:

df['column_new_1'] = np.nan
df['column_new_2'] = 'dogs'
df['column_new_3'] = 3

Nota: muchas de estas opciones ya se han cubierto en otras respuestas: agregue varias columnas a DataFrame y configúrelas igual a una columna existente . ¿Es posible agregar varias columnas a la vez a un DataFrame de pandas? , Agregue múltiples columnas vacías a pandas DataFrame

Matthias Fripp
fuente
¿No .reindexalteraría el enfoque # 7 ( ) el índice del marco de datos? ¿Por qué alguien querría alterar innecesariamente el índice al agregar columnas a menos que sea un objetivo explícito ...
Acumenus
1
.reindex()se usa con el columnsargumento, por lo que solo cambia la columna "índice" (nombres). No altera el índice de la fila.
Matthias Fripp
para algunos de los enfoques, puede utilizar OrderedDict: por ejemplo,df.join(pd.DataFrame( OrderedDict([('column_new_2', 'dogs'),('column_new_1', np.nan),('column_new_3', 3)]), index=df.index ))
hashmuke
@hashmuke Eso tiene sentido para las primeras versiones de Python. Puede resultar especialmente atractivo para las personas que utilizan diccionarios para varias cosas en Pandas, por ejemplo, df = pd.DataFrame({'before': [1, 2, 3], 'after': [4, 5, 6]})vs.df = pd.DataFrame(OrderedDict([('before', [1, 2, 3]), ('after', [4, 5, 6])])
Matthias Fripp
2
En caso de que esté usando la opción con join, asegúrese de no tener duplicados en su índice (o use uno reset_indexprimero). Podría ahorrarle algunas horas de depuración.
Guido
40

Puede usar assigncon un dict de nombres y valores de columna.

In [1069]: df.assign(**{'col_new_1': np.nan, 'col2_new_2': 'dogs', 'col3_new_3': 3})
Out[1069]:
   col_1  col_2 col2_new_2  col3_new_3  col_new_1
0      0      4       dogs           3        NaN
1      1      5       dogs           3        NaN
2      2      6       dogs           3        NaN
3      3      7       dogs           3        NaN
Cero
fuente
¿Hay alguna forma de hacer lo mismo que mantenga un orden específico de las columnas?
user48956
1
Puede mantener un orden específico con versiones anteriores de Python llamando a asignar varias veces: df.assign(**{'col_new_1': np.nan}).assign(**{'col2_new_2': 'dogs'}).assign(**{'col3_new_3': 3})
skasch
9

Con el uso de concat :

In [128]: df
Out[128]: 
   col_1  col_2
0      0      4
1      1      5
2      2      6
3      3      7

In [129]: pd.concat([df, pd.DataFrame(columns = [ 'column_new_1', 'column_new_2','column_new_3'])])
Out[129]: 
   col_1  col_2 column_new_1 column_new_2 column_new_3
0    0.0    4.0          NaN          NaN          NaN
1    1.0    5.0          NaN          NaN          NaN
2    2.0    6.0          NaN          NaN          NaN
3    3.0    7.0          NaN          NaN          NaN

No estoy muy seguro de lo que querías hacer [np.nan, 'dogs',3]. ¿Quizás ahora establecerlos como valores predeterminados?

In [142]: df1 = pd.concat([df, pd.DataFrame(columns = [ 'column_new_1', 'column_new_2','column_new_3'])])
In [143]: df1[[ 'column_new_1', 'column_new_2','column_new_3']] = [np.nan, 'dogs', 3]

In [144]: df1
Out[144]: 
   col_1  col_2  column_new_1 column_new_2  column_new_3
0    0.0    4.0           NaN         dogs             3
1    1.0    5.0           NaN         dogs             3
2    2.0    6.0           NaN         dogs             3
3    3.0    7.0           NaN         dogs             3
Nehal J Wani
fuente
si hubiera una manera de hacer su segunda parte en un paso, sí, valores constantes en las columnas como ejemplo.
runningbirds
3

uso de la comprensión de listas, pd.DataFrameypd.concat

pd.concat(
    [
        df,
        pd.DataFrame(
            [[np.nan, 'dogs', 3] for _ in range(df.shape[0])],
            df.index, ['column_new_1', 'column_new_2','column_new_3']
        )
    ], axis=1)

ingrese la descripción de la imagen aquí

piRSquared
fuente
3

si agrego muchas columnas faltantes (a, b, c, ....) con el mismo valor, aquí 0, hice esto:

    new_cols = ["a", "b", "c" ] 
    df[new_cols] = pd.DataFrame([[0] * len(new_cols)], index=df.index)

Se basa en la segunda variante de la respuesta aceptada.

A. Rabus
fuente
0

Solo quiero señalar esa opción2 en la respuesta de @Matthias Fripp

(2) No necesariamente esperaría que DataFrame funcione de esta manera, pero lo hace

df [['column_new_1', 'column_new_2', 'column_new_3']] = pd.DataFrame ([[np.nan, 'perros', 3]], index = df.index)

ya está documentado en la propia documentación de los pandas http://pandas.pydata.org/pandas-docs/stable/indexing.html#basics

Puede pasar una lista de columnas a [] para seleccionar columnas en ese orden. Si una columna no está contenida en el DataFrame, se generará una excepción. También se pueden configurar varias columnas de esta manera. Puede encontrar esto útil para aplicar una transformación ( en el lugar ) a un subconjunto de las columnas.

media luna
fuente
Creo que esto es bastante estándar para la asignación de varias columnas. La parte que me sorprendió fue que pd.DataFrame([[np.nan, 'dogs', 3]], index=df.index)replica la fila que se le da para crear un marco de datos completo de la misma longitud que el índice.
Matthias Fripp
0

Si solo desea agregar nuevas columnas vacías, reindex hará el trabajo

df
   col_1  col_2
0      0      4
1      1      5
2      2      6
3      3      7

df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)
   col_1  col_2  column_new_1  column_new_2  column_new_3
0      0      4           NaN           NaN           NaN
1      1      5           NaN           NaN           NaN
2      2      6           NaN           NaN           NaN
3      3      7           NaN           NaN           NaN

ejemplo de código completo

import numpy as np
import pandas as pd

df = {'col_1': [0, 1, 2, 3],
        'col_2': [4, 5, 6, 7]}
df = pd.DataFrame(df)
print('df',df, sep='\n')
print()
df=df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)
print('''df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)''',df, sep='\n')

de lo contrario, vaya a ceros, responda con asignar

Markus Dutschke
fuente
0

No me siento cómodo usando "Index" y así sucesivamente ... podría aparecer como se muestra a continuación

df.columns
Index(['A123', 'B123'], dtype='object')

df=pd.concat([df,pd.DataFrame(columns=list('CDE'))])

df.rename(columns={
    'C':'C123',
    'D':'D123',
    'E':'E123'
},inplace=True)


df.columns
Index(['A123', 'B123', 'C123', 'D123', 'E123'], dtype='object')
Alex
fuente