chained-assignment filas - Agregar una nueva columna al DataFrame existente en los pandas de Python




insertar datos (18)

Una cosa a tener en cuenta, sin embargo, es que si lo haces

df1['e'] = Series(np.random.randn(sLength), index=df1.index)

esta será efectivamente una unión izquierda en el df1.index. Entonces, si desea tener un efecto de combinación externa , mi solución probablemente imperfecta es crear un marco de datos con valores de índice que cubran el universo de sus datos y luego usar el código de arriba. Por ejemplo,

data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)

Tengo el siguiente DataFrame indexado con columnas nombradas y filas con 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

Probé diferentes versiones de join , append , merge , pero no obtuve el resultado que quería, solo errores a lo sumo. ¿Cómo puedo agregar la columna e al ejemplo anterior?


Hacer esto directamente a través de NumPy será lo más eficiente:

df1['e'] = np.random.randn(sLength)

Tenga en cuenta que mi sugerencia original (muy antigua) era usar el map (que es mucho más lento):

df1['e'] = df1['a'].map(lambda x: np.random.random())

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


En aras de la integridad, otra solución más que utiliza el método DataFrame.eval() :

Datos:

In [44]: e
Out[44]:
0    1.225506
1   -1.033944
2   -0.498953
3   -0.373332
4    0.615030
5   -0.622436
dtype: float64

In [45]: df1
Out[45]:
          a         b         c         d
0 -0.634222 -0.103264  0.745069  0.801288
4  0.782387 -0.090279  0.757662 -0.602408
5 -0.117456  2.124496  1.057301  0.765466
7  0.767532  0.104304 -0.586850  1.051297
8 -0.103272  0.958334  1.163092  1.182315
9 -0.616254  0.296678 -0.112027  0.679112

Solución:

In [46]: df1.eval("e = @e.values", inplace=True)

In [47]: df1
Out[47]:
          a         b         c         d         e
0 -0.634222 -0.103264  0.745069  0.801288  1.225506
4  0.782387 -0.090279  0.757662 -0.602408 -1.033944
5 -0.117456  2.124496  1.057301  0.765466 -0.498953
7  0.767532  0.104304 -0.586850  1.051297 -0.373332
8 -0.103272  0.958334  1.163092  1.182315  0.615030
9 -0.616254  0.296678 -0.112027  0.679112 -0.622436

Para agregar una nueva columna, 'e', ​​al marco de datos existente

 df1.loc[:,'e'] = Series(np.random.randn(sLength))

Antes de asignar una nueva columna, si tiene datos indexados, necesita ordenar el índice. Al menos en mi caso tuve que:

data.set_index(['index_column'], inplace=True)
"if index is unsorted, assignment of a new column will fail"        
data.sort_index(inplace = True)
data.loc['index_value1', 'column_y'] = np.random.randn(data.loc['index_value1', 'column_x'].shape[0])

Si obtiene el SettingWithCopyWarning , una solución fácil es copiar el DataFrame al que está intentando agregar una columna.

df = df.copy()
df['col_name'] = values

Maneras más fáciles: -

datos ['new_col'] = list_of_values

data.loc [:, 'new_col'] = list_of_values


Lo siguiente es lo que hice ... Pero soy bastante nuevo con los pandas y realmente con Python en general, así que no tengo promesas.

df = pd.DataFrame([[1, 2], [3, 4], [5,6]], columns=list('AB'))

newCol = [3,5,7]
newName = 'C'

values = np.insert(df.values,df.shape[1],newCol,axis=1)
header = df.columns.values.tolist()
header.append(newName)

df = pd.DataFrame(values,columns=header)

Si desea establecer la nueva columna completa en un valor base inicial (por ejemplo, None ), puede hacer esto: df1['e'] = None

Esto en realidad asignaría el tipo de "objeto" a la celda. Entonces, más tarde, podrá colocar tipos de datos complejos, como la lista, en celdas individuales.


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 tiene la misma longitud que un marco de datos).

Supongo que los valores de índice en e coinciden con los de df1 .

La forma más fácil de iniciar una nueva columna llamada 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 puede usar assign , que asigna nuevas columnas a un marco de datos 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 función de assign ), también puede 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 here .


Asignación de columna super simple

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

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

Por ejemplo, este marco de datos puede tener una columna agregada 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 y trata de asignarlo a un marco de datos donde los índices están desactivados, se encontrará con 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 pd.Series de forma predeterminada tiene un índice enumerado de 0 a n. Y el método de pandas [] = trata de ser "inteligente"

Lo que realmente está sucediendo.

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

Nota al margen

Esto causa rápidamente disonancia cognitiva, ya que el método []= está tratando de hacer muchas cosas diferentes dependiendo de la entrada, y el resultado no se puede predecir a menos que sepa cómo funcionan los pandas. Por lo tanto, recomendaría contra []= en las bases de código, pero al explorar datos en un cuaderno, está bien.

