Legen Sie den Wert für eine bestimmte Zelle in Pandas DataFrame mithilfe des Index fest


478

Ich habe einen Pandas DataFrame erstellt

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

und habe das bekommen

    xy
Ein NaN NaN
B NaN NaN
C NaN NaN


Dann möchte ich einer bestimmten Zelle einen Wert zuweisen, zum Beispiel für Zeile 'C' und Spalte 'x'. Ich habe erwartet, ein solches Ergebnis zu erzielen:

    xy
Ein NaN NaN
B NaN NaN
C 10 NaN

mit diesem Code:

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

aber der Inhalt von dfhat sich nicht geändert. Es ist wieder nur NaNs in DataFrame.

Irgendwelche Vorschläge?


29
Verwenden Sie nicht 'Chained Indexing' ( df['x']['C']), sondern df.ix['x','C'].
Yariv

3
Die Reihenfolge des Indexzugriffs muss lauten: dataframe[column (series)] [row (Series index)]Während viele Personen (einschließlich ich) eher an die dataframe[row][column]Reihenfolge gewöhnt sind . Als Matlab- und R-Programmierer fühlt sich Letzteres für mich intuitiver an, aber anscheinend funktioniert Pandas nicht so.
Zhubarb

1
Ich habe das versucht, aber am Ende habe ich weitere Zeilennamen x und weitere Spaltennamen C hinzugefügt. Sie müssen zuerst die Zeile und dann die Spalte erstellen. also df.ix ['C', 'x'] = 10
Matthew

5
Zu @ Yarivs Kommentar. Warnung: Ab 0.20.0 ist der .ix-Indexer zugunsten der strengeren .iloc- und .loc-Indexer veraltet. pandas.pydata.org/pandas-docs/stable/generated/… . df.at sieht so aus, als ob es hier bleibt.
Jeffhale

Antworten:


592

RukTech Antwort , df.set_value('C', 'x', 10)ist bei weitem schneller als die Optionen , die ich unten vorgeschlagen habe. Es ist jedoch für die Abschreibung vorgesehen .

In Zukunft ist die empfohlene Methode.iat/.at .


Warum df.xs('C')['x']=10funktioniert das nicht:

df.xs('C')Gibt standardmäßig einen neuen Datenrahmen mit einer Kopie der Daten zurück

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

Ändert nur diesen neuen Datenrahmen.

df['x'] gibt eine Ansicht des zurück dfGibt Datenrahmens zurück

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

ändert dfsich.

Warnung : Es ist manchmal schwierig vorherzusagen, ob ein Vorgang eine Kopie oder eine Ansicht zurückgibt. Aus diesem Grund ist die empfehlen Dokumente, Zuweisungen mit "verketteter Indizierung" zu vermeiden .


Die empfohlene Alternative ist also

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

das nicht ändern 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

Es gibt nicht so etwas wie df.xin der API . Was hast du gemeint?
smci

3
@smci: 'x'ist der Name einer Spalte in df. df.xGibt a Seriesmit den Werten in der Spalte zurück x. Ich werde es ändern, df['x']da diese Notation mit jedem Spaltennamen funktioniert (im Gegensatz zur Punktnotation) und ich denke, ist klarer.
Unutbu

1
Ich wusste das, ich dachte, Sie sagten, es df.xsei eine unbekannte neue Methode nebendf.xs, df.ix
smci

df.xs(..., copy=True)gibt eine Kopie zurück, und das ist das Standardverhalten. df.xs(..., copy=False)gibt das Original zurück.
smci

7
Laut den Betreuern ist dies nicht die empfohlene Methode zum Festlegen eines Werts. Siehe stackoverflow.com/a/21287235/1579844 und meine Antwort.
Yariv

225

Update: Die .set_valueMethode wird veraltet sein . .iat/.atsind gute ersetzungen, leider liefert pandas wenig dokumentation


Der schnellste Weg, dies zu tun, ist die Verwendung von set_value . Diese Methode ist ~ 100-mal schneller als die .ixMethode. Zum Beispiel:

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


5
Es ist noch besser als df['x']['C'] = 10 .
ALH

6
1000 Schleifen, am besten 3: 195 µs pro Schleife "df ['x'] ['C'] = 10" 1000 Schleifen, am besten 3: 310 µs pro Schleife "df.ix ['C', 'x'] = 10 "1000 Schleifen, am besten 3: 189 µs pro Schleife" df.xs ('C', copy = False) ['x'] = 10 "1000 Schleifen, am besten 3: 7,22 µs pro Schleife" df.set_value ('C', 'x', 10) "
propjk007

1
Funktioniert dies auch zum Hinzufügen einer neuen Zeile / Spalte zum Datenrahmen?
st.ph.n

Ja, das tut es (für Pandas 0.16.2)
RukTech

