Mengapa panda saya 'berlaku' fungsi referensi beberapa kolom berfungsi? [Tutup]

239

Saya memiliki beberapa masalah dengan fungsi berlaku Pandas, ketika menggunakan beberapa kolom dengan kerangka data berikut

df = DataFrame ({'a' : np.random.randn(6),
                 'b' : ['foo', 'bar'] * 3,
                 'c' : np.random.randn(6)})

dan fungsi berikut

def my_test(a, b):
    return a % b

Ketika saya mencoba menerapkan fungsi ini dengan:

df['Value'] = df.apply(lambda row: my_test(row[a], row[c]), axis=1)

Saya mendapatkan pesan kesalahan:

NameError: ("global name 'a' is not defined", u'occurred at index 0')

Saya tidak mengerti pesan ini, saya mendefinisikan nama dengan benar.

Saya akan sangat menghargai bantuan apa pun tentang masalah ini

Memperbarui

Terima kasih atas bantuan Anda. Saya memang membuat beberapa kesalahan sintaks dengan kode, indeks harus diletakkan ''. Namun saya masih mendapatkan masalah yang sama menggunakan fungsi yang lebih kompleks seperti:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 
Andy
sumber
1
Hindari menggunakan applysebanyak mungkin. Jika Anda tidak yakin perlu menggunakannya, mungkin Anda tidak menggunakannya. Saya merekomendasikan untuk melihat Kapan saya ingin menggunakan panda apply () dalam kode saya? .
cs95
Ini hanya tentang kesalahan sintaks referensi kolom dataframe, dan mengapa fungsi perlu argumen. Mengenai pertanyaan kedua Anda, fungsi my_test(a)tidak tahu apa dfkarena tidak disahkan sebagai argumen (kecuali dfseharusnya menjadi global, yang akan menjadi praktik yang mengerikan). Anda harus melewati semua nilai yang Anda perlukan di dalam suatu fungsi sebagai argumen (lebih disukai dalam urutan), jika tidak, bagaimana lagi fungsi itu tahu dari mana dfdatangnya? Selain itu, praktik buruk untuk memprogram dalam namespace yang dipenuhi dengan variabel global, Anda tidak akan mendapatkan kesalahan seperti ini.
smci

Jawaban:

379

Sepertinya Anda lupa ''string Anda.

In [43]: df['Value'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)

In [44]: df
Out[44]:
                    a    b         c     Value
          0 -1.674308  foo  0.343801  0.044698
          1 -2.163236  bar -2.046438 -0.116798
          2 -0.199115  foo -0.458050 -0.199115
          3  0.918646  bar -0.007185 -0.001006
          4  1.336830  foo  0.534292  0.268245
          5  0.976844  bar -0.773630 -0.570417

BTW, menurut saya, cara berikut ini lebih elegan:

In [53]: def my_test2(row):
....:     return row['a'] % row['c']
....:     

In [54]: df['Value'] = df.apply(my_test2, axis=1)
waitingkuo
sumber
Terima kasih, Anda benar saya lupa ''. Namun saya masih memiliki masalah yang sama dengan fungsi yang lebih kompleks. Saya akan sangat menghargai bantuan Anda dengan itu. Terima kasih
Andy
5
@Andy mengikuti [53-54] memungkinkan Anda menerapkan fungsi yang lebih kompleks.
Andy Hayden
@Andy, Anda dapat menentukan fungsi kompleks Anda seperti cara [53].
waitingkuo
apakah semua menerapkan strategi melakukan hal yang sama? Saya baru mengenal panda dan selalu menganggap menerapkan sedikit membingungkan tetapi strategi Anda di [53-54] mudah bagi saya untuk memahami (dan mudah-mudahan ingat) ... di atas meja besar apakah itu secepat bentuk lain dari penerapan disajikan?
whytheq
Mengapa membuat metode terpisah dianggap lebih elegan - bahkan untuk metode kecil. Saya telah melakukan proyek signifikan dalam python selama 7 tahun tetapi kemungkinan tidak akan pernah dianggap sebagai pythonistakarena beberapa perspektif termasuk yang ini.
javadba
33

Jika Anda hanya ingin menghitung (kolom a)% (kolom b), Anda tidak perlu apply, lakukan saja secara langsung:

In [7]: df['a'] % df['c']                                                                                                                                                        
Out[7]: 
0   -1.132022                                                                                                                                                                    
1   -0.939493                                                                                                                                                                    
2    0.201931                                                                                                                                                                    
3    0.511374                                                                                                                                                                    
4   -0.694647                                                                                                                                                                    
5   -0.023486                                                                                                                                                                    
Name: a
herrfz
sumber
16
Saya tahu, ini hanyalah contoh untuk menunjukkan masalah saya dalam menerapkan fungsi ke beberapa kolom
Andy
18

Katakanlah kita ingin menerapkan fungsi add5 ke kolom 'a' dan 'b' dari DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)
Mir_Murtaza
sumber
Saya mendapatkan kesalahan berikut saat mencoba cuplikan kode Anda. TypeError: ('harus str, bukan int', 'terjadi di indeks b') dapatkah Anda melihatnya.
debaonline4u
Kolom b dari kerangka data Anda adalah tipe string atau kolom tipe objek, harus berupa kolom integer yang akan ditambahkan dengan angka.
Mir_Murtaza
Bukankah perubahan hanya berlaku setelah penugasan?
S.aad
11

Semua saran di atas berfungsi, tetapi jika Anda ingin perhitungan Anda dengan lebih efisien, Anda harus mengambil keuntungan dari operasi vektor numpy (seperti yang ditunjukkan di sini) .

import pandas as pd
import numpy as np


df = pd.DataFrame ({'a' : np.random.randn(6),
             'b' : ['foo', 'bar'] * 3,
             'c' : np.random.randn(6)})

Contoh 1: berulang dengan pandas.apply():

%%timeit
def my_test2(row):
    return row['a'] % row['c']

df['Value'] = df.apply(my_test2, axis=1)

Laju paling lambat butuh 7,49 kali lebih lama dari yang tercepat. Ini bisa berarti bahwa hasil antara sedang di-cache. 1000 loop, terbaik 3: 481 μs per loop

Contoh 2: membuat vektor menggunakan pandas.apply():

%%timeit
df['a'] % df['c']

Laju paling lambat butuh 458,85 kali lebih lama dari yang tercepat. Ini bisa berarti bahwa hasil antara sedang di-cache. 10000 loop, terbaik 3: 70,9 μs per loop

Contoh 3: membuat vektor menggunakan numpy array:

%%timeit
df['a'].values % df['c'].values

Laju paling lambat butuh 7,98 kali lebih lama dari yang tercepat. Ini bisa berarti bahwa hasil antara sedang di-cache. 100000 loop, terbaik 3: 6,39 μs per loop

Jadi vektorisasi menggunakan array numpy meningkatkan kecepatan hampir dua urutan besarnya.

Blane
sumber
Hasil berubah bahkan lebih dramatis untuk angka besar, misalnya mengganti 6 dengan 10K, saya mendapatkan 248 ms, 332 μs, 263 μs masing-masing. Jadi kedua solusi vektor jauh lebih dekat satu sama lain, tetapi solusi non-vektor 1000 kali lebih lambat. (diuji pada python-3.7)
stason
3

Ini sama dengan solusi sebelumnya tetapi saya telah mendefinisikan fungsi di df.apply sendiri:

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
shaurya airi
sumber
2

Saya telah memberikan perbandingan ketiganya yang dibahas di atas.

Menggunakan nilai

% timeit df ['value'] = df ['a']. nilai% df ['c']. nilai

139 µs ± 1.91 µs per loop (rata-rata ± st. Dev dari 7 run, masing-masing 10.000 loop)

Tanpa nilai

% timeit df ['value'] = df ['a']% df ['c'] 

216 µs ± 1.86 µs per loop (rata-rata ± st. Dev dari 7 run, masing-masing 1000 loop)

Terapkan fungsi

% timeit df ['Value'] = df.apply (baris lambda: baris ['a']% baris ['c'], sumbu = 1)

474 µs ± 5.07 µs per loop (rata-rata ± st. Dev dari 7 run, masing-masing 1000 loop)

Gursewak Singh
sumber