Ustaw wartość dla konkretnej komórki w pandach DataFrame za pomocą indeksu


477

Stworzyłem Pandas DataFrame

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

i mam to

    xy
NaN NaN
B NaN NaN
C NaN NaN


Następnie chcę przypisać wartość do konkretnej komórki, na przykład dla wiersza „C” i kolumny „x”. Spodziewałem się takiego wyniku:

    xy
NaN NaN
B NaN NaN
C 10 NaN

z tym kodem:

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

ale zawartość dfsię nie zmieniła. Znowu jest tylko NaNw DataFrame.

Jakieś sugestie?


29
Nie używaj „indeksowania łańcuchowego” ( df['x']['C']), użyj df.ix['x','C'].
Yariv

3
Kolejność dostępu do indeksu musi być następująca: dataframe[column (series)] [row (Series index)]podczas gdy wiele osób (w tym ja) jest bardziej przyzwyczajonych do tej dataframe[row][column]kolejności. Jako programista Matlab i R. ten ostatni wydaje mi się bardziej intuicyjny, ale najwyraźniej nie tak działa Pandas ..
Zhubarb

1
próbowałem tego, ale ostatecznie dodałem kolejne nazwy wierszy x i kolejne nazwy kolumn C. musisz najpierw wykonać wiersz, a następnie kolumnę. więc df.ix ['C', 'x'] = 10
Matthew

5
Do komentarza @ Yariv. Ostrzeżenie: Począwszy od wersji 0.20.0 indeksator .ix jest przestarzały, na rzecz bardziej rygorystycznych indeksatorów .iloc i .loc. pandas.pydata.org/pandas-docs/stable/generated/… . Wygląda na to, że się trzyma.
jeffhale

Odpowiedzi:


592

Odpowiedź RukTech za , df.set_value('C', 'x', 10)jest daleko i szybciej niż opcje mam sugerowanych poniżej. Zostało jednak przeznaczone na wycofanie .

W przyszłości zalecaną metodą jest.iat/.at .


Dlaczego df.xs('C')['x']=10nie działa:

df.xs('C')domyślnie zwraca nową ramkę danych z kopią danych, więc

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

modyfikuje tylko tę nową ramkę danych.

df['x']zwraca widok dframki danych, więc

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

modyfikuje dfsię.

Ostrzeżenie : Czasami trudno jest przewidzieć, czy operacja zwraca kopię lub widok. Z tego powodu doktorzy zalecają unikanie zadań z „indeksowaniem łańcuchowym” .


Tak więc zalecaną alternatywą jest

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

który się modyfikuje 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

Nie ma czegoś takiego jak df.xw API . Co miałeś na myśli?
smci

3
@smci: 'x'to nazwa kolumny w df. df.xzwraca a Seriesz wartościami w kolumnie x. Zmienię to na, df['x']ponieważ ta notacja będzie działać z dowolną nazwą kolumny (w przeciwieństwie do notacji kropkowej) i myślę, że jest jaśniejsza.
unutbu

1
Wiedziałem o tym, myślałem, że mówisz, że df.xobok jest jakaś nieznana nowa metodadf.xs, df.ix
smci

df.xs(..., copy=True)zwraca kopię, a to jest zachowanie domyślne. df.xs(..., copy=False)zwraca oryginał.
smci

7
Według opiekunów nie jest to zalecany sposób ustawiania wartości. Zobacz stackoverflow.com/a/21287235/1579844 i moją odpowiedź.
Yariv

224

Aktualizacja: .set_value metoda będzie przestarzała . .iat/.atsą dobrymi zamiennikami, niestety pandy dostarczają mało dokumentacji


Najszybszym sposobem na to jest użycie set_value . Ta metoda jest ~ 100 razy szybsza niż .ixmetoda. Na przykład:

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


5
To nawet lepsze niż df['x']['C'] = 10 .
ALH

6
1000 pętli, najlepiej 3: 195 µs na pętlę „df ['x'] ['C'] = 10” 1000 pętli, najlepiej 3: 310 µs na pętlę "df.ix ['C', 'x'] = 10 „1000 pętli, najlepiej 3: 189 µs na pętlę” df.xs („C”, kopia = Fałsz) [”x”] = 10 „1000 pętli, najlepiej 3: 7,22 µs na pętlę” df.set_value („C”, „x”, 10) ”
propjk007

1
czy to działa również w przypadku dodawania nowego wiersza / kolumny do ramki danych?
st.ph.n

Tak, robi (dla pand 0.16.2)
RukTech

