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

479

He creado un marco de datos de Pandas

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

y tengo esto

    xy
A NaN NaN
B NaN NaN
C NaN NaN


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

    xy
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, solo NaNestá en DataFrame.

¿Alguna sugerencia?

Mitkp
fuente
29
No use 'indexación encadenada' ( df['x']['C']), use df.ix['x','C'].
Yariv
3
El orden de acceso al índice debe ser: dataframe[column (series)] [row (Series index)]mientras que muchas personas (incluido yo mismo) están más acostumbradas al dataframe[row][column]orden. Como programador de Matlab and R, este último me parece más intuitivo, pero al parecer esa no es la forma en que trabaja Pandas ...
Zhubarb
1
Lo intenté, pero terminé agregando otros nombres de fila xy otros nombres de columna C. primero debe hacer la fila y luego la columna. entonces df.ix ['C', 'x'] = 10
Mateo
55
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

Respuestas:

594

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, se ha programado para la desaprobación .

En adelante, el método recomendado es.iat/.at .


¿ df.xs('C')['x']=10Por qué no 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

solo modifica este nuevo marco de datos.

df['x']devuelve una vista del dfmarco de datos, entonces

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
unutbu
fuente
No existe tal cosa como df.xen la API . ¿Qué querías decir?
smci
3
@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 clara.
unutbu
1
Sabía eso, pensé que estabas diciendo que df.xera un nuevo método desconocido juntodf.xs, df.ix
smci
df.xs(..., copy=True)devuelve una copia, y ese es el comportamiento predeterminado. df.xs(..., copy=False)Devuelve el original.
smci
77
Según los mantenedores, esta no es la forma recomendada de establecer un valor. Consulte stackoverflow.com/a/21287235/1579844 y mi respuesta.
Yariv
225

Actualización: el .set_valuemétodo va a estar en desuso . .iat/.atson buenos reemplazos, desafortunadamente los pandas proporcionan poca documentación


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

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

RukTech
fuente
55
Es incluso mejor que df['x']['C'] = 10 .
ALH
66
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', copia = Falso) ['x'] = 10 "1000 bucles, mejor de 3: 7.22 µs por bucle" df.set_value ('C', 'x', 10) "
propjk007
1
¿Esto también funciona para agregar una nueva fila / columna al marco de datos?
st.ph.n
Sí, lo hace (para pandas 0.16.2)
RukTech
¿Es posible usar esto para establecer un valor en a df=df.append(df.sum(numeric_only=True),ignore_index=True)?
ctrl-alt-delete
95

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

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

dónde <some_column_nameestá la columna con la que desea verificar la <condition>variable y <another_column_name>es la columna a la que desea agregar (puede ser una nueva columna 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 en función de una condición.

Blairg23
fuente
8
la segunda columna debe estar entre paréntesis, de lo contrario, todas las columnas se sobrescribirán con valor. Así:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei
No puedo hacer que esto funcione cuando <some_column_name> es mi índice (por ejemplo, índice unixtime) 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
¿Es posible cambiar un valor basado en los valores de índice y celda?
BND
@BND No estoy seguro, pero ¿podría sortear esta aparente trampa pero simplemente duplicando la columna de índice con otra columna con el mismo valor? La respuesta corta es que no lo sé.
Blairg23
@yeliabsalohcin ver la respuesta anterior.
Blairg23
40

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

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

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

Ver:

Yariv
fuente
funciona perfecto! ¡aunque será desaprobado alguna vez!
Pavlos Ponos
35

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

Yash
fuente
66
¡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
22

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

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

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

Alon Galor
fuente
hizo .locreemplazar .iat/.at?
Gabriel Fair
1
atSimilar a loc, en que ambos proporcionan búsquedas basadas en etiquetas. Úselo atsi solo necesita obtener o establecer un solo valor en un DataFrame o Series. De padas doc
Rutrus
Agradable esto funcionó para mí cuando mis elementos de índice eran numéricos.
Christopher John
Esto no funciona para una combinación de índices numéricos y de cadena.
Seanny123
12

.iat/.atEs la buena solución. Supongamos que tiene 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"]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 la función de preposesión que iatcambia para multiplicar el valor de cada celda por 2:

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

Para establecer valores, use:

df.at[0, 'clm1'] = 0
  • El método más rápido recomendado para establecer variables.
  • set_value, ixhan quedado en desuso.
  • Sin advertencia, a diferencia ilocyloc
Miladiosos
fuente
1
Llegué a la misma conclusión .
prosti
6

puedes usar .iloc.

df.iloc[[2], [0]] = 10
Muge Cevik
fuente
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
1
funciona perfecto, sin advertencia de desaprobación!
Pavlos Ponos
6

En mi ejemplo, solo 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 DataField con la columna 'peso'

Grzegorz Brzęczyszczykiewicz
fuente
4

set_value() es obsoleto.

A partir de la versión 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

ivanleoncz
fuente
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, df.loc y df.at son compatibles 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
Buena voluntad
fuente
3

Lo probé y la salida es un df.set_valuepoco más rápida, pero el método oficial df.atparece ser la forma más rápida y desaprobada 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 es establecer el valor para una sola celda. Para los vectores locy ilocdeberían ser mejores opciones ya que están vectorizados.

prosti
fuente
3

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 en una variedad de formas como

  1. Reemplace el valor de una columna para conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Reemplazar el valor de varias columnas para conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Una ventaja 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, por lo que evita los recorridos una y otra vez.

Atta Jutt
fuente
¿Qué hay de cambiar las filas?
FabioSpaghetti
simplemente use, df.loc [conditional_index,] = <nuevo valor> Reemplazará un nuevo valor en todas las columnas de filas que satisfagan la condición
Atta Jutt
2

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

Sujit Singh
fuente
1

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 ...
    • ... añádelo (13.0 s)
    • ... concatenarlo (13.1 s)
  • Almacene todas las filas nuevas en otro contenedor primero, conviértalas en un nuevo marco de datos una vez y agregue ...
    • contenedor = listas de listas (2.0 s)
    • contenedor = diccionario de listas (1.9 s)
  • Preasigne todo el marco de datos, itere sobre nuevas filas y todas las columnas y complete usando
    • ... a (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 aleatorios numpy. A este marco de datos, se agregaron 100 filas nuevas.

Código ver abajo:

#!/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))
gebbissimo
fuente
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)
Kirill Dolmatov
fuente
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 un solo valor

andrei deusteanu
fuente
0

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
Ichsan
fuente
-4

Yo también estaba buscando este tema y preparé 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'])
Joshua Magaña
fuente