Tetapkan nilai untuk sel tertentu dalam panda DataFrame menggunakan indeks

479

Saya telah membuat DataFrame Pandas

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

dan dapatkan ini

    xy
A NaN NaN
B NaN NaN
C NaN NaN


Lalu saya ingin memberikan nilai ke sel tertentu, misalnya untuk baris 'C' dan kolom 'x'. Saya berharap mendapatkan hasil seperti itu:

    xy
A NaN NaN
B NaN NaN
C 10 NaN

dengan kode ini:

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

tapi isinya dfbelum berubah. Lagi-lagi hanya ada NaNdi DataFrame.

Ada saran?

Mitkp
sumber
29
Jangan gunakan 'pengindeksan berantai' ( df['x']['C']), gunakan df.ix['x','C'].
Yariv
3
Urutan akses indeks harus dataframe[column (series)] [row (Series index)]:, sedangkan banyak orang (termasuk saya) lebih terbiasa dengan dataframe[row][column]pesanan. Sebagai seorang programmer Matlab dan R, yang terakhir ini terasa lebih intuitif bagi saya tetapi ternyata itu bukan cara kerja Pandas ..
Zhubarb
1
saya mencoba itu, tetapi saya akhirnya menambahkan nama baris lain x dan nama kolom lain C. Anda harus melakukan baris terlebih dahulu kemudian kolom. so df.ix ['C', 'x'] = 10
Matius
5
Untuk komentar @ Yariv. Peringatan: Mulai dari 0.20.0, pengindeks .ix sudah tidak digunakan lagi, karena pengindeksan .iloc dan .loc yang lebih ketat. pandas.pydata.org/pandas-docs/stable/generated/… . df.at tampak seperti itu bertahan.
jeffhale

Jawaban:

593

Jawaban RukTech ini , df.set_value('C', 'x', 10)adalah jauh dan jauh lebih cepat daripada pilihan saya disarankan di bawah ini. Namun, telah dijadwalkan untuk penghentian .

Ke depan, metode yang disarankan adalah.iat/.at .


Mengapa df.xs('C')['x']=10tidak berfungsi:

df.xs('C')secara default, mengembalikan bingkai data baru dengan salinan data, jadi

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

memodifikasi kerangka data baru ini saja.

df['x']mengembalikan tampilan dari dfkerangka data, jadi

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

memodifikasi dfsendiri.

Peringatan : Terkadang sulit untuk memprediksi apakah suatu operasi mengembalikan salinan atau tampilan. Untuk alasan ini, dokumen merekomendasikan menghindari tugas dengan "pengindeksan berantai" .


Jadi alternatif yang disarankan adalah

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

yang tidak memodifikasi 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
sumber
Tidak ada yang namanya df.xdi API . Apa yang kamu maksud?
smci
3
@smci: 'x'adalah nama kolom di df. df.xmengembalikan a Seriesdengan nilai-nilai di kolom x. Saya akan mengubahnya df['x']karena notasi ini akan berfungsi dengan nama kolom apa pun (tidak seperti notasi titik) dan saya pikir lebih jelas.
unutbu
1
Saya tahu itu, saya pikir Anda katakan df.xadalah beberapa metode baru yang tidak diketahui bersamadf.xs, df.ix
smci
df.xs(..., copy=True)mengembalikan salinan, dan itulah perilaku default. df.xs(..., copy=False)mengembalikan yang asli.
smci
7
Menurut pengelola, ini bukan cara yang disarankan untuk menetapkan nilai. Lihat stackoverflow.com/a/21287235/1579844 dan jawaban saya.
Yariv
225

Pembaruan: .set_valueMetode ini akan ditinggalkan . .iat/.atadalah pengganti yang bagus, sayangnya panda menyediakan sedikit dokumentasi


Cara tercepat untuk melakukan ini adalah menggunakan set_value . Metode ini ~ 100 kali lebih cepat dari .ixmetode. Sebagai contoh:

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

RukTech
sumber
5
Itu bahkan lebih baik daripada df['x']['C'] = 10 .
ALH
6
1000 loop, terbaik 3: 195 μs per loop "df ['x'] ['C'] = 10" 1000 loop, terbaik 3: 310 μs per loop "df.ix ['C', 'x'] = 10 "1000 loop, terbaik 3: 189 µs per loop" df.xs ('C', copy = False) ['x'] = 10 "1000 loop, terbaik dari 3: 7.22 µs per loop" df.set_value ('C', 'x', 10) "
propjk007
1
apakah ini juga berfungsi untuk menambahkan baris / col baru ke dataframe?
st.ph.n
Ya benar (untuk panda 0.16.2)
RukTech
Apakah mungkin untuk menggunakan ini untuk menetapkan nilai ke df=df.append(df.sum(numeric_only=True),ignore_index=True)?
ctrl-alt-delete
95

Anda juga dapat menggunakan pencarian bersyarat menggunakan .locseperti yang terlihat di sini:

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

di mana <some_column_namekolom yang ingin Anda periksa <condition>variabelnya dan <another_column_name>merupakan kolom yang ingin Anda tambahkan (bisa berupa kolom baru atau yang sudah ada). <value_to_add>adalah nilai yang ingin Anda tambahkan ke kolom / baris itu.

Contoh ini tidak bekerja secara tepat dengan pertanyaan yang ada, tetapi mungkin berguna bagi seseorang yang ingin menambahkan nilai tertentu berdasarkan suatu kondisi.

Blairg23
sumber
8
kolom kedua harus dalam tanda kurung, jika tidak semua kolom akan ditimpa dengan nilai. Seperti ini:df.loc[df['age']==3, ['age-group']] = 'toddler'
Piizei
Saya tidak dapat mengaktifkan ini ketika <some_column_name> adalah indeks saya (katakanlah indeks unixtime) dan saya mencoba untuk menambahkan timestamp yang belum keluar (yaitu pembacaan stempel waktu baru). Adakah pikiran?
yeliabsalohcin
Apakah mungkin untuk mengubah nilai berdasarkan indeks dan nilai sel?
BND
@ BND Saya tidak yakin, tetapi Anda bisa mengatasi perangkap yang jelas ini tetapi hanya menduplikasi kolom indeks dengan kolom lain dengan nilai yang sama? Jawaban singkatnya adalah saya tidak tahu.
Blairg23
@ yeliabsalohcin lihat jawaban di atas.
Blairg23
40

Cara yang disarankan (menurut pengelola) untuk menetapkan nilai adalah:

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

Menggunakan 'pengindeksan berantai' ( df['x']['C']) dapat menyebabkan masalah.

Lihat:

Yariv
sumber
7
ixsudah ditinggalkan: pandas-docs.github.io/pandas-docs-travis/…
ecoe
bekerja dengan sempurna! meskipun kadang-kadang akan usang!
Pavlos Ponos
35

Coba gunakan df.loc[row_index,col_indexer] = value

Yash
sumber
6
Selamat Datang di Stack Overflow! Silakan pertimbangkan untuk mengedit posting Anda untuk menambahkan lebih banyak penjelasan tentang apa yang kode Anda lakukan dan mengapa itu akan menyelesaikan masalah. Jawaban yang sebagian besar hanya berisi kode (bahkan jika itu berfungsi) biasanya tidak akan membantu OP untuk memahami masalah mereka. Anda juga disarankan untuk tidak mengirim jawaban jika itu hanya dugaan. Jawaban yang baik akan memiliki alasan yang masuk akal mengapa hal itu bisa menyelesaikan masalah OP.
SuperBiasedMan
22

Ini adalah satu-satunya hal yang berhasil untuk saya!

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

Pelajari lebih lanjut di .loc sini .

Alon Galor
sumber
apakah .locmenggantikan .iat/.at?
Gabriel Fair
1
atMirip dengan itu loc, keduanya menyediakan pencarian berbasis label. Gunakan atjika Anda hanya perlu mendapatkan atau menetapkan nilai tunggal dalam DataFrame atau Seri. Dari padas doc
Rutrus
Bagus ini bekerja untuk saya ketika elemen indeks saya numerik.
Christopher John
Ini tidak berfungsi untuk campuran indeks numerik dan string.
Seanny123
12

.iat/.atadalah solusi yang bagus. Andaikata Anda memiliki data_frame sederhana ini:

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

jika kita ingin memodifikasi nilai sel, kita [0,"A"]dapat menggunakan salah satu dari solusi tersebut:

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

Dan berikut ini adalah contoh lengkap cara menggunakan iatuntuk mendapatkan dan menetapkan nilai sel:

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

y_train sebelumnya:

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

y_train setelah memanggil fungsi prepossessing yang iatakan diubah untuk mengalikan nilai setiap sel dengan 2:

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

Untuk menetapkan nilai, gunakan:

df.at[0, 'clm1'] = 0
  • Metode tercepat yang disarankan untuk pengaturan variabel.
  • set_value, ixtelah ditinggalkan.
  • Tidak ada peringatan, tidak seperti ilocdanloc
Miladiouss
sumber
1
Saya sampai pada kesimpulan yang persis sama .
prosti
6

Anda dapat menggunakan .iloc.

df.iloc[[2], [0]] = 10
Cevik besar
sumber
Metode ini tampaknya tidak mendukung beberapa nilai, misalnya df.iloc[[2:8], [0]] = [2,3,4,5,6,7]metode mana yang df.loc()dilakukan secara asli.
strpeter
1
bekerja dengan sempurna, tanpa peringatan penghinaan!
Pavlos Ponos
6

Dalam contoh saya, saya hanya mengubahnya di sel yang dipilih

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

'result' adalah dataField dengan kolom 'weight'

Grzegorz Brzęczyszczykiewicz
sumber
4

set_value() sudah ditinggalkan.

Mulai dari rilis 0.23.4, Pandas " mengumumkan masa depan " ...

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

Mempertimbangkan saran ini, berikut ini adalah demonstrasi cara menggunakannya:

  • menurut posisi integer baris / kolom

>>> 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
  • dengan label baris / kolom

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

Referensi:

ivanleoncz
sumber
3

Berikut ini adalah ringkasan dari solusi valid yang disediakan oleh semua pengguna, untuk frame data yang diindeks oleh integer dan string.

df.iloc, df.loc dan df.at bekerja untuk kedua jenis frame data, df.iloc hanya bekerja dengan indeks integer baris / kolom, df.loc dan df.at mendukung untuk menetapkan nilai menggunakan nama kolom dan / atau indeks integer .

Ketika indeks yang ditentukan tidak ada, baik df.loc dan df.at akan menambahkan baris / kolom yang baru disisipkan ke bingkai data yang ada, tetapi df.iloc akan menaikkan "IndexError: pengindeksional posisi berada di luar batas". Contoh kerja yang diuji dengan Python 2.7 dan 3.7 adalah sebagai berikut:

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
Niat Baik
sumber
3

Saya menguji dan hasilnya df.set_valuesedikit lebih cepat, tetapi metode resmi df.atsepertinya cara tercepat yang tidak ketinggalan untuk melakukannya.

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)

Perhatikan ini mengatur nilai untuk sel tunggal. Untuk vektor locdan ilocharus menjadi pilihan yang lebih baik karena vektor.

prosti
sumber
3

Salah satu cara untuk menggunakan indeks dengan kondisi adalah pertama-tama dapatkan indeks dari semua baris yang memenuhi kondisi Anda dan kemudian cukup gunakan indeks baris itu dalam berbagai cara

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

Contoh kondisi seperti

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

Kemudian Anda dapat menggunakan indeks baris ini dalam berbagai cara seperti

  1. Ganti nilai satu kolom untuk conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. Ganti nilai beberapa kolom untuk conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
  1. Satu manfaat dengan menyimpan conditional_index adalah Anda dapat menetapkan nilai satu kolom ke kolom lain dengan indeks baris yang sama
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

Ini semua dimungkinkan karena .index mengembalikan array indeks yang .loc dapat digunakan dengan pengalamatan langsung sehingga menghindari traversal lagi dan lagi.

Atta Jutt
sumber
bagaimana dengan mengubah baris?
FabioSpaghetti
cukup gunakan, df.loc [conditional_index,] = <nilai baru> Ini akan menggantikan nilai baru di semua kolom baris yang memenuhi kondisi
Atta Jutt
2

df.loc['c','x']=10 Ini akan mengubah nilai dari baris ke- c dan kolom ke- x .

Sujit Singh
sumber
1

Selain jawaban di atas, berikut ini adalah tolok ukur yang membandingkan berbagai cara untuk menambahkan baris data ke kerangka data yang sudah ada. Ini menunjukkan bahwa menggunakan at atau set-value adalah cara yang paling efisien untuk kerangka data yang besar (setidaknya untuk kondisi pengujian ini).

  • Buat kerangka data baru untuk setiap baris dan ...
    • ... tambahkan (13.0 dtk)
    • ... menyatukannya (13.1 dtk)
  • Simpan semua baris baru dalam wadah lain terlebih dahulu, konversi ke kerangka data baru satu kali dan tambahkan ...
    • container = daftar daftar (2.0 dtk)
    • container = kamus daftar (1.9 s)
  • Preallocate seluruh dataframe, iterate di atas baris baru dan semua kolom dan isi menggunakan
    • ... at (0,6 dtk)
    • ... set_value (0,4 s)

Untuk pengujian, digunakan kerangka data yang terdiri dari 100.000 baris dan 1.000 kolom dan nilai numpy acak. Untuk kerangka data ini, 100 baris baru ditambahkan.

Kode lihat di bawah:

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

Jika Anda ingin mengubah nilai bukan untuk seluruh baris, tetapi hanya untuk beberapa kolom:

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

Dari versi 0.21.1 Anda juga dapat menggunakan .atmetode. Ada beberapa perbedaan dibandingkan dengan yang .locdisebutkan di sini - panda. Pada versus .loc , tetapi lebih cepat pada penggantian nilai tunggal

andrei deusteanu
sumber
0

Soo, pertanyaan Anda untuk mengonversi NaN di ['x', C] ke nilai 10

jawabannya adalah..

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

kode alternatif adalah

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

Saya juga sedang mencari topik ini dan saya menyusun cara untuk beralih melalui DataFrame dan memperbaruinya dengan nilai pencarian dari DataFrame kedua. Ini kode saya.

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
sumber