Atendiendo el problema

Si tiene una pd.Series y desea que se asigne de arriba a abajo, o si está codificando un código productivo y no está seguro del orden del índice, vale la pena salvaguardar este tipo de problemas.

Usted podría pd.Series el pd.Series a un np.ndarray o una list , esto hará el truco.

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.

Algún programador puede venir y decir "Hey, esto parece redundante, solo optimizaré esto".

Forma explícita

Establecer el índice de pd.Series para que sea el índice de la df es explícito.

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

O más realista, es probable que ya tengas una serie de 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 puede ser asignado

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

df.reset_index() alternativa con df.reset_index()

Dado que la disonancia del índice es el problema, si siente que el índice del marco de datos no debe dictar las cosas, simplemente puede eliminar el índice, esto debería ser más rápido, pero no está muy limpio, ya que su función ahora probablemente hace 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 sobre df.assign

Si bien df.assign hace que sea 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

Solo df.assign cuidado con df.assign que su columna no se llama self . Causará errores. Esto hace que df.assign mal , ya que hay 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'

Puedes decir: "Bueno, entonces no me self ". Pero quién sabe cómo esta función cambia en el futuro para admitir nuevos argumentos. Tal vez el nombre de su columna sea un argumento en una nueva actualización de pandas, causando problemas con la actualización.


Si el marco de datos y el objeto Serie tienen el mismo índice , pandas.concat también funciona aquí:

import pandas as pd
df
#          a            b           c           d
#0  0.671399     0.101208   -0.181532    0.241273
#1  0.446172    -0.243316    0.051767    1.577318
#2  0.614758     0.075793   -0.451460   -0.012493

e = pd.Series([-0.335485, -1.166658, -0.385571])    
e
#0   -0.335485
#1   -1.166658
#2   -0.385571
#dtype: float64

# here we need to give the series object a name which converts to the new  column name 
# in the result
df = pd.concat([df, e.rename("e")], axis=1)
df

#          a            b           c           d           e
#0  0.671399     0.101208   -0.181532    0.241273   -0.335485
#1  0.446172    -0.243316    0.051767    1.577318   -1.166658
#2  0.614758     0.075793   -0.451460   -0.012493   -0.385571

En caso de que no tengan el mismo índice:

e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)

Utilice los índices df1 originales para crear la serie:

df1['e'] = Series(np.random.randn(sLength), index=df1.index)

Editar 2015
Algunos informaron que obtuvieron el SettingWithCopyWarning con este código.
Sin embargo, el código aún funciona a la perfección 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'] = p.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

>>> p.version.short_version
'0.16.1'

El SettingWithCopyWarning tiene SettingWithCopyWarning objetivo informar de una asignación posiblemente no válida en una copia del Dataframe. No necesariamente dice que lo hiciste mal (puede desencadenar falsos positivos), pero a partir de la versión 0.13.0 te hace saber que existen métodos más adecuados para el mismo propósito. Luego, si recibe la advertencia, simplemente siga su consejo: intente usar .loc [row_index, col_indexer] = value en su lugar

>>> df1.loc[:,'f'] = p.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 descrito en pandas docs.

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 estar utilizando assign :

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

Si la columna que está intentando agregar es una variable de serie, simplemente:

df["new_columns_name"]=series_variable_name #this will do it for you

Esto funciona bien incluso si está reemplazando una columna existente. Simplemente escriba la nueva_columns_name igual que la columna que desea reemplazar. Solo sobrescribirá los datos de la columna existente con los nuevos datos de la serie.


Permítanme agregar que, al igual que para , .loc no resolvió el SettingWithCopyWarning y tuve que recurrir a df.insert() . En mi caso, un falso positivo fue generado por la cadena "falso" de indexación dict['a']['e'] , donde 'e' es la nueva columna, y dict['a'] es un DataFrame proveniente del diccionario.

También tenga en cuenta que si sabe lo que está haciendo, puede cambiar la advertencia utilizando pd.options.mode.chained_assignment = None y luego usar una de las otras soluciones que se ofrecen aquí.


Parece que en las versiones recientes de Pandas el camino a seguir es usar assign :

df1 = df1.assign(e=np.random.randn(sLength))

No produce SettingWithCopyWarning .


SÍ, Python tiene un operador ternario, aquí está la sintaxis y un código de ejemplo para demostrar lo mismo :)

#[On true] if [expression] else[On false]
# if the expression evaluates to true then it will pass On true otherwise On false


a= input("Enter the First Number ")
b= input("Enter the Second Number ")

print("A is Bigger") if a>b else print("B is Bigger")




python pandas dataframe chained-assignment