Defina o valor para uma célula específica no DataFrame do pandas usando o índice

478

Eu criei um DataFrame do Pandas

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

e entendi

    xy
A NaN NaN
B NaN NaN
C NaN NaN


Quero atribuir valor a uma célula específica, por exemplo, para a linha 'C' e a coluna 'x'. Eu esperava obter esse resultado:

    xy
A NaN NaN
B NaN NaN
C 10 NaN

com este código:

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

mas o conteúdo de dfnão mudou. É novamente apenas NaNs no DataFrame.

Alguma sugestão?

Mitkp
fonte
29
Não use 'indexação encadeada' ( df['x']['C']), use df.ix['x','C'].
Yariv
3
A ordem do acesso ao índice precisa ser dataframe[column (series)] [row (Series index)]:, enquanto muitas pessoas (inclusive eu) estão mais acostumadas à dataframe[row][column]ordem. Como um programador Matlab e R últimos sente mais intuitivas para mim, mas que, aparentemente, não é o caminho Pandas funciona ..
Zhubarb
1
Eu tentei isso, mas acabei adicionando outros nomes de linha xe outros nomes de coluna C. você precisa fazer a linha primeiro e depois a coluna. então df.ix ['C', 'x'] = 10
Matthew
5
Para o comentário de @ Yariv. Aviso: A partir de 0.20.0, o indexador .ix foi descontinuado, em favor dos indexadores .iloc e .loc mais rigorosos. pandas.pydata.org/pandas-docs/stable/generated/… . Parece que o df.at está persistindo.
jeffhale

Respostas:

593

A resposta de RukTech , df.set_value('C', 'x', 10), é de longe mais rápido do que as opções que eu sugeri abaixo. No entanto, está programado para ser preterido .

No futuro, o método recomendado é.iat/.at .


Por df.xs('C')['x']=10que não funciona:

df.xs('C')por padrão, retorna um novo quadro de dados com uma cópia dos dados, portanto

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

modifica apenas esse novo quadro de dados.

df['x']retorna uma visão do quadro de dfdados, portanto

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

se modifica df.

Aviso : Às vezes, é difícil prever se uma operação retorna uma cópia ou uma exibição. Por esse motivo, os documentos recomendam evitar atribuições com "indexação encadeada" .


Portanto, a alternativa recomendada é

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

que se modifique 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
fonte
Não existe df.xna API . O que você quis dizer?
smci 20/05
3
@smci: 'x'é o nome de uma coluna em df. df.xretorna a Seriescom os valores na coluna x. Vou mudar para df['x']uma vez que essa notação funcionará com qualquer nome de coluna (diferente da notação de ponto) e acho que é mais clara.
Unutbu 20/05
1
Eu sabia que, eu pensei que você estava dizendo df.xera algum novo método desconhecido ao ladodf.xs, df.ix
SMCI
df.xs(..., copy=True)retorna uma cópia, e esse é o comportamento padrão. df.xs(..., copy=False)retorna o original.
SMCI
7
Segundo os mantenedores, essa não é a maneira recomendada de definir um valor. Consulte stackoverflow.com/a/21287235/1579844 e minha resposta.
Yariv
225

Atualização: o .set_valuemétodo será preterido . .iat/.atsão boas substituições, infelizmente os pandas fornecem pouca documentação


A maneira mais rápida de fazer isso é usando set_value . Este método é ~ 100 vezes mais rápido que o .ixmétodo. Por exemplo:

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

RukTech
fonte
5
É ainda melhor que df['x']['C'] = 10 .
ALH
6
1000 loops, melhor de 3: 195 µs por loop "df ['x'] ['C'] = 10" 1000 loops, melhor de 3: 310 µs por loop "df.ix ['C', 'x'] = 10 "1000 loops, melhor de 3: 189 µs por loop" df.xs ('C', cópia = Falso) ['x'] = 10 "1000 loops, melhor de 3: 7,22 µs por loop" df.set_value ('C', 'x', 10) "
propjk007 12/01/16
1
isso também funciona para adicionar uma nova linha / coluna ao quadro de dados?
st.ph.n
Sim, funciona (para pandas 0.16.2)
RukTech
É possível usar isso para definir um valor para a df=df.append(df.sum(numeric_only=True),ignore_index=True)?
ctrl-alt-delete
95

