web-dev-qa-db-fra.com

Définir la valeur d'une cellule particulière dans Pandas DataFrame en utilisant index


J'ai créé un DataFrame Pandas

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

et obtenu ceci

 x y 
 A NaN NaN 
 B NaN NaN 
 C NaN NaN 


Ensuite, je souhaite attribuer une valeur à une cellule particulière, par exemple pour la ligne 'C' et la colonne 'x'

 x y 
 A NaN NaN 
 B NaN NaN 
 C 10 NaN 

avec ce code:

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

mais le contenu de df n'ont pas changé. Encore une fois, il n'y a que Nan dans le cadre de données. 

Aucune suggestion?

289
Mitkp

La réponse de RukTech , df.set_value('C', 'x', 10), est bien plus rapide que les options que j'ai suggérées ci-dessous. Cependant, il a été prévu pour dépréciation .

À l’avenir, la méthode recommandée par est .iat/.at .


Pourquoi df.xs('C')['x']=10 ne fonctionne pas:

df.xs('C') par défaut, retourne une nouvelle image avec une copie des données, donc 

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

modifie uniquement ce nouveau cadre de données.

df['x'] renvoie une vue du df dataframe, donc 

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

modifie df lui-même.

Avertissement: Il est parfois difficile de prédire si une opération renvoie une copie ou une vue. Pour cette raison, les docs recommandent d'éviter les affectations avec "indexation en chaîne"


Donc, l'alternative recommandée est

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

quel fait modifier 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
359
unutbu

Vous pouvez également utiliser une recherche conditionnelle à l'aide de .loc, comme indiqué ici:

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

<some_column_name est la colonne sur laquelle vous souhaitez vérifier la variable <condition> et <another_column_name> est la colonne à laquelle vous souhaitez ajouter (peut être une nouvelle colonne ou une colonne existante). <value_to_add> est la valeur que vous souhaitez ajouter à cette colonne/ligne.

Cet exemple ne fonctionne pas précisément avec la question en question, mais il peut être utile si quelqu'un souhaite ajouter une valeur spécifique en fonction d'une condition.

65
Blairg23

La méthode recommandée (selon les responsables) pour définir une valeur est la suivante:

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

L'utilisation de l'indexation chaînée (df['x']['C']) peut entraîner des problèmes.

Voir:

29
Yariv

Essayez d'utiliser df.loc[row_index,col_indexer] = value

16
Yash

C'est la seule chose qui a fonctionné pour moi!

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

En savoir plus sur .locici .

15
Alon Galor

vous pouvez utiliser .iloc.

df.iloc[[2], [0]] = 10
4
Muge Cevik

Dans mon exemple, je viens de le changer dans la cellule sélectionnée

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

'result' est un champ de données avec la colonne 'weight'

.iat/.at est la bonne solution. En supposant que vous ayez ce simple data_frame:

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

si nous voulons modifier la valeur de la cellule [0,"A"], vous pouvez utiliser l'une de ces solutions:

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

Et voici un exemple complet sur l'utilisation de iat pour obtenir et définir une valeur de cellule:

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

y_train avant:

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

y_train après l'appel de la fonction de pré-acquisition que iat doit modifier pour multiplier la valeur de chaque cellule par 2

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

df.loc['c','x']=10 Cela changera la valeur de c th rangée et x ème colonne.

1
Sujit Singh

Pour définir des valeurs, utilisez:

df.at[0, 'clm1'] = 0
  • La méthode recommandée la plus rapide pour définir des variables.
  • set_value, ix sont obsolètes.
  • Pas d'avertissement, contrairement à iloc et loc
1
Miladiouss

Voici un résumé des solutions valides fournies par tous les utilisateurs, pour les trames de données indexées par un entier et une chaîne.

df.iloc, df.loc et df.at fonctionnent pour les deux types de trames de données, df.iloc ne fonctionne qu'avec des index entiers de ligne/colonne, df.loc et df.at prennent en charge le paramétrage de valeurs à l'aide de noms de colonne et/ou d'indices d'entiers .

Lorsque l'index spécifié n'existe pas, df.loc et df.at ajouteraient les lignes/colonnes récemment insérées au cadre de données existant, mais df.iloc déclencherait "IndexError: les indexeurs de position sont en dehors des limites". Voici un exemple de travail testé en Python 2.7 et 3.7:

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
1
Good Will

set_value() est obsolète.

À partir de la version 0.23.4, Pandas "annonce le futur} _" ...

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

Compte tenu de ces conseils, voici une démonstration de leur utilisation:

  • par rangées/positions entières de colonnes

>>> 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
  • par étiquettes de ligne/colonne

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

Références:

0
ivanleoncz

Outre les réponses ci-dessus, voici un point de repère comparant différentes manières d’ajouter des lignes de données à une trame de données existante. Il montre que l'utilisation de ou de la valeur définie est le moyen le plus efficace pour les grandes trames de données (au moins pour ces conditions de test).

  • Créez une nouvelle base de données pour chaque ligne et ...
    • ... l'ajouter (13.0 s)
    • ... concaténez-le (13.1 s)
  • Stockez d’abord toutes les nouvelles lignes dans un autre conteneur, convertissez-les une nouvelle fois en une nouvelle image de données et ajoutez-les ...
    • conteneur = listes de listes (2.0 s)
    • conteneur = dictionnaire de listes (1.9 s)
  • Préaffectez toute la trame de données, parcourez de nouvelles lignes et toutes les colonnes, puis remplissez-les à l'aide de
    • ... à (0,6 s)
    • ... set_value (0.4 s)

Pour le test, une base de données existante comprenant 100 000 lignes et 1 000 colonnes et des valeurs numériques aléatoires a été utilisée. À cette base de données, 100 nouvelles lignes ont été ajoutées. 

Code voir ci-dessous:

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

Si vous souhaitez modifier les valeurs non pour la ligne entière, mais uniquement pour certaines colonnes:

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

J'ai testé et la sortie est df.set_value est un peu plus rapide, mais la méthode officielle df.at ressemble à la façon la plus rapide non dépréciée de le faire.

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)

Notez que vous définissez la valeur pour une seule cellule. Pour les vecteurs loc et iloc devraient être de meilleures options car ils sont vectorisés.

0
prosti

Depuis la version 0.21.1, vous pouvez également utiliser la méthode .at. Il y a quelques différences par rapport à .loc comme mentionné ici - pandas .at versus .loc , mais c'est plus rapide pour le remplacement à valeur unique

0
andrei deusteanu