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.
- Primero crea una lista de python que contenga datos relevantes.
- 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