panda membuat kolom baru berdasarkan nilai dari kolom lain / menerapkan fungsi beberapa kolom, berdasarkan baris

316

Saya ingin menerapkan fungsi kustom saya (itu menggunakan tangga jika-lain) sampai enam kolom ini ( ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) di setiap baris dataframe saya.

Saya sudah mencoba berbagai metode dari pertanyaan lain tetapi sepertinya masih belum menemukan jawaban yang tepat untuk masalah saya. Bagian penting dari ini adalah bahwa jika orang tersebut dianggap sebagai Hispanik mereka tidak dapat dihitung sebagai hal lain. Bahkan jika mereka memiliki "1" di kolom etnis lain mereka masih dianggap sebagai Hispanik bukan dua atau lebih ras. Demikian pula, jika jumlah semua kolom ERI lebih besar dari 1 mereka dihitung sebagai dua atau lebih ras dan tidak dapat dihitung sebagai etnis yang unik (kecuali untuk Hispanik). Semoga ini masuk akal. Bantuan apa pun akan sangat dihargai.

Hampir seperti melakukan for for melalui setiap baris dan jika setiap catatan memenuhi kriteria mereka ditambahkan ke satu daftar dan dihilangkan dari aslinya.

Dari kerangka data di bawah ini saya perlu menghitung kolom baru berdasarkan spesifikasi berikut dalam SQL:

========================= KRITERIA ======================== =======

IF [ERI_Hispanic] = 1 THEN RETURN Hispanic
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN Two or More
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN A/I AK Native
ELSE IF [ERI_Asian] = 1 THEN RETURN Asian
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN Black/AA
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN White

Komentar: Jika Bendera ERI untuk Hispanik benar (1), karyawan tersebut diklasifikasikan sebagai "Hispanik"

Komentar: Jika lebih dari 1 Bendera ERI non-Hispanik benar, kembalikan "Dua atau Lebih"

====================== DATAFRAME =============================

     lname          fname       rno_cd  eri_afr_amer    eri_asian   eri_hawaiian    eri_hispanic    eri_nat_amer    eri_white   rno_defined
0    MOST           JEFF        E       0               0           0               0               0               1           White
1    CRUISE         TOM         E       0               0           0               1               0               0           White
2    DEPP           JOHNNY              0               0           0               0               0               1           Unknown
3    DICAP          LEO                 0               0           0               0               0               1           Unknown
4    BRANDO         MARLON      E       0               0           0               0               0               0           White
5    HANKS          TOM         0                       0           0               0               0               1           Unknown
6    DENIRO         ROBERT      E       0               1           0               0               0               1           White
7    PACINO         AL          E       0               0           0               0               0               1           White
8    WILLIAMS       ROBIN       E       0               0           1               0               0               0           White
9    EASTWOOD       CLINT       E       0               0           0               0               0               1           White
Dave
sumber
Fungsi khusus Anda hanyalah tangga if-else di mana nilai beberapa variabel lebih diprioritaskan. Itu akan disebut prioritas-decoder dalam bahasa rekayasa perangkat keras.
smci

Jawaban:

408

OK, dua langkah untuk ini - pertama adalah menulis fungsi yang melakukan terjemahan yang Anda inginkan - Saya telah memberikan contoh berdasarkan pseudo-code Anda:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

Anda mungkin ingin membahas ini, tetapi tampaknya melakukan trik - perhatikan bahwa parameter yang masuk ke fungsi dianggap sebagai objek Seri berlabel "baris".

Selanjutnya, gunakan fungsi terapkan dalam panda untuk menerapkan fungsi - misalnya

df.apply (lambda row: label_race(row), axis=1)

Perhatikan sumbu = 1 specifier, itu berarti bahwa aplikasi dilakukan pada satu baris, bukan pada level kolom. Hasilnya di sini:

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White

Jika Anda puas dengan hasil itu, jalankan lagi, simpan hasilnya ke kolom baru di kerangka data asli Anda.

df['race_label'] = df.apply (lambda row: label_race(row), axis=1)

Kerangka data yang dihasilkan terlihat seperti ini (gulir ke kanan untuk melihat kolom baru):

      lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label