Ist es möglich, damit einen Wert auf a zu setzen df=df.append(df.sum(numeric_only=True),ignore_index=True)?
Strg-Alt-Löschen

95

Sie können auch eine bedingte Suche verwenden, indem Sie Folgendes verwenden .loc:

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

Wo <some_column_nameist die Spalte, mit der Sie die <condition>Variable vergleichen möchten, und wo ist die Spalte, zu der <another_column_name>Sie hinzufügen möchten (kann eine neue oder bereits vorhandene Spalte sein).<value_to_add>ist der Wert, den Sie dieser Spalte / Zeile hinzufügen möchten.

Dieses Beispiel funktioniert nicht genau mit der vorliegenden Frage, aber es kann nützlich sein, wenn jemand einen bestimmten Wert basierend auf einer Bedingung hinzufügen möchte.


8
Die zweite Spalte muss in Klammern stehen, sonst werden alle Spalten mit Wert überschrieben. So:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei

Ich kann dies nicht zum Laufen bringen, wenn <some_column_name> mein Index ist (sagen wir Unixtime-Index) und ich versuche, einen Zeitstempel hinzuzufügen, der noch nicht beendet wird (dh einen neuen Zeitstempelwert). Irgendwelche Gedanken?
Yeliabsalohcin

Ist es möglich, einen Wert basierend auf dem Index und den Zellenwerten zu ändern?
BND

@BND Ich bin mir nicht sicher, aber Sie könnten diese offensichtliche Gefahr umgehen, indem Sie einfach die Indexspalte mit einer anderen Spalte mit demselben Wert duplizieren? Die kurze Antwort lautet: Ich weiß es nicht.
Blairg23

@yeliabsalohcin siehe obige Antwort.
Blairg23

40

Die empfohlene Methode (laut den Betreuern) zum Festlegen eines Werts lautet:

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

Verwenden der verketteten Indizierung (df['x']['C'] ) kann zu Problemen führen.

Sehen:



funktioniert perfekt! obwohl es irgendwann veraltet sein wird!
Pavlos Ponos

35

Versuchen Sie es mit df.loc[row_index,col_indexer] = value


6
Willkommen bei Stack Overflow! Bitte überlegen Sie, Ihren Beitrag zu bearbeiten, um weitere Erklärungen darüber hinzuzufügen, was Ihr Code tut und warum er das Problem löst. Eine Antwort, die meistens nur Code enthält (auch wenn sie funktioniert), hilft dem OP normalerweise nicht, ihr Problem zu verstehen. Es wird auch empfohlen, keine Antwort zu posten, wenn es sich nur um eine Vermutung handelt. Eine gute Antwort hat einen plausiblen Grund, warum sie das Problem des OP lösen könnte.
SuperBiasedMan

22

Dies ist das einzige, was für mich funktioniert hat!

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

Erfahren Sie mehr über .loc hier .


hat .locersetzen .iat/.at?
Gabriel Fair

1
atÄhnlich wie locbei beiden bieten sie markenbasierte Suchvorgänge. Verwenden atSie diese Option, wenn Sie nur einen einzelnen Wert in einem DataFrame oder einer Serie abrufen oder festlegen müssen. Von Padas Doc
Rutrus

Schön, dass das bei mir funktioniert hat, als meine Indexelemente numerisch waren.
Christopher John

Dies funktioniert nicht für eine Mischung aus numerischen und Zeichenfolgenindizes.
Seanny123

12

.iat/.atist die gute Lösung. Angenommen, Sie haben diesen einfachen Datenrahmen:

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

Wenn wir den Wert der Zelle ändern möchten, [0,"A"]können Sie eine der folgenden Lösungen verwenden:

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

Und hier ist ein vollständiges Beispiel, wie Sie einen Zellwert iatabrufen und festlegen können:

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

y_train vorher:

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

y_train nach dem Aufruf der Vorbesitzfunktion, iatdie geändert werden muss, um den Wert jeder Zelle mit 2 zu multiplizieren:

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

8

Verwenden Sie zum Festlegen von Werten:

df.at[0, 'clm1'] = 0
  • Die am schnellsten empfohlene Methode zum Festlegen von Variablen.
  • set_value, ix Sind veraltet.
  • Keine Warnung, im Gegensatz zu ilocundloc

1
Ich bin zu genau dem gleichen Schluss gekommen .
Prosti

6

Sie können verwenden .iloc.

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

Diese Methode scheint nicht mehrere Werte zu unterstützen, z. B. df.iloc[[2:8], [0]] = [2,3,4,5,6,7]was die Methode df.loc()nativ tut.
Strpeter

1
funktioniert perfekt, ohne Abwertungswarnung!
Pavlos Ponos

6

In meinem Beispiel ändere ich es einfach in der ausgewählten Zelle

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

'result' ist ein dataField mit der Spalte 'weight'


4