Czy można tego użyć, aby ustawić wartość na df=df.append(df.sum(numeric_only=True),ignore_index=True)?
ctrl-alt-delete

94

Możesz także skorzystać z wyszukiwania warunkowego, korzystając z .locponiższego przykładu:

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

gdzie <some_column_namejest kolumna, dla której chcesz sprawdzić <condition>zmienną względem i<another_column_name> jest kolumna, do której chcesz dodać (może to być nowa kolumna lub kolumna, która już istnieje). <value_to_add>to wartość, którą chcesz dodać do tej kolumny / wiersza.

Ten przykład nie działa dokładnie z danym pytaniem, ale może być przydatny dla kogoś, kto chce dodać konkretną wartość na podstawie warunku.


8
druga kolumna musi znajdować się w nawiasach, w przeciwnym razie wszystkie kolumny zostaną zastąpione wartością. W ten sposób:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei,

Nie mogę tego uruchomić, gdy <some_column_name> jest moim indeksem (powiedzmy indeksem unixtime) i próbuję dodać znacznik czasu, który jeszcze nie wychodzi (tj. Nowy odczyt znacznika czasu). jakieś pomysły?
yeliabsalohcin

Czy można zmienić wartość na podstawie wartości indeksu i komórki?
BND

@BND Nie jestem pewien, ale można obejść ten oczywisty pułapkę, ale po prostu powielając kolumnę indeksu z inną kolumną o tej samej wartości? Krótka odpowiedź brzmi: nie wiem.
Blairg23

@yeliabsalohcin patrz wyżej odpowiedź.
Blairg23,

40

Zalecanym sposobem (według opiekunów) ustawienia wartości jest:

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

Korzystanie z „indeksowania łańcuchowego” ( df['x']['C']) może powodować problemy.

Widzieć:



działa idealnie! chociaż kiedyś będzie przestarzałe!
Pavlos Ponos

35

Spróbuj użyć df.loc[row_index,col_indexer] = value


6
Witamy w Stack Overflow! Rozważ edycję swojego posta, aby dodać więcej wyjaśnień na temat tego, co robi Twój kod i dlaczego rozwiąże problem. Odpowiedź, która w większości zawiera tylko kod (nawet jeśli działa), zwykle nie pomaga OP zrozumieć jego problemu. Zaleca się również, aby nie zamieszczać odpowiedzi, jeśli jest to tylko przypuszczenie. Dobra odpowiedź będzie miała uzasadniony powód, dla którego mogłaby rozwiązać problem PO.
SuperBiasedMan

22

To jedyna rzecz, która działała dla mnie!

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

Dowiedz się więcej o .loc tutaj .


nie .loczastąpi .iat/.at?
Gabriel Fair

1
atPodobne do tego loc, że oba zapewniają wyszukiwania oparte na etykietach. Użyj, atjeśli potrzebujesz tylko uzyskać lub ustawić pojedynczą wartość w ramce danych lub serii. Z padas doc
Rutrus

Fajnie, że działało to dla mnie, gdy moje elementy indeksu były numeryczne.
Christopher John

Nie działa to w przypadku kombinacji wskaźników liczbowych i indeksów ciągów.
Seanny123

12

.iat/.atjest dobrym rozwiązaniem. Załóżmy, że masz tę prostą ramkę danych:

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

jeśli chcemy zmodyfikować wartość komórki, [0,"A"]możemy użyć jednego z tych rozwiązań:

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

A oto kompletny przykład, jak użyć, iataby uzyskać i ustawić wartość komórki:

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

y_train przed:

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

y_train po wywołaniu funkcji prepossessing, iataby zmienić, aby pomnożyć wartość każdej komórki przez 2:

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

8

Aby ustawić wartości, użyj:

df.at[0, 'clm1'] = 0
  • Najszybsza zalecana metoda ustawiania zmiennych.
  • set_value, ix Były przestarzałe.
  • Bez ostrzeżenia, w przeciwieństwie do ilociloc

1
Doszedłem do dokładnie tego samego wniosku .
prosti

6

możesz użyć .iloc.

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

Wydaje się, że ta metoda nie obsługuje kilku wartości, np. df.iloc[[2:8], [0]] = [2,3,4,5,6,7]Które metoda df.loc()natywnie obsługuje.
strpeter

1
działa idealnie, bez ostrzeżenia o wycofaniu!
Pavlos Ponos

6

W moim przykładzie po prostu zmieniam to w wybranej komórce

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