Você também pode usar uma pesquisa condicional usando .loccomo visto aqui:

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

onde <some_column_nameestá a coluna na qual você deseja verificar a <condition>variável e <another_column_name>a coluna à qual deseja adicionar (pode ser uma nova coluna ou uma que já existe). <value_to_add>é o valor que você deseja adicionar a essa coluna / linha.

Este exemplo não funciona exatamente com a pergunta em questão, mas pode ser útil para alguém que deseja adicionar um valor específico com base em uma condição.

Blairg23
fonte
8
a segunda coluna precisa estar entre colchetes, caso contrário, todas as colunas serão substituídas pelo valor. Assim:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei 12/09
Não consigo fazer isso funcionar quando <some_column_name> é o meu índice (digamos o índice unixtime) e estou tentando adicionar um carimbo de data / hora que ainda não sai (por exemplo, uma nova leitura de carimbo de data / hora). Alguma ideia?
yeliabsalohcin
É possível alterar um valor com base nos valores de índice e célula?
BND
@BND Não tenho certeza, mas você pode contornar essa armadilha aparente, mas simplesmente duplicar a coluna do índice com outra coluna com o mesmo valor? A resposta curta é que eu não sei.
Blairg23 17/01
@yeliabsalohcin veja a resposta acima.
Blairg23 17/01
40

A maneira recomendada (de acordo com os mantenedores) para definir um valor é:

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

Usar 'indexação encadeada' ( df['x']['C']) pode levar a problemas.

Vejo:

Yariv
fonte
funciona perfeito! embora seja preterido em algum momento!
Pavlos Ponos
35

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

Yash
fonte
6
Bem-vindo ao Stack Overflow! Considere editar sua postagem para adicionar mais explicações sobre o que seu código faz e por que ele resolverá o problema. Uma resposta que geralmente contém apenas código (mesmo que esteja funcionando) geralmente não ajuda o OP a entender o problema. Também é recomendável que você não poste uma resposta, se for apenas um palpite. Uma boa resposta terá um motivo plausível para resolver o problema do OP.
SuperBiasedMan
22

Esta é a única coisa que funcionou para mim!

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

Saiba mais sobre .loc aqui .

Alon Galor
fonte
se .locsubstituir .iat/.at?
Gabriel Fair
1
atSemelhante loc, na medida em que ambos fornecem pesquisas baseadas em etiquetas. Use atse você precisar apenas obter ou definir um único valor em um DataFrame ou Series. De padas doc
Rutrus 31/07
Bom, isso funcionou para mim quando meus elementos de índice eram numéricos.
Christopher John
Isso não funciona para uma mistura de índices numéricos e de string.
Seanny123
12

.iat/.até a boa solução. Supondo que você tenha esse data_frame simples:

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

se quisermos modificar o valor da célula, [0,"A"]você pode usar uma dessas soluções:

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

E aqui está um exemplo completo de como usar iatpara obter e definir um valor de célula:

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 após chamar a função prepossessing que iatmuda para multiplicar o valor de cada célula por 2:

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

Para definir valores, use:

df.at[0, 'clm1'] = 0
  • O método mais rápido recomendado para definir variáveis.
  • set_value, ixforam preteridos.
  • Nenhum aviso, diferente iloceloc
Miladiouss
fonte
1
Cheguei exatamente à mesma conclusão .
prosti
6

você pode usar .iloc.

df.iloc[[2], [0]] = 10
Muge Cevik
fonte
Este método parece não suportar vários valores, por exemplo, o df.iloc[[2:8], [0]] = [2,3,4,5,6,7]que o método df.loc()faz nativamente.
Strpeter 23/11
1
funciona perfeito, sem aviso de reprovação!
Pavlos Ponos
6

No meu exemplo, eu apenas mudo na célula selecionada

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

'result' é um dataField com a coluna 'weight'

Grzegorz Brzęczyszczykiewicz
fonte
4

set_value() está obsoleto.

A partir do release 0.23.4, o Pandas " anuncia o 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 este conselho, aqui está uma demonstração de como usá-los:

  • por posições inteiras de linha / coluna

>>> 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 rótulos de linha / coluna

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

Referências:

ivanleoncz
fonte
3

Aqui está um resumo das soluções válidas fornecidas por todos os usuários, para quadros de dados indexados por número inteiro e sequência.

O df.iloc, o df.loc e o df.at funcionam para ambos os tipos de quadros de dados, o df.iloc funciona apenas com índices inteiros de linha / coluna, os suportes df.loc e df.at para definir valores usando nomes de coluna e / ou índices inteiros .

Quando o índice especificado não existir, o df.loc e o df.at anexarão as linhas / colunas recém-inseridas ao quadro de dados existente, mas o df.iloc aumentará "IndexError: indexadores posicionais estão fora dos limites". Um exemplo de trabalho testado no Python 2.7 e 3.7 é o seguinte:

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
Boa vontade
fonte
3

Eu testei e a saída é df.set_valueum pouco mais rápida, mas o método oficial df.atparece a maneira mais rápida e não obsoleta de fazer isso.

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)

Observe que isso está configurando o valor para uma única célula. Para os vetores loce ilocdeve haver melhores opções, pois são vetorizados.

prosti
fonte
3

Uma maneira de usar o índice com condição é primeiro obter o índice de todas as linhas que satisfazem sua condição e, em seguida, simplesmente usar esses índices de várias maneiras

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

A condição de exemplo é como

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

Em seguida, você pode usar esses índices de linha de várias maneiras, como

  1. Substitua o valor de uma coluna por conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Substituir valor de várias colunas por conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Um benefício ao salvar o índice condicional é que você pode atribuir valor de uma coluna a outra coluna com o mesmo índice de linha
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Isso tudo é possível porque .index retorna uma matriz de índice que .loc pode usar com endereçamento direto, evitando travessias repetidas vezes.

Atta Jutt
fonte
que tal mudar linhas?
FabioSpaghetti
basta usar, df.loc [índice_condicional,] = <novo valor> Ele substituirá o novo valor em todas as colunas de linhas que satisfaçam a condição
Atta Jutt
2

df.loc['c','x']=10 Isso irá alterar o valor de c ª linha e x th coluna.

Sujit Singh
fonte
1

Além das respostas acima, aqui está uma referência comparando diferentes maneiras de adicionar linhas de dados a um quadro de dados já existente. Isso mostra que o uso de ou valor definido é a maneira mais eficiente para grandes quadros de dados (pelo menos para essas condições de teste).

  • Crie um novo quadro de dados para cada linha e ...
    • ... anexá-lo (13,0 s)
    • ... concatenar (13,1 s)
  • Armazene todas as novas linhas em outro contêiner primeiro, converta para o novo dataframe uma vez e acrescente ...
    • container = listas de listas (2.0 s)
    • container = dicionário de listas (1,9 s)
  • Pré-aloque o quadro de dados inteiro, itere sobre novas linhas e todas as colunas e preencha usando
    • ... em (0,6 s)
    • ... valor_conjunto (0,4 s)

Para o teste, foi utilizado um quadro de dados existente compreendendo 100.000 linhas e 1.000 colunas e valores aleatórios de numpy. Para esse quadro de dados, 100 novas linhas foram adicionadas.

Código veja abaixo:

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

Se você deseja alterar os valores não para toda a linha, mas apenas para algumas colunas:

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

A partir da versão 0.21.1, você também pode usar o .atmétodo Existem algumas diferenças em comparação com .locas mencionadas aqui - pandas .at versus .loc , mas é mais rápido na substituição de valor único

andrei deusteanu
fonte
0

Então, sua pergunta para converter NaN em ['x', C] para o valor 10

a resposta é..

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

código alternativo é

df.loc['C':'x']=10
df
Ichsan
fonte
-4

Eu também estava procurando esse tópico e criei uma maneira de iterar através de um DataFrame e atualizá-lo com valores de pesquisa de um segundo DataFrame. Aqui está o meu 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
fonte