set_value() ist veraltet.

Ab der Veröffentlichung 0.23.4 kündigt Pandas " die Zukunft an " ...

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

In Anbetracht dieses Ratschlags finden Sie hier eine Demonstration ihrer Verwendung:

  • nach ganzzahligen Zeilen- / Spaltenpositionen

>>> 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
  • nach Zeilen- / Spaltenbezeichnungen

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

Verweise:


3

Hier ist eine Zusammenfassung der gültigen Lösungen, die von allen Benutzern für Datenrahmen bereitgestellt werden, die durch Ganzzahl und Zeichenfolge indiziert sind.

df.iloc, df.loc und df.at funktionieren für beide Arten von Datenrahmen, df.iloc funktioniert nur mit Zeilen- / Spalten-Ganzzahlindizes, df.loc und df.at unterstützen das Festlegen von Werten mithilfe von Spaltennamen und / oder Ganzzahlindizes .

Wenn der angegebene Index nicht vorhanden ist, würden sowohl df.loc als auch df.at die neu eingefügten Zeilen / Spalten an den vorhandenen Datenrahmen anhängen, aber df.iloc würde "IndexError: Positionsindexer sind außerhalb der Grenzen" auslösen. Ein in Python 2.7 und 3.7 getestetes Arbeitsbeispiel lautet wie folgt:

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

Ich habe getestet und die Ausgabe ist df.set_valueetwas schneller, aber die offizielle Methode df.atscheint die schnellste nicht veraltete Methode zu sein.

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)

Beachten Sie, dass hiermit der Wert für eine einzelne Zelle festgelegt wird. Für die Vektoren locund ilocsollten bessere Optionen sein, da sie vektorisiert sind.


3

Eine Möglichkeit, den Index mit Bedingung zu verwenden, besteht darin, zuerst den Index aller Zeilen abzurufen, die Ihre Bedingung erfüllen, und diese Zeilenindizes dann einfach auf verschiedene Arten zu verwenden

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

Beispielbedingung ist wie

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

Dann können Sie diese Zeilenindizes auf verschiedene Arten verwenden, wie z

  1. Ersetzen Sie den Wert einer Spalte für conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Ersetzen Sie den Wert mehrerer Spalten für conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Ein Vorteil beim Speichern des bedingten Index ist, dass Sie den Wert einer Spalte einer anderen Spalte mit demselben Zeilenindex zuweisen können
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Dies ist alles möglich, da .index ein Indexarray zurückgibt, das .loc für die direkte Adressierung verwenden kann, um das Durchlaufen immer wieder zu vermeiden.


Was ist mit Zeilenwechsel?
FabioSpaghetti

Verwenden Sie einfach df.loc [conditional_index,] = <neuer Wert> Er ersetzt den neuen Wert in allen Spalten von Zeilen, die die Bedingung erfüllen
Atta Jutt

2

df.loc['c','x']=10 Dies ändert den Wert der c- ten Zeile und der x- ten Spalte.


1

Zusätzlich zu den obigen Antworten finden Sie hier einen Benchmark, in dem verschiedene Möglichkeiten zum Hinzufügen von Datenzeilen zu einem bereits vorhandenen Datenrahmen verglichen werden. Es zeigt, dass die Verwendung von at oder set-value der effizienteste Weg für große Datenrahmen ist (zumindest für diese Testbedingungen).

  • Erstellen Sie einen neuen Datenrahmen für jede Zeile und ...
    • ... anhängen (13.0 s)
    • ... verketten (13,1 s)
  • Speichern Sie zuerst alle neuen Zeilen in einem anderen Container, konvertieren Sie sie einmal in einen neuen Datenrahmen und fügen Sie ...
    • Container = Listen von Listen (2,0 s)
    • Container = Wörterbuch der Listen (1,9 s)
  • Ordnen Sie den gesamten Datenrahmen vorab zu, durchlaufen Sie neue Zeilen und alle Spalten und füllen Sie ihn mit
    • ... um (0,6 s)
    • ... set_value (0,4 s)

Für den Test wurde ein vorhandener Datenrahmen mit 100.000 Zeilen und 1.000 Spalten sowie zufälligen Zahlenwerten verwendet. Zu diesem Datenrahmen wurden 100 neue Zeilen hinzugefügt.

Code siehe unten:

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

Wenn Sie Werte nicht für die gesamte Zeile, sondern nur für einige Spalten ändern möchten:

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


0

Also, Ihre Frage, NaN bei ['x', C] in den Wert 10 umzuwandeln

die Antwort ist..

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

alternativer Code ist

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

-4

Auch ich habe nach diesem Thema gesucht und eine Möglichkeit zusammengestellt, einen DataFrame zu durchlaufen und ihn mit Suchwerten aus einem zweiten DataFrame zu aktualisieren. Hier ist mein Code.

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'])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.