„wynik” to pole danych z kolumną „waga”


4

set_value() jest przestarzałe.

Począwszy od wydania 0.23.4, Pandas „ ogłasza przyszłość ” ...

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

Biorąc pod uwagę tę radę, oto pokaz, jak z nich korzystać:

  • według pozycji liczb całkowitych wierszy / kolumn

>>> 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
  • według etykiet wierszy / kolumn

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

Bibliografia:


3

Oto podsumowanie prawidłowych rozwiązań dostarczonych przez wszystkich użytkowników dla ramek danych indeksowanych według liczb całkowitych i ciągu.

df.iloc, df.loc i df.at działają dla obu typów ramek danych, df.iloc działa tylko z indeksami liczb całkowitych wierszy / kolumn, df.loc i df.at obsługuje ustawianie wartości przy użyciu nazw kolumn i / lub indeksów liczb całkowitych .

Gdy określony indeks nie istnieje, zarówno df.loc, jak i df.at dołączałyby nowo wstawione wiersze / kolumny do istniejącej ramki danych, ale df.iloc spowodowałoby błąd „IndexError: indeksatory pozycyjne są poza zakresem”. Przykład działania przetestowany w Pythonie 2.7 i 3.7 jest następujący:

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

Testowałem, a wynik jest df.set_valuenieco szybszy, ale oficjalna metoda df.atwygląda na najszybszy nieaktualny sposób na zrobienie tego.

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)

Zauważ, że to ustawia wartość dla pojedynczej komórki. Dla wektorów loci ilocpowinny być lepszymi opcjami, ponieważ są wektoryzowane.


3

Jednym ze sposobów użycia indeksu z warunkiem jest najpierw uzyskanie indeksu wszystkich wierszy spełniających warunek, a następnie po prostu użycie tych indeksów wierszy na wiele sposobów

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

Przykładowy warunek jest podobny

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

Następnie możesz użyć tych indeksów wierszy na różne sposoby, np

  1. Zamień wartość jednej kolumny na indeks warunkowy
df.loc[conditional_index , [col name]]= <new value>
  1. Zamień wartość wielu kolumn na indeks warunkowy
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Jedną korzyścią z zapisania indeksu warunkowego jest to, że można przypisać wartość jednej kolumny do innej kolumny z tym samym indeksem wiersza
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Jest to możliwe, ponieważ .index zwraca tablicę indeksu, z której .loc może korzystać przy bezpośrednim adresowaniu, dzięki czemu unika ciągłego przechodzenia.


co ze zmianą wierszy?
FabioSpaghetti,

wystarczy użyć, df.loc [conditional_index,] = <nowa wartość> Zastąpi nową wartość we wszystkich kolumnach wierszy, które spełniają warunek
Atta Jutt

2

df.loc['c','x']=10 Spowoduje to zmianę wartości c- tego wiersza i x- tej kolumny.


1

Oprócz powyższych odpowiedzi, tutaj jest test porównawczy różnych sposobów dodawania wierszy danych do już istniejącej ramki danych. Pokazuje, że użycie wartości at lub set-value jest najbardziej wydajnym sposobem dla dużych ramek danych (przynajmniej w tych warunkach testowych).

  • Utwórz nową ramkę danych dla każdego wiersza i ...
    • ... dodaj go (13,0 s)
    • ... połącz to (13,1 s)
  • Najpierw przechowaj wszystkie nowe wiersze w innym kontenerze, raz przekonwertuj na nową ramkę danych i dołącz ...
    • kontener = listy list (2,0 s)
    • kontener = słownik list (1,9 s)
  • Prealokuj całą ramkę danych, iteruj po nowych wierszach i wszystkich kolumnach i wypełniaj za pomocą
    • ... przy (0,6 s)
    • ... set_value (0.4 s)

Do testu wykorzystano istniejącą ramkę danych zawierającą 100 000 wierszy i 1000 kolumn oraz losowe wartości liczbowe. Do tej ramki danych dodano 100 nowych wierszy.

Kod patrz poniżej:

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

Jeśli chcesz zmienić wartości nie dla całego wiersza, ale tylko dla niektórych kolumn:

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


0

Tak, twoje pytanie o konwersję NaN w ['x', C] na wartość 10

odpowiedź to..

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

alternatywny kod to

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

-4

Ja również szukałem tego tematu i opracowałem sposób na iterację w DataFrame i zaktualizowałem go o wartości wyszukiwania z drugiej DataFrame. Oto mój kod.

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'])
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.