una nueva insertar filas fila datos data columnas columna agregar python pandas dataframe chained-assignment

insertar - Agregar una nueva columna al DataFrame existente en los pandas de Python



nueva columna pandas (21)

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?


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.


  1. Primero crea una lista de python que contenga datos relevantes.
  2. Use esto: df [''e''] = list_of_e

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])


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


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


Estaba buscando una forma general de agregar una columna de numpy.nan s a un marco de datos sin obtener el tonto SettingWithCopyWarning .

De lo siguiente:

  • las respuestas aqui
  • esta pregunta acerca de pasar una variable como un argumento de palabra clave
  • este método para generar una matriz numpy de NaNs en línea

Se me ocurrió esto:

col = ''column_name'' df = df.assign(**{col:numpy.full(len(df), numpy.nan)})


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())


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)


Maneras más fáciles: -

datos [''new_col''] = list_of_values

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


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

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


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 .


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í.


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.


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)


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.


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


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)


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)


SettingWithCopyWarning el temido SettingWithCopyWarning , y no se solucionó con la sintaxis de iloc. Mi DataFrame fue creado por read_sql desde una fuente ODBC. Usando una sugerencia de lowtech arriba, lo siguiente funcionó para mí:

df.insert(len(df.columns), ''e'', pd.Series(np.random.randn(sLength), index=df.index))

Esto funcionó bien para insertar la columna al final. No sé si es la más eficiente, pero no me gustan los mensajes de advertencia. Creo que hay una solución mejor, pero no puedo encontrarla, y creo que depende de algún aspecto del índice.
Nota Que esto solo funciona una vez y dará un mensaje de error si se intenta sobrescribir una columna existente.
Nota Como arriba y desde 0.16.0, asignar es la mejor solución. Consulte la documentación http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.assign.html#pandas.DataFrame.assign Funciona bien para el tipo de flujo de datos donde no sobrescribe sus valores intermedios.


Infalible:

df.loc[:, ''NewCol''] = ''New_Val''

Ejemplo:

df = pd.DataFrame(data=np.random.randn(20, 4), columns=[''A'', ''B'', ''C'', ''D'']) df A B C D 0 -0.761269 0.477348 1.170614 0.752714 1 1.217250 -0.930860 -0.769324 -0.408642 2 -0.619679 -1.227659 -0.259135 1.700294 3 -0.147354 0.778707 0.479145 2.284143 4 -0.529529 0.000571 0.913779 1.395894 5 2.592400 0.637253 1.441096 -0.631468 6 0.757178 0.240012 -0.553820 1.177202 7 -0.986128 -1.313843 0.788589 -0.707836 8 0.606985 -2.232903 -1.358107 -2.855494 9 -0.692013 0.671866 1.179466 -1.180351 10 -1.093707 -0.530600 0.182926 -1.296494 11 -0.143273 -0.503199 -1.328728 0.610552 12 -0.923110 -1.365890 -1.366202 -1.185999 13 -2.026832 0.273593 -0.440426 -0.627423 14 -0.054503 -0.788866 -0.228088 -0.404783 15 0.955298 -1.430019 1.434071 -0.088215 16 -0.227946 0.047462 0.373573 -0.111675 17 1.627912 0.043611 1.743403 -0.012714 18 0.693458 0.144327 0.329500 -0.655045 19 0.104425 0.037412 0.450598 -0.923387 df.drop([3, 5, 8, 10, 18], inplace=True) df A B C D 0 -0.761269 0.477348 1.170614 0.752714 1 1.217250 -0.930860 -0.769324 -0.408642 2 -0.619679 -1.227659 -0.259135 1.700294 4 -0.529529 0.000571 0.913779 1.395894 6 0.757178 0.240012 -0.553820 1.177202 7 -0.986128 -1.313843 0.788589 -0.707836 9 -0.692013 0.671866 1.179466 -1.180351 11 -0.143273 -0.503199 -1.328728 0.610552 12 -0.923110 -1.365890 -1.366202 -1.185999 13 -2.026832 0.273593 -0.440426 -0.627423 14 -0.054503 -0.788866 -0.228088 -0.404783 15 0.955298 -1.430019 1.434071 -0.088215 16 -0.227946 0.047462 0.373573 -0.111675 17 1.627912 0.043611 1.743403 -0.012714 19 0.104425 0.037412 0.450598 -0.923387 df.loc[:, ''NewCol''] = 0 df A B C D NewCol 0 -0.761269 0.477348 1.170614 0.752714 0 1 1.217250 -0.930860 -0.769324 -0.408642 0 2 -0.619679 -1.227659 -0.259135 1.700294 0 4 -0.529529 0.000571 0.913779 1.395894 0 6 0.757178 0.240012 -0.553820 1.177202 0 7 -0.986128 -1.313843 0.788589 -0.707836 0 9 -0.692013 0.671866 1.179466 -1.180351 0 11 -0.143273 -0.503199 -1.328728 0.610552 0 12 -0.923110 -1.365890 -1.366202 -1.185999 0 13 -2.026832 0.273593 -0.440426 -0.627423 0 14 -0.054503 -0.788866 -0.228088 -0.404783 0 15 0.955298 -1.430019 1.434071 -0.088215 0 16 -0.227946 0.047462 0.373573 -0.111675 0 17 1.627912 0.043611 1.743403 -0.012714 0 19 0.104425 0.037412 0.450598 -0.923387 0