0      MOST    JEFF      E             0          0             0              0             0          1       White         White
1    CRUISE     TOM      E             0          0             0              1             0          0       White      Hispanic
2      DEPP  JOHNNY    NaN             0          0             0              0             0          1     Unknown         White
3     DICAP     LEO    NaN             0          0             0              0             0          1     Unknown         White
4    BRANDO  MARLON      E             0          0             0              0             0          0       White         Other
5     HANKS     TOM    NaN             0          0             0              0             0          1     Unknown         White
6    DENIRO  ROBERT      E             0          1             0              0             0          1       White   Two Or More
7    PACINO      AL      E             0          0             0              0             0          1       White         White
8  WILLIAMS   ROBIN      E             0          0             1              0             0          0       White  Haw/Pac Isl.
9  EASTWOOD   CLINT      E             0          0             0              0             0          1       White         White
Thomas Kimber
sumber
69
hanya sebuah catatan: jika Anda hanya memberi makan baris ke fungsi Anda, Anda bisa melakukan:df.apply(label_race, axis=1)
Paul H
1
Jika saya ingin melakukan sesuatu yang mirip dengan baris lain, bisakah saya menggunakan fungsi yang sama? Misalnya, dari hasil, jika ['race_label'] == "White" kembalikan 'White' dan seterusnya. Tetapi jika ['race_label'] == 'Tidak Dikenal' mengembalikan nilai dari kolom ['rno_defined']. Saya menganggap fungsi yang sama akan berfungsi, tetapi sepertinya saya tidak tahu cara mendapatkan nilai dari kolom lainnya.
Dave
2
Anda dapat menulis fungsi baru, yang terlihat di bidang 'race_label', dan mengirim hasilnya ke bidang baru, atau - dan saya pikir ini mungkin lebih baik dalam hal ini, mengedit fungsi asli, mengubah return 'Other'baris terakhir return row['rno_defined']yang seharusnya ganti nilai dari kolom itu dalam kasus-kasus di mana set pernyataan if / then tidak menemukan kecocokan (yaitu di mana saat ini, Anda melihat 'Lainnya').
Thomas Kimber
9
Anda dapat menyederhanakan: df.apply(lambda row: label_race (row),axis=1)kedf.apply(label_race, axis=1)
user48956
5
Dalam versi yang lebih baru, jika Anda mendapatkan 'SettingWithCopyWarning', Anda harus melihat metode 'assign'. Lihat: stackoverflow.com/a/12555510/3015186
np8
218

Karena ini adalah hasil Google pertama untuk 'kolom baru dari orang lain', berikut adalah contoh sederhana:

import pandas as pd

# make a simple dataframe
df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
df
#    a  b
# 0  1  3
# 1  2  4

# create an unattached column with an index
df.apply(lambda row: row.a + row.b, axis=1)
# 0    4
# 1    6

# do same but attach it to the dataframe
df['c'] = df.apply(lambda row: row.a + row.b, axis=1)
df
#    a  b  c
# 0  1  3  4
# 1  2  4  6

Jika Anda mendapatkan, SettingWithCopyWarningAnda dapat melakukannya dengan cara ini juga:

fn = lambda row: row.a + row.b # define a function for the new column
col = df.apply(fn, axis=1) # get column data with an index
df = df.assign(c=col.values) # assign values to column 'c'

Sumber: https://stackoverflow.com/a/12555510/243392

Dan jika nama kolom Anda termasuk spasi Anda dapat menggunakan sintaks seperti ini:

df = df.assign(**{'some column name': col.values})

Dan inilah dokumentasi untuk diterapkan , dan ditugaskan .

Brian Burns
sumber
1
Jawaban singkat, disaring sampai ke esensi!
Frode Akselsen
1
Saya mendapatkan SettingWithCopyWarningketika saya melakukannya df['c'] = df.apply(lambda row: row.a + row.b, axis=1) Apakah itu masalah nyata di sini, atau haruskah saya tidak mengkhawatirkannya?
Nate
2
@Nate Saya tidak pernah mendapat peringatan itu - mungkin itu tergantung pada data dalam dataframe? Tapi saya mengubah jawaban berdasarkan jawaban lain dari 2017.
Brian Burns
57

Jawaban di atas benar-benar valid, tetapi ada solusi vektor, dalam bentuk numpy.select. Ini memungkinkan Anda untuk menentukan kondisi, lalu menentukan keluaran untuk kondisi tersebut, jauh lebih efisien daripada menggunakan apply:


Pertama, tentukan kondisi:

conditions = [
    df['eri_hispanic'] == 1,
    df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    df['eri_nat_amer'] == 1,
    df['eri_asian'] == 1,
    df['eri_afr_amer'] == 1,
    df['eri_hawaiian'] == 1,
    df['eri_white'] == 1,
]

Sekarang, tentukan output yang sesuai:

outputs = [
    'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]

Akhirnya, menggunakan numpy.select:

res = np.select(conditions, outputs, 'Other')
pd.Series(res)

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White
dtype: object

Kenapa harus numpy.selectdigunakan berlebihan apply? Berikut ini beberapa pemeriksaan kinerja:

df = pd.concat([df]*1000)

In [42]: %timeit df.apply(lambda row: label_race(row), axis=1)
1.07 s ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [44]: %%timeit
    ...: conditions = [
    ...:     df['eri_hispanic'] == 1,
    ...:     df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
    ...:     df['eri_nat_amer'] == 1,
    ...:     df['eri_asian'] == 1,
    ...:     df['eri_afr_amer'] == 1,
    ...:     df['eri_hawaiian'] == 1,
    ...:     df['eri_white'] == 1,
    ...: ]
    ...:
    ...: outputs = [
    ...:     'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
    ...: ]
    ...:
    ...: np.select(conditions, outputs, 'Other')
    ...:
    ...:
3.09 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Menggunakan numpy.selectmemberi kami kinerja yang jauh lebih baik, dan perbedaan hanya akan meningkat ketika data bertambah.

pengguna3483203
sumber
8
Solusi ini sangat diremehkan. Saya tahu bahwa saya bisa melakukan sesuatu yang mirip dengan yang berlaku tetapi sedang mencari alternatif karena saya harus melakukan operasi itu untuk ribuan file. Senang saya menemukan posting Anda.
mlx
Saya mengalami masalah dengan membuat sesuatu yang serupa. Saya mendapatkan "nilai kebenaran suatu seri adalah ambigu ..." pesan kesalahan. Kode saya adalah Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] kondisi = [df_merge ['state_alpha'] di Kansas_City] outputs = [' Kansas City '] df_merge [' Region '] = np.select (kondisi, keluaran,' Lainnya ') Ada yang bisa membantu?
Shawn Schreier
3
Ini harus menjadi jawaban yang diterima. Yang lain baik-baik saja tetapi begitu Anda bekerja dalam data yang lebih besar, ini adalah satu-satunya yang berfungsi, dan bekerja sangat cepat.
TheProletariat
29

.apply()mengambil fungsi sebagai parameter pertama; operasikan label_racefungsi sebagai berikut:

df['race_label'] = df.apply(label_race, axis=1)

Anda tidak perlu membuat fungsi lambda untuk lulus dalam suatu fungsi.

Gabrielle Simard-Moore
sumber
12

coba ini,

df.loc[df['eri_white']==1,'race_label'] = 'White'
df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
df['race_label'].fillna('Other', inplace=True)

O / P:

     lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian  \
0      MOST    JEFF      E             0          0             0   
1    CRUISE     TOM      E             0          0             0   
2      DEPP  JOHNNY    NaN             0          0             0   
3     DICAP     LEO    NaN             0          0             0   
4    BRANDO  MARLON      E             0          0             0   
5     HANKS     TOM    NaN             0          0             0   
6    DENIRO  ROBERT      E             0          1             0   
7    PACINO      AL      E             0          0             0   
8  WILLIAMS   ROBIN      E             0          0             1   
9  EASTWOOD   CLINT      E             0          0             0   

   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label  
0             0             0          1       White         White  
1             1             0          0       White      Hispanic  
2             0             0          1     Unknown         White  
3             0             0          1     Unknown         White  
4             0             0          0       White         Other  
5             0             0          1     Unknown         White  
6             0             0          1       White   Two Or More  
7             0             0          1       White         White  
8             0             0          0       White  Haw/Pac Isl.  
9             0             0          1       White         White 

gunakan .locsebagai ganti apply.

itu meningkatkan vektorisasi.

.loc bekerja dengan cara sederhana, mask baris berdasarkan kondisi, terapkan nilai pada baris beku.

untuk lebih jelasnya kunjungi, .loc docs

Metrik kinerja:

Jawaban yang diterima:

def label_race (row):
   if row['eri_hispanic'] == 1 :
      return 'Hispanic'
   if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
      return 'Two Or More'
   if row['eri_nat_amer'] == 1 :
      return 'A/I AK Native'
   if row['eri_asian'] == 1:
      return 'Asian'
   if row['eri_afr_amer']  == 1:
      return 'Black/AA'
   if row['eri_hawaiian'] == 1:
      return 'Haw/Pac Isl.'
   if row['eri_white'] == 1:
      return 'White'
   return 'Other'

df=pd.read_csv('dataser.csv')
df = pd.concat([df]*1000)

%timeit df.apply(lambda row: label_race(row), axis=1)

1,15 s ± 46,5 ms per loop (rata-rata ± dev. 7 putaran, masing-masing 1 loop)

Jawaban Usulan Saya:

def label_race(df):
    df.loc[df['eri_white']==1,'race_label'] = 'White'
    df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
    df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
    df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
    df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
    df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
    df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
    df['race_label'].fillna('Other', inplace=True)
df=pd.read_csv('s22.csv')
df = pd.concat([df]*1000)

%timeit label_race(df)

24,7 ms ± 1,7 ms per loop (rata-rata ± dev. 7 berjalan, masing-masing 10 loop)

Mohamed Thasin ah
sumber