Cómo agregar una nueva columna a un DataFrame existente
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 e
al ejemplo anterior?
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 SettingWithCopyWarning
este 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 SettingWithCopyWarning
objetivo 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)
Esta es la forma sencilla de agregar una nueva columna:df['e'] = e
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 e
coinciden con los de df1
.
La forma más sencilla de iniciar una nueva columna denominada e
y 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 assign
funció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í .
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.Series
e 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.Series
de 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.Series
y 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.Series
a np.ndarray
o 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.Series
como índice de df
es explícito.
df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)
O, de manera más realista, probablemente ya tengas uno pd.Series
disponible.
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.assign
haga 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.assign
que 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.