Cómo agregar una nueva columna a un DataFrame existente

Resuelto tomasz74 asked hace 12 años • 33 respuestas

Tengo el siguiente DataFrame indexado con columnas y filas con nombre, números no continuos:

          a         b         c         d
2  0.671399  0.101208 -0.181532  0.241273
3  0.446172 -0.243316  0.051767  1.577318
5  0.614758  0.075793 -0.451460 -0.012493

Me gustaría agregar una nueva columna 'e'al marco de datos existente y no quiero cambiar nada en el marco de datos (es decir, la nueva columna siempre tiene la misma longitud que el marco de datos).

0   -0.335485
1   -1.166658
2   -0.385571
dtype: float64

¿ Cómo puedo agregar una columna eal ejemplo anterior?

tomasz74 avatar Sep 24 '12 02:09 tomasz74
Aceptado

Editar 2017

Como se indica en los comentarios y por @Alexander, actualmente el mejor método para agregar los valores de una Serie como una nueva columna de un DataFrame podría ser usar assign:

df1 = df1.assign(e=pd.Series(np.random.randn(sLength)).values)

Editar 2015
Algunos informaron haber recibido SettingWithCopyWarningeste código.
Sin embargo, el código aún se ejecuta perfectamente con la versión actual de pandas 0.16.1.

>>> sLength = len(df1['a'])
>>> df1
          a         b         c         d
6 -0.269221 -0.026476  0.997517  1.294385
8  0.917438  0.847941  0.034235 -0.448948

>>> df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e
6 -0.269221 -0.026476  0.997517  1.294385  1.757167
8  0.917438  0.847941  0.034235 -0.448948  2.228131

>>> pd.version.short_version
'0.16.1'

El SettingWithCopyWarningobjetivo es informar de una cesión posiblemente no válida en una copia del Dataframe. No necesariamente dice que lo hiciste mal (puede generar falsos positivos), pero desde 0.13.0 te permite saber que existen métodos más adecuados para el mismo propósito. Luego, si recibe la advertencia, simplemente siga sus consejos: intente usar .loc[row_index,col_indexer] = value en su lugar

>>> df1.loc[:,'f'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e         f
6 -0.269221 -0.026476  0.997517  1.294385  1.757167 -0.050927
8  0.917438  0.847941  0.034235 -0.448948  2.228131  0.006109
>>> 

De hecho, este es actualmente el método más eficiente como se describe en los documentos de pandas.


Respuesta original:

Utilice los índices df1 originales para crear la serie:

df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
joaquin avatar Sep 23 '2012 19:09 joaquin

Esta es la forma sencilla de agregar una nueva columna:df['e'] = e

Kathirmani Sukumar avatar Dec 12 '2012 16:12 Kathirmani Sukumar

Me gustaría agregar una nueva columna, 'e', ​​al marco de datos existente y no cambiar nada en el marco de datos. (La serie siempre tuvo la misma longitud que un marco de datos).

Supongo que los valores del índice ecoinciden con los de df1.

La forma más sencilla de iniciar una nueva columna denominada ey asignarle los valores de su serie e:

df['e'] = e.values

asignar (Pandas 0.16.0+)

A partir de Pandas 0.16.0, también puedes usar assign, que asigna nuevas columnas a un DataFrame y devuelve un nuevo objeto (una copia) con todas las columnas originales además de las nuevas.

df1 = df1.assign(e=e.values)

Según este ejemplo (que también incluye el código fuente de la assignfunción), también puedes incluir más de una columna:

df = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
>>> df.assign(mean_a=df.a.mean(), mean_b=df.b.mean())
   a  b  mean_a  mean_b
0  1  3     1.5     3.5
1  2  4     1.5     3.5

En contexto con su ejemplo:

np.random.seed(0)
df1 = pd.DataFrame(np.random.randn(10, 4), columns=['a', 'b', 'c', 'd'])
mask = df1.applymap(lambda x: x <-0.7)
df1 = df1[-mask.any(axis=1)]
sLength = len(df1['a'])
e = pd.Series(np.random.randn(sLength))

>>> df1
          a         b         c         d
0  1.764052  0.400157  0.978738  2.240893
2 -0.103219  0.410599  0.144044  1.454274
3  0.761038  0.121675  0.443863  0.333674
7  1.532779  1.469359  0.154947  0.378163
9  1.230291  1.202380 -0.387327 -0.302303

>>> e
0   -1.048553
1   -1.420018
2   -1.706270
3    1.950775
4   -0.509652
dtype: float64

df1 = df1.assign(e=e.values)

>>> df1
          a         b         c         d         e
0  1.764052  0.400157  0.978738  2.240893 -1.048553
2 -0.103219  0.410599  0.144044  1.454274 -1.420018
3  0.761038  0.121675  0.443863  0.333674 -1.706270
7  1.532779  1.469359  0.154947  0.378163  1.950775
9  1.230291  1.202380 -0.387327 -0.302303 -0.509652

La descripción de esta nueva característica cuando se introdujo por primera vez se puede encontrar aquí .

Alexander avatar Feb 14 '2016 00:02 Alexander

Asignación de columnas súper simple

Un marco de datos de pandas se implementa como un dictado ordenado de columnas.

Esto significa que __getitem__ []no solo se puede usar para obtener una determinada columna, sino que __setitem__ [] =también se puede usar para asignar una nueva columna.

Por ejemplo, a este marco de datos se le puede agregar una columna simplemente usando el []descriptor de acceso

    size      name color
0    big      rose   red
1  small    violet  blue
2  small     tulip   red
3  small  harebell  blue

df['protected'] = ['no', 'no', 'no', 'yes']

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

Tenga en cuenta que esto funciona incluso si el índice del marco de datos está desactivado.

df.index = [3,2,1,0]
df['protected'] = ['no', 'no', 'no', 'yes']
    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

[]= es el camino a seguir, ¡pero cuidado!

Sin embargo, si tiene un pd.Seriese intenta asignarlo a un marco de datos donde los índices están desactivados, tendrá problemas. Ver ejemplo:

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'])
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

Esto se debe a que a pd.Seriesde forma predeterminada tiene un índice enumerado de 0 a n. [] =Y el método pandas intenta ser "inteligente"

Lo que realmente está pasando.

Cuando utiliza el [] =método, pandas realiza silenciosamente una unión externa o una fusión externa utilizando el índice del marco de datos de la izquierda y el índice de la serie de la derecha.df['column'] = series

Nota al margen

Esto rápidamente causa disonancia cognitiva, ya que el []=método intenta hacer muchas cosas diferentes dependiendo de la entrada, y el resultado no se puede predecir a menos que sepas cómo funciona Pandas. Por lo tanto, desaconsejaría las []=bases de código, pero cuando se exploran datos en un cuaderno, está bien.

Dando la vuelta al problema

Si tiene un código pd.Seriesy desea que se le asigne de arriba a abajo, o si está codificando código productivo y no está seguro del orden del índice, vale la pena protegerlo para este tipo de problema.

Podrías abatir el pd.Seriesa np.ndarrayo a list, esto funcionará.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes']).values

o

df['protected'] = list(pd.Series(['no', 'no', 'no', 'yes']))

Pero esto no es muy explícito.

Es posible que aparezca algún programador y diga: "Oye, esto parece redundante, lo optimizaré".

manera explícita

Establecer el índice de pd.Seriescomo índice de dfes explícito.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)

O, de manera más realista, probablemente ya tengas uno pd.Seriesdisponible.

protected_series = pd.Series(['no', 'no', 'no', 'yes'])
protected_series.index = df.index

3     no
2     no
1     no
0    yes

Ahora se puede asignar

df['protected'] = protected_series

    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

forma alternativa condf.reset_index()

Dado que la disonancia del índice es el problema, si cree que el índice del marco de datos no debería dictar las cosas, simplemente puede eliminar el índice, esto debería ser más rápido, pero no es muy limpio, ya que su función ahora probablemente haga dos cosas.

df.reset_index(drop=True)
protected_series.reset_index(drop=True)
df['protected'] = protected_series

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

Nota sobredf.assign

Aunque df.assignhaga más explícito lo que está haciendo, en realidad tiene los mismos problemas que el anterior.[]=

df.assign(protected=pd.Series(['no', 'no', 'no', 'yes']))
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

Sólo ten cuidado con df.assignque tu columna no sea llamada self. Causará errores. Esto hace que df.assign huela mal , ya que existen este tipo de artefactos en la función.

df.assign(self=pd.Series(['no', 'no', 'no', 'yes'])
TypeError: assign() got multiple values for keyword argument 'self'

Quizás digas: "Bueno, entonces no lo consumiré self". Pero quién sabe cómo cambiará esta función en el futuro para sustentar nuevos argumentos. Quizás el nombre de su columna sea un argumento en una nueva actualización de pandas, causando problemas con la actualización.

firelynx avatar Apr 03 '2017 08:04 firelynx