Imposta il valore per una particolare cella in Panda DataFrame usando index


479

Ho creato un Pandas DataFrame

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

e ottenuto questo

    xy
A NaN NaN
B NaN NaN
C NaN NaN


Quindi desidero assegnare un valore a una cella particolare, ad esempio per la riga 'C' e la colonna 'x'. Mi aspettavo di ottenere questo risultato:

    xy
A NaN NaN
B NaN NaN
C 10 NaN

con questo codice:

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

ma i contenuti di dfnon sono cambiati. È di nuovo solo NaNin DataFrame.

Eventuali suggerimenti?


29
Non usare 'indicizzazione concatenata' ( df['x']['C']), usare df.ix['x','C'].
Yariv,

3
L'ordine di accesso all'indice deve essere dataframe[column (series)] [row (Series index)]:, mentre molte persone (incluso me stesso) sono più abituate dataframe[row][column]all'ordine. Come programmatore Matlab e R, quest'ultimo mi sembra più intuitivo, ma a quanto pare non è il modo in cui funziona Pandas ..
Zhubarb

1
l'ho provato, ma ho finito per aggiungere un altro nome di riga x e un altro nome di colonna C. devi prima fare la riga e poi la colonna. così df.ix ['C', 'x'] = 10
Matthew

5
Al commento di @Yariv. Avvertenza: a partire da 0.20.0, l'indicizzatore .ix è obsoleto, a favore degli indicizzatori .iloc e .loc più rigorosi. pandas.pydata.org/pandas-docs/stable/generated/… . df.at sembra che stia restando in giro.
jeffhale,

Risposte:


594

La risposta di RukTech , df.set_value('C', 'x', 10), è di gran lunga più veloce rispetto alle opzioni che ho suggerito di seguito. Tuttavia, è stato programmato per la deprecazione .

Andando avanti, il metodo raccomandato è.iat/.at .


Perché df.xs('C')['x']=10non funziona:

df.xs('C')per impostazione predefinita, restituisce un nuovo frame di dati con una copia dei dati, quindi

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

modifica solo questo nuovo frame di dati.

df['x']restituisce una vista del dfframe di dati, quindi

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

si modifica da dfsolo.

Avviso : a volte è difficile prevedere se un'operazione restituisce una copia o una vista. Per questo motivo i documenti raccomandano di evitare assegnazioni con "indicizzazione concatenata" .


Quindi l'alternativa consigliata è

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

che non modifica 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

Non c'è cosa come df.xnel API . Cosa intendevi?
smci,

3
@smci: 'x'è il nome di una colonna in df. df.xrestituisce a Seriescon i valori nella colonna x. Lo cambierò in df['x']poiché questa notazione funzionerà con qualsiasi nome di colonna (a differenza della notazione punto) e penso che sia più chiaro.
unutbu,

1
Lo sapevo, pensavo che stavi dicendo che df.xc'era un nuovo metodo sconosciuto a fiancodf.xs, df.ix
smci

df.xs(..., copy=True)restituisce una copia e questo è il comportamento predefinito. df.xs(..., copy=False)restituisce l'originale.
smci,

7
Secondo i manutentori, questo non è il modo raccomandato per impostare un valore. Vedi stackoverflow.com/a/21287235/1579844 e la mia risposta.
Yariv,

225

Aggiornamento: il .set_valuemetodo sarà obsoleto . .iat/.atsono buoni sostituti, purtroppo i panda forniscono poca documentazione


Il modo più veloce per farlo è usare set_value . Questo metodo è ~ 100 volte più veloce del .ixmetodo. Per esempio:

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


5
È anche meglio di df['x']['C'] = 10 .
ALH,

6
1000 loop, meglio di 3: 195 µs per loop "df ['x'] ['C'] = 10" 1000 loop, meglio di 3: 310 µs per loop "df.ix ['C', 'x'] = 10 "1000 loop, meglio di 3: 189 µs per loop" df.xs ('C', copy = False) ['x'] = 10 "1000 loop, meglio di 3: 7.22 µs per loop" df.set_value ('C', 'x', 10) "
propjk007,

1
funziona anche per aggiungere una nuova riga / colonna al frame di dati?
st.ph.n,

Sì, lo fa (per i panda 0.16.2)
RukTech

È possibile utilizzare questo per impostare un valore su a df=df.append(df.sum(numeric_only=True),ignore_index=True)?
ctrl-alt-delete

95

Puoi anche usare una ricerca condizionale usando .loccome visto qui:

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

dove si <some_column_nametrova la colonna in cui si desidera verificare la <condition>variabile ed <another_column_name>è la colonna che si desidera aggiungere (può essere una nuova colonna o una già esistente). <value_to_add>è il valore che desideri aggiungere a quella colonna / riga.

Questo esempio non funziona esattamente con la domanda a portata di mano, ma potrebbe essere utile per qualcuno che desidera aggiungere un valore specifico basato su una condizione.


8
la seconda colonna deve essere tra parentesi, altrimenti tutte le colonne verranno sovrascritte con valore. In questo modo:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei,

Non riesco a farlo funzionare quando <some_column_name> è il mio indice (l'indice unixtime dice) e sto cercando di aggiungere un timestamp che non esce ancora (cioè una nuova lettura di timestamp). qualche idea?
yeliabsalohcin,

È possibile modificare un valore in base all'indice e ai valori della cella?
BND

@BND Non ne sono sicuro, ma potresti aggirare questa apparente trappola ma semplicemente duplicando la colonna dell'indice con un'altra colonna con lo stesso valore? La risposta breve è che non lo so.
Blairg23,

@yeliabsalohcin vedi la risposta sopra.
Blairg23,

40

Il modo consigliato (secondo i manutentori) di impostare un valore è:

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

L'uso di "indicizzazione concatenata" ( df['x']['C']) può causare problemi.

Vedere:



funziona perfettamente! anche se qualche volta sarà deprecato!
Pavlos Ponos,

35

Prova a usare df.loc[row_index,col_indexer] = value


6
Benvenuto in Stack Overflow! Ti consigliamo di modificare il tuo post per aggiungere ulteriori spiegazioni su cosa fa il tuo codice e perché risolverà il problema. Una risposta che per lo più contiene solo codice (anche se funziona) di solito non aiuta l'OP a capire il loro problema. Si consiglia inoltre di non pubblicare una risposta se si tratta solo di un'ipotesi. Una buona risposta avrà una ragione plausibile per cui potrebbe risolvere il problema del PO.
SuperBiasedMan,

22

Questa è l'unica cosa che ha funzionato per me!

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

Ulteriori informazioni .loc qui .


ha .locsostituito .iat/.at?
Gabriel Fair,

1
atSimile a loc, in quanto entrambi forniscono ricerche basate su etichette. Utilizzare atse è necessario solo ottenere o impostare un singolo valore in un DataFrame o in una serie. Da padas doc
Rutrus,

Bello, questo ha funzionato per me quando i miei elementi di indice erano numerici.
Christopher John,

Questo non funziona per un mix di indici numerici e di stringhe.
Seanny123,

12

.iat/.atè la buona soluzione. Supponendo di avere questo semplice frame_dati:

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

se vogliamo modificare il valore della cella [0,"A"]puoi usare una di quelle soluzioni:

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

Ed ecco un esempio completo su come usare iatper ottenere e impostare un valore di cella:

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

y_train prima:

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

y_train dopo aver chiamato la funzione di pre-acquisizione che iatper cambiare per moltiplicare il valore di ogni cella per 2:

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

8

Per impostare i valori, utilizzare:

df.at[0, 'clm1'] = 0
  • Il metodo consigliato più veloce per impostare le variabili.
  • set_value, ixsono stati deprecati.
  • Nessun avvertimento, a differenza di iloceloc

1
Sono arrivato alla stessa identica conclusione .
prosti

6

puoi usare .iloc.

df.iloc[[2], [0]] = 10

Questo metodo sembra non supportare diversi valori, ad es. Come fa df.iloc[[2:8], [0]] = [2,3,4,5,6,7]il metodo in df.loc()modo nativo.
strpeter,

1
funziona perfettamente, senza preavviso di deprecazione!
Pavlos Ponos,

6

Nel mio esempio, lo cambio nella cella selezionata

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

'risultato' è un campo dati con colonna 'peso'


4

set_value() è deprecato.

A partire dalla versione 0.23.4, Pandas " annuncia il 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

Considerando questo consiglio, ecco una dimostrazione di come usarli:

  • per posizioni intere di riga / colonna

>>> 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
  • per etichette di riga / colonna

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

Riferimenti:


3

Ecco un riepilogo delle soluzioni valide fornite da tutti gli utenti, per i frame di dati indicizzati da numero intero e stringa.

df.iloc, df.loc e df.at funzionano per entrambi i tipi di frame di dati, df.iloc funziona solo con indici interi di riga / colonna, df.loc e df.at supporta l'impostazione di valori usando nomi di colonne e / o indici interi .

Quando l'indice specificato non esiste, sia df.loc che df.at aggiungerebbero le righe / colonne appena inserite al frame di dati esistente, ma df.iloc genererebbe "IndexError: gli indicizzatori di posizione sono fuori limite". Un esempio funzionante testato in Python 2.7 e 3.7 è il seguente:

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

Ho testato e l'output è un df.set_valuepo 'più veloce, ma il metodo ufficiale df.atsembra il modo più veloce non deprecato per farlo.

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)

Nota che si sta impostando il valore per una singola cella. Per i vettori loce ilocdovrebbero essere opzioni migliori poiché sono vettorializzate.


3

Un modo per utilizzare l'indice con condizione è innanzitutto ottenere l'indice di tutte le righe che soddisfano la propria condizione e quindi semplicemente utilizzare tali indici di riga in più modi

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

La condizione di esempio è simile

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

Quindi è possibile utilizzare questi indici di riga in vari modi come

  1. Sostituisci il valore di una colonna per conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Sostituisci il valore di più colonne per conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Un vantaggio con il salvataggio di conditional_index è che è possibile assegnare il valore di una colonna a un'altra colonna con lo stesso indice di riga
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Tutto ciò è possibile perché .index restituisce una matrice di indice che .loc può utilizzare con l'indirizzamento diretto in modo da evitare ripetutamente gli attraversamenti.


che dire di cambiare le righe?
Fabio Spaghetti,

basta usare, df.loc [conditional_index,] = <nuovo valore> Sostituirà il nuovo valore in tutte le colonne di righe che soddisfano la condizione
Atta Jutt

2

df.loc['c','x']=10 Questo cambierà il valore di c esima riga e x esima colonna.


1

Oltre alle risposte di cui sopra, ecco un benchmark che confronta diversi modi per aggiungere righe di dati a un frame di dati già esistente. Mostra che l'utilizzo di at o set-value è il modo più efficiente per grandi frame di dati (almeno per queste condizioni di test).

  • Crea un nuovo frame di dati per ogni riga e ...
    • ... aggiungilo (13.0 s)
    • ... concatenalo (13.1 s)
  • Archivia prima tutte le nuove righe in un altro contenitore, converti una volta in un nuovo frame di dati e aggiungi ...
    • container = elenchi di elenchi (2.0 s)
    • container = dizionario degli elenchi (1.9 s)
  • Preallocare l'intero frame di dati, scorrere su nuove righe e tutte le colonne e riempire usando
    • ... a (0,6 s)
    • ... set_value (0.4 s)

Per il test, è stato utilizzato un frame di dati esistente comprendente 100.000 righe e 1.000 colonne e valori casuali numpy. A questo frame di dati sono state aggiunte 100 nuove righe.

Codice vedi sotto:

#!/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

Se si desidera modificare i valori non per l'intera riga, ma solo per alcune colonne:

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

0

Dalla versione 0.21.1 puoi anche usare il .atmetodo. Ci sono alcune differenze rispetto a .locquelle menzionate qui - panda .at contro .loc , ma è più veloce sulla sostituzione di un singolo valore


0

Quindi, la tua domanda per convertire NaN a ['x', C] in valore 10

la risposta è..

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

codice alternativo è

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

-4

Anch'io stavo cercando questo argomento e ho messo insieme un modo per scorrere un DataFrame e aggiornarlo con i valori di ricerca di un secondo DataFrame. Ecco il mio codice

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'])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.