Tengo el siguiente DataFrame indexado con columnas con nombre 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 como máximo. ¿Cómo puedo agregar la columna e
al ejemplo anterior?
23 respuestas
Use los índices df1 originales para crear la serie:
df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
Editar 2015
Algunos informaron haber obtenido el SettingWithCopyWarning
con este código.
Sin embargo, el código aún funciona 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
>>> p.version.short_version
'0.16.1'
El SettingWithCopyWarning
tiene como objetivo informar de una asignación posiblemente no válida en una copia del Marco de datos. No necesariamente dice que lo hizo mal (puede desencadenar falsos positivos) pero desde 0.13.0 le informa que hay 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 descrito en documentos de pandas
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 Marco de datos podría estar usando assign
:
df1 = df1.assign(e=pd.Series(np.random.randn(sLength)).values)
Asignación de columna super simple
Un marco de datos de pandas se implementa como un dict ordenado de columnas.
Esto significa que __getitem__
[]
no solo se puede utilizar para obtener una determinada columna, sino que también se puede utilizar __setitem__
[] =
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
e intenta asignarlo a un marco de datos donde los índices están apagados, 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
por defecto tiene un índice enumerado de 0 a n. Y el método de pandas [] =
intenta ser "inteligente"
Lo que realmente está pasando.
Cuando utiliza el método [] =
, pandas realiza silenciosamente una combinación o 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 []=
está tratando de hacer muchas cosas diferentes dependiendo de la entrada, y el resultado no puede predecirse a menos que usted simplemente sepa cómo funcionan los pandas. Por lo tanto, aconsejaría contra el []=
en las bases de código, pero al explorar los datos en un cuaderno, está bien.
Dando la vuelta al problema
Si tiene un pd.Series
y desea asignarlo de arriba a abajo, o si está codificando un código productivo y no está seguro del orden del índice, vale la pena protegerlo para este tipo de problema.
Puede bajar el pd.Series
a un np.ndarray
o un 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 codificador puede venir y decir "Oye, esto parece redundante, simplemente lo optimizaré".
Manera explícita
Establecer el índice de pd.Series
para que sea el í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 tenga un 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 con df.reset_index()
Dado que la disonancia del índice es el problema, si considera que el índice del marco de datos no debe dictar las cosas, simplemente puede soltar el índice, esto debería ser más rápido, pero no es muy limpio, ya que su La 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 los anteriores []=
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 tenga cuidado con df.assign
que su columna no se llama self
. Causará errores. Esto hace que df.assign
huela 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'
Puede decir: "Bueno, entonces no usaré self
entonces". Pero quién sabe cómo cambia esta función en el futuro para respaldar 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.
Formas más fáciles: -
data['new_col'] = list_of_values
data.loc[ : , 'new_col'] = list_of_values
Antes de asignar una nueva columna, si tiene datos indexados, debe 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])
Parece que en versiones recientes de Pandas, el camino a seguir es usar df.assign:
df1 = df1.assign(e=np.random.randn(sLength))
No produce SettingWithCopyWarning
.
Si obtiene SettingWithCopyWarning
, una solución fácil es copiar el DataFrame al que está intentando agregar una columna.
df = df.copy()
df['col_name'] = values
Para insertar una nueva columna en una ubicación determinada (0 <= loc <= cantidad de columnas) en un marco de datos, solo use Dataframe.insert:
DataFrame.insert(loc, column, value)
Por lo tanto, si desea agregar la columna e al final de un marco de datos llamado df , puede utilizar:
e = [-0.335485, -1.166658, -0.385571]
DataFrame.insert(loc=len(df.columns), column='e', value=e)
valor puede ser una Serie, un número entero (en cuyo caso todas las celdas se rellenan con este valor) o una estructura tipo matriz
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.insert.html
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 aquí
- esta pregunta sobre pasar una variable como argumento de palabra clave
- este método para generar una matriz
numpy
de NaN 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 map
(que es mucho más lento):
df1['e'] = df1['a'].map(lambda x: np.random.random())
Si la columna que está intentando agregar es una variable en 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 new_columns_name igual que la columna que desea reemplazar. Simplemente sobrescribirá los datos de la columna existente con los datos de la nueva serie.
Si desea establecer toda la nueva columna en un valor base inicial (por ejemplo, None
), puede hacer esto: df1['e'] = None
Esto realmente asignaría el tipo de "objeto" a la celda. Así que más tarde puede colocar tipos de datos complejos, como list, en celdas individuales.
Para agregar una nueva columna, 'e', al marco de datos existente
df1.loc[:,'e'] = Series(np.random.randn(sLength))
Sin embargo, una cosa a tener en cuenta 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 unión externo , 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 anterior. Por ejemplo,
data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)
Esta es la manera simple de agregar una nueva columna: df['e'] = e
- Primero cree un
list_of_e
de Python que tenga datos relevantes. - Utilizar esta:
df['e'] = list_of_e
Obtuve el temido SettingWithCopyWarning
, y no se solucionó usando la sintaxis iloc. Mi DataFrame fue creado por read_sql de una fuente ODBC. Usando una sugerencia de lowtech anterior, 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 el más eficiente, pero no me gustan los mensajes de advertencia. Creo que hay una mejor solución, 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 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 / generate / pandas.DataFrame.assign.html # pandas.DataFrame.assign Funciona bien para el tipo de flujo de datos donde no sobrescribe sus valores intermedios.
A toda prueba:
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
Lo siguiente es lo que hice ... Pero soy bastante nuevo en pandas y realmente Python en general, así que no hay 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 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)
Permítanme agregar eso, al igual que para hum3, .loc
no resolvió el SettingWithCopyWarning
y tuve que recurrir a df.insert()
. En mi caso, se generó un falso positivo mediante la indexación en cadena "falsa" 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 usando pd.options.mode.chained_assignment = None
y que use una de las otras soluciones dadas aquí.
En aras de la exhaustividad, otra solución más usando DataFrame. Método 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
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 {{{ X0}}, 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 función 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 tu 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í.
Para crear una columna vacía
df['i'] = np.nan
Preguntas relacionadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.