Establecer valor para una celda particular en pandas DataFrame usando índice

642

He creado un DataFrame de Pandas

df = DataFrame(index=['A','B','C'], columns=['x','y'])

y tengo esto

    x    y
A  NaN  NaN
B  NaN  NaN
C  NaN  NaN


Luego, quiero asignar un valor a una celda en particular, por ejemplo, para la fila 'C' y la columna 'x'. Esperaba obtener tal resultado:

    x    y
A  NaN  NaN
B  NaN  NaN
C  10  NaN

con este código:

df.xs('C')['x'] = 10

pero el contenido de dfno ha cambiado. De nuevo NaNes solo s en DataFrame.

¿Alguna sugerencia?

5
  • 37
    No use 'indexación encadenada' ( df['x']['C']), use df.ix['x','C']. Yariv 22 de enero de 2014 a las 15:55
  • 4
    El orden de acceso al índice debe ser:, dataframe[column (series)] [row (Series index)]mientras que muchas personas (incluyéndome a mí) están más acostumbradas al dataframe[row][column]orden. Como programador Matlab y R este último se siente más intuitivos para mí, pero que al parecer no es la forma en que funciona pandas ..Zhubarb 31 de enero de 2014 a las 11:24
  • 2
    Intenté eso, pero terminé agregando otros nombres de fila x y otros nombres de columna C. tienes que hacer la fila primero y luego la columna. entonces df.ix ['C', 'x'] = 10Matthew 1/04/2016 a las 14:58
  • 8
    Al comentario de @ Yariv. Advertencia: A partir de 0.20.0, el indexador .ix está en desuso, a favor de los indexadores .iloc y .loc más estrictos. pandas.pydata.org/pandas-docs/stable/generated/… . df.at parece que se está quedando. jeffhale 30/08/18 a las 23:24
  • Asegúrese de verificar (y votar a favor de undig) la respuesta de Atta Jutt si necesita cambiar los valores de un subconjunto completo del marco de datos usando los valores de índice. Skippy le Grand Gourou 3 de marzo a las 11:02
784

La respuesta de RukTech , df.set_value('C', 'x', 10), es de lejos más rápido que las opciones que he sugerido a continuación. Sin embargo, está programado para su desaprobación .

De cara al futuro, el método recomendado es.iat/.at .


Por qué df.xs('C')['x']=10no funciona:

df.xs('C')de forma predeterminada, devuelve un nuevo marco de datos con una copia de los datos, por lo que

df.xs('C')['x']=10

modifica este nuevo marco de datos solamente.

df['x']devuelve una vista del dfmarco de datos, por lo que

df['x']['C'] = 10

se modifica a dfsí mismo.

Advertencia : a veces es difícil predecir si una operación devuelve una copia o una vista. Por esta razón, los documentos recomiendan evitar las asignaciones con "indexación encadenada" .


Entonces la alternativa recomendada es

df.at['C', 'x'] = 10

los cuales no modificar df.


In [18]: %timeit df.set_value('C', 'x', 10)
100000 loops, best of 3: 2.9 µs per loop

In [20]: %timeit df['x']['C'] = 10
100000 loops, best of 3: 6.31 µs per loop

In [81]: %timeit df.at['C', 'x'] = 10
100000 loops, best of 3: 9.2 µs per loop
8
  • No existe tal cosa df.xen la API . ¿Qué querías decir? smci 20 de mayo de 2013 a las 2:21
  • 4
    @smci: 'x'es el nombre de una columna en df. df.xdevuelve un Seriescon los valores en la columna x. Lo cambiaré a df['x']ya que esta notación funcionará con cualquier nombre de columna (a diferencia de la notación de puntos) y creo que es más claro. unutbu 20 de mayo de 2013 a las 11:58
  • 1
    Sabía eso, pensé que estabas diciendo que df.xhabía un nuevo método desconocido junto adf.xs, df.ix ... smci 20 de mayo de 2013 a las 23:27
  • 7
    Según los mantenedores, esta no es la forma recomendada de establecer un valor. Vea stackoverflow.com/a/21287235/1579844 y mi respuesta. Yariv 22/01/2014 a las 15:45
  • 1
    En mi caso, tenía una mezcla, es decir, la ubicación del índice y la etiqueta de la columna. Encontré esta manera de hacerlo funcionar:df_temp.iat[0, df_temp.columns.get_loc('Cash')] = df_temp['Cash'].iloc[0] + start_valPete 26/03/19 a las 2:09
242

Actualización: el .set_valuemétodo quedará obsoleto . .iat/.atson buenos reemplazos, desafortunadamente los pandas proporcionan poca documentación


La forma más rápida de hacer esto es usando set_value . Este método es ~ 100 veces más rápido que el .ixmétodo. Por ejemplo:

df.set_value('C', 'x', 10)

8
  • 5
    Es incluso mejor que df['x']['C'] = 10 . Alireza 17/10/15 a las 13:16
  • 7
    1000 bucles, mejor de 3: 195 µs por bucle "df ['x'] ['C'] = 10" 1000 bucles, mejor de 3: 310 µs por bucle "df.ix ['C', 'x'] = 10 "1000 bucles, mejor de 3: 189 µs por bucle" df.xs ('C', copy = False) ['x'] = 10 "1000 bucles, mejor de 3: 7,22 µs por bucle" df.set_value ('C', 'x', 10) "propjk007 12/01/16 a las 17:37
  • 1
    ¿Esto también funciona para agregar una nueva fila / columna al marco de datos? st.ph.n 24 feb 2016 a las 18:46
  • Sí lo hace (para pandas 0.16.2)RukTech 2 de marzo de 2016 a las 0:33
  • ¿Es posible usar esto para establecer un valor en a df=df.append(df.sum(numeric_only=True),ignore_index=True)? ctrl-alt-delete 17 de mayo de 2016 a las 11:57
137

También puede usar una búsqueda condicional usando .loccomo se ve aquí:

df.loc[df[<some_column_name>] == <condition>, [<another_column_name>]] = <value_to_add>

donde <some_column_namees la columna con la que desea comparar la <condition>variable y <another_column_name>es la columna a la que desea agregar (puede ser una columna nueva o una que ya existe). <value_to_add>es el valor que desea agregar a esa columna / fila.

Este ejemplo no funciona precisamente con la pregunta en cuestión, pero podría ser útil para alguien que quiera agregar un valor específico basado en una condición.

6
  • 14
    la segunda columna debe estar entre corchetes, de lo contrario, todas las columnas se sobrescribirán con valor. Así:df.loc[df['age']==3, ['age-group']] = 'toddler'Piizei 12/09/18 a las 10:55
  • No puedo hacer que esto funcione cuando <some_column_name> es mi índice (digamos un índice de tiempo único) y estoy tratando de agregar una marca de tiempo que aún no sale (es decir, una nueva lectura de marca de tiempo). ¿Alguna idea? yeliabsalohcin 14/06/19 a las 20:43
  • ¿Es posible cambiar un valor en función del índice y los valores de la celda? BND 8 de enero de 2020 a las 10:59
  • @BND No estoy seguro, pero podría evitar este aparente error pero simplemente duplicando la columna de índice con otra columna con el mismo valor. La respuesta corta es no lo sé. Blairg23 17 de enero de 2020 a las 23:58
  • @yeliabsalohcin vea la respuesta anterior. Blairg23 17 de enero de 2020 a las 23:59
60

Intenta usar df.loc[row_index,col_indexer] = value

1
  • 10
    ¡Bienvenido a Stack Overflow! Considere editar su publicación para agregar más explicaciones sobre lo que hace su código y por qué resolverá el problema. Una respuesta que en su mayoría solo contiene código (incluso si está funcionando) generalmente no ayudará al OP a comprender su problema. También se recomienda que no publique una respuesta si es solo una suposición. Una buena respuesta tendrá una razón plausible de por qué podría resolver el problema del OP. SuperBiasedMan 15 oct.15 a las 16:46
46

La forma recomendada (según los mantenedores) de establecer un valor es:

df.ix['x','C']=10

El uso de 'indexación encadenada' ( df['x']['C']) puede ocasionar problemas.

Ver:

2
32

¡Esto es lo único que funcionó para mí!

df.loc['C', 'x'] = 10

Obtenga más información .loc aquí .

4
  • hizo .locreemplazar .iat/.at? Gabriel Fair 17/07/18 a las 22:48
  • 1
    atSimilar a loc, en que ambos proporcionan búsquedas basadas en etiquetas. Úselo atsi solo necesita obtener o establecer un valor único en un DataFrame o Series. De padas docRutrus 31 de julio de 2018 a las 1:31
  • Bueno, esto funcionó para mí cuando mis elementos de índice eran numéricos. Christopher John 25 feb 2019 a las 8:13
  • 1
    Esto no funciona para una combinación de índices numéricos y de cadena. Seanny123 26/03/19 a las 17:21
18

Para establecer valores, use:

df.at[0, 'clm1'] = 0
  • El método recomendado más rápido para configurar variables.
  • set_value, ixhan quedado obsoletos.
  • Sin advertencia, a diferencia ilocyloc
1
17

.iat/.ates la buena solución. Suponiendo que tenga este simple data_frame:

   A   B   C
0  1   8   4 
1  3   9   6
2  22 33  52

si queremos modificar el valor de la celda [0,"A"]u podemos usar una de esas soluciones:

  1. df.iat[0,0] = 2
  2. df.at[0,'A'] = 2

Y aquí hay un ejemplo completo de cómo usar iatpara obtener y establecer un valor de celda:

def prepossessing(df):
  for index in range(0,len(df)): 
      df.iat[index,0] = df.iat[index,0] * 2
  return df

y_train antes:

    0
0   54
1   15
2   15
3   8
4   31
5   63
6   11

y_train después de llamar a una función atractiva que iatcambiar para multiplicar el valor de cada celda por 2:

     0
0   108
1   30
2   30
3   16
4   62
5   126
6   22
8

En mi ejemplo, simplemente lo cambio en la celda seleccionada

    for index, row in result.iterrows():
        if np.isnan(row['weight']):
            result.at[index, 'weight'] = 0.0

'resultado' es un campo de datos con la columna 'peso'

6

puede utilizar .iloc.

df.iloc[[2], [0]] = 10
1
  • Este método parece no admitir varios valores, por ejemplo, lo df.iloc[[2:8], [0]] = [2,3,4,5,6,7]que hace el método de df.loc()forma nativa. strpeter 23/11/2017 a las 10:58
6

set_value() es obsoleto.

A partir del lanzamiento 0.23.4, Pandas " anuncia el futuro " ...

>>> df
                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        245.0
2      Chevrolet Malibu        190.0
>>> df.set_value(2, 'Prices (U$)', 240.0)
__main__:1: FutureWarning: set_value is deprecated and will be removed in a future release.
Please use .at[] or .iat[] accessors instead

                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        245.0
2      Chevrolet Malibu        240.0

Teniendo en cuenta este consejo, aquí hay una demostración de cómo usarlos:

  • por posiciones enteras de fila / columna

>>> df.iat[1, 1] = 260.0
>>> df
                   Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        260.0
2      Chevrolet Malibu        240.0
  • por etiquetas de fila / columna

>>> df.at[2, "Cars"] = "Chevrolet Corvette"
>>> df
                  Cars  Prices (U$)
0               Audi TT        120.0
1 Lamborghini Aventador        260.0
2    Chevrolet Corvette        240.0

Referencias:

1
  • 1
    df.at[]es una opción mucho más fácil ... graciasManoj Kumar 12 de marzo a las 19:22
6

Yo sugeriría:

df.loc[index_position, "column_name"] = some_value
4

Probé y la salida es un df.set_valuepoco más rápida, pero el método oficial df.atparece la forma más rápida y no obsoleta de hacerlo.

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.rand(100, 100))

%timeit df.iat[50,50]=50 # ✓
%timeit df.at[50,50]=50 #  ✔
%timeit df.set_value(50,50,50) # will deprecate
%timeit df.iloc[50,50]=50
%timeit df.loc[50,50]=50

7.06 µs ± 118 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
5.52 µs ± 64.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
3.68 µs ± 80.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
98.7 µs ± 1.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
109 µs ± 1.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Tenga en cuenta que esto establece el valor de una sola celda. Para los vectores locy ilocdeberían ser mejores opciones ya que están vectorizados.

4

Una forma de usar el índice con condición es primero obtener el índice de todas las filas que satisfacen su condición y luego simplemente usar esos índices de fila de varias maneras

conditional_index = df.loc[ df['col name'] <condition> ].index

La condición de ejemplo es como

==5, >10 , =="Any string", >= DateTime

Luego, puede usar estos índices de fila de diversas formas, como

  1. Reemplazar el valor de una columna por conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Reemplazar el valor de varias columnas por conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Un beneficio de guardar el conditional_index es que puede asignar el valor de una columna a otra columna con el mismo índice de fila
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Todo esto es posible porque .index devuelve una matriz de índice que .loc puede usar con direccionamiento directo para evitar recorridos una y otra vez.

2
  • ¿qué pasa con el cambio de filas? FabioSpaghetti 20 dic 2019 a las 9:15
  • solo use, df.loc [conditional_index,] = <nuevo valor> Reemplazará el nuevo valor en todas las columnas de filas que satisfagan la condiciónAtta Jutt 8 de ene. De 2020 a las 9:16
3

Aquí hay un resumen de las soluciones válidas proporcionadas por todos los usuarios, para marcos de datos indexados por entero y cadena.

df.iloc, df.loc y df.at funcionan para ambos tipos de marcos de datos, df.iloc solo funciona con índices enteros de fila / columna, soporte df.loc y df.at para establecer valores usando nombres de columna y / o índices enteros .

Cuando el índice especificado no existe, tanto df.loc como df.at agregarían las filas / columnas recién insertadas al marco de datos existente, pero df.iloc generaría "IndexError: los indexadores posicionales están fuera de los límites". Un ejemplo de trabajo probado en Python 2.7 y 3.7 es el siguiente:

import numpy as np, pandas as pd

df1 = pd.DataFrame(index=np.arange(3), columns=['x','y','z'])
df1['x'] = ['A','B','C']
df1.at[2,'y'] = 400

# rows/columns specified does not exist, appends new rows/columns to existing data frame
df1.at['D','w'] = 9000
df1.loc['E','q'] = 499

# using df[<some_column_name>] == <condition> to retrieve target rows
df1.at[df1['x']=='B', 'y'] = 10000
df1.loc[df1['x']=='B', ['z','w']] = 10000

# using a list of index to setup values
df1.iloc[[1,2,4], 2] = 9999
df1.loc[[0,'D','E'],'w'] = 7500
df1.at[[0,2,"D"],'x'] = 10
df1.at[:, ['y', 'w']] = 8000

df1
>>> df1
     x     y     z     w      q
0   10  8000   NaN  8000    NaN
1    B  8000  9999  8000    NaN
2   10  8000  9999  8000    NaN
D   10  8000   NaN  8000    NaN
E  NaN  8000  9999  8000  499.0
3

Entonces, su pregunta para convertir NaN en ['x', C] al valor 10

la respuesta es..

df['x'].loc['C':]=10
df

el código alternativo es

df.loc['C', 'x']=10
df
2

df.loc['c','x']=10 Esto cambiará el valor de c º fila y x ésima columna.

2

Si uno quiere cambiar la celda en la posición (0,0) del df a una cadena como '"236"76"', las siguientes opciones harán el trabajo:

df[0][0] = '"236"76"'
# %timeit df[0][0] = '"236"76"'
# 938 µs ± 83.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

O usando pandas.DataFrame.at

df.at[0, 0] = '"236"76"'
#  %timeit df.at[0, 0] = '"236"76"' 
#15 µs ± 2.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

O usando pandas.DataFrame.iat

df.iat[0, 0] = '"236"76"'
#  %timeit df.iat[0, 0] = '"236"76"'
# 41.1 µs ± 3.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

O usando pandas.DataFrame.loc

df.loc[0, 0] = '"236"76"'
#  %timeit df.loc[0, 0] = '"236"76"'
# 5.21 ms ± 401 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

O usando pandas.DataFrame.iloc

df.iloc[0, 0] = '"236"76"'
#  %timeit df.iloc[0, 0] = '"236"76"'
# 5.12 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Si el tiempo es importante, usar pandas.DataFrame.ates el enfoque más rápido.

0

Si desea cambiar los valores no para toda la fila, sino solo para algunas columnas:

x = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
x.iloc[1] = dict(A=10, B=-10)
0

Desde la versión 0.21.1 también puede usar el .atmétodo. Hay algunas diferencias en comparación con .loclo mencionado aquí: pandas .at versus .loc , pero es más rápido en el reemplazo de valor único

0

Además de las respuestas anteriores, aquí hay un punto de referencia que compara diferentes formas de agregar filas de datos a un marco de datos ya existente. Muestra que usar at o set-value es la forma más eficiente para grandes marcos de datos (al menos para estas condiciones de prueba).

  • Cree un nuevo marco de datos para cada fila y ...
    • ... agregarlo (13.0 s)
    • ... concatenarlo (13.1 s)
  • Almacene todas las filas nuevas en otro contenedor primero, conviértalas a un nuevo marco de datos una vez y agregue ...
    • contenedor = listas de listas (2.0 s)
    • contenedor = diccionario de listas (1,9 s)
  • Preasigne el marco de datos completo, itere sobre nuevas filas y todas las columnas y complete usando
    • ... en (0,6 s)
    • ... set_value (0,4 s)

Para la prueba, se utilizó un marco de datos existente que comprende 100,000 filas y 1,000 columnas y valores numéricos aleatorios. A este marco de datos, se agregaron 100 filas nuevas.

Ver código a continuación:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 21 16:38:46 2018

@author: gebbissimo
"""

import pandas as pd
import numpy as np
import time

NUM_ROWS = 100000
NUM_COLS = 1000
data = np.random.rand(NUM_ROWS,NUM_COLS)
df = pd.DataFrame(data)

NUM_ROWS_NEW = 100
data_tot = np.random.rand(NUM_ROWS + NUM_ROWS_NEW,NUM_COLS)
df_tot = pd.DataFrame(data_tot)

DATA_NEW = np.random.rand(1,NUM_COLS)


#%% FUNCTIONS

# create and append
def create_and_append(df):
    for i in range(NUM_ROWS_NEW):
        df_new = pd.DataFrame(DATA_NEW)
        df = df.append(df_new)
    return df

# create and concatenate
def create_and_concat(df):
    for i in range(NUM_ROWS_NEW):
        df_new = pd.DataFrame(DATA_NEW)
        df = pd.concat((df, df_new))
    return df


# store as dict and 
def store_as_list(df):
    lst = [[] for i in range(NUM_ROWS_NEW)]
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            lst[i].append(DATA_NEW[0,j])
    df_new = pd.DataFrame(lst)
    df_tot = df.append(df_new)
    return df_tot

# store as dict and 
def store_as_dict(df):
    dct = {}
    for j in range(NUM_COLS):
        dct[j] = []
        for i in range(NUM_ROWS_NEW):
            dct[j].append(DATA_NEW[0,j])
    df_new = pd.DataFrame(dct)
    df_tot = df.append(df_new)
    return df_tot




# preallocate and fill using .at
def fill_using_at(df):
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            #print("i,j={},{}".format(i,j))
            df.at[NUM_ROWS+i,j] = DATA_NEW[0,j]
    return df


# preallocate and fill using .at
def fill_using_set(df):
    for i in range(NUM_ROWS_NEW):
        for j in range(NUM_COLS):
            #print("i,j={},{}".format(i,j))
            df.set_value(NUM_ROWS+i,j,DATA_NEW[0,j])
    return df


#%% TESTS
t0 = time.time()    
create_and_append(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
create_and_concat(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
store_as_list(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
store_as_dict(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
fill_using_at(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))

t0 = time.time()    
fill_using_set(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
0

Evite la asignación con indexación encadenada

Se trata de una tarea con indexación encadenada que dará como resultado una SettingWithCopyadvertencia . Esto debe evitarse por todos los medios.

Tu asignación tendrá que recurrir a una sola .loc[]o por .iloc[]rebanada, como se explica aquí . Por lo tanto, en su caso:

df.loc['C', 'x'] = 10
-4

Yo también estaba buscando este tema y armé una forma de iterar a través de un DataFrame y actualizarlo con valores de búsqueda de un segundo DataFrame. Aquí está mi código.

src_df = pd.read_sql_query(src_sql,src_connection)
for index1, row1 in src_df.iterrows():
    for index, row in vertical_df.iterrows():
        src_df.set_value(index=index1,col=u'etl_load_key',value=etl_load_key)
        if (row1[u'src_id'] == row['SRC_ID']) is True:
            src_df.set_value(index=index1,col=u'vertical',value=row['VERTICAL'])
0