นุ่นสร้างคอลัมน์ใหม่ขึ้นอยู่กับค่าจากคอลัมน์อื่น ๆ / ใช้ฟังก์ชั่นของหลาย ๆ คอลัมน์แถวฉลาด


316

ฉันต้องการที่จะใช้ฟังก์ชั่นที่กำหนดเองของฉัน (จะใช้บันไดถ้า-อื่น) เหล่านี้หกคอลัมน์ ( ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) ในแถวของ dataframe ของฉันในแต่ละ

ฉันลองใช้วิธีการที่แตกต่างจากคำถามอื่น ๆ แต่ก็ยังหาคำตอบที่เหมาะสมสำหรับปัญหาของฉันไม่ได้ สิ่งสำคัญของเรื่องนี้คือถ้าบุคคลนั้นถูกนับว่าเป็นฮิสแปนิกพวกเขาจะไม่สามารถถูกนับเป็นสิ่งอื่นได้ แม้ว่าพวกเขาจะมี "1" ในคอลัมน์เชื้อชาติอื่นพวกเขายังคงถูกนับว่าเป็นฮิสแปนิกไม่ใช่สองเผ่าพันธุ์หรือมากกว่า ในทำนองเดียวกันหากผลรวมของคอลัมน์ ERI ทั้งหมดมากกว่า 1 พวกเขาจะถูกนับเป็นสองเชื้อชาติหรือมากกว่าและไม่สามารถนับได้ว่าเป็นเชื้อชาติที่ไม่ซ้ำกัน (ยกเว้นฮิสแปนิก) หวังว่านี่จะสมเหตุสมผล ความช่วยเหลือใด ๆ จะได้รับการชื่นชมอย่างมาก

มันเกือบจะเหมือนกับการทำลูปผ่านแต่ละแถวและถ้าแต่ละเรคคอร์ดตรงตามเกณฑ์พวกมันจะถูกเพิ่มเข้าไปในรายการเดียวและตัดออกจากต้นฉบับ

จาก dataframe ด้านล่างฉันจำเป็นต้องคำนวณคอลัมน์ใหม่ตามข้อมูลจำเพาะต่อไปนี้ใน SQL:

========================= เกณฑ์การตัดสิน ======================== =======

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

ความคิดเห็น: หากการตั้งค่าสถานะ ERI สำหรับประเทศสเปนเป็นจริง (1) พนักงานจะถูกจัดประเภทเป็น "สเปน"

ความคิดเห็น: หากมีมากกว่า 1 ธง ERI ที่ไม่ใช่ประเทศสเปนเป็นความจริงให้ส่งคืน“ สองคนหรือมากกว่า”

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

ฟังก์ชั่นเฉพาะของคุณเป็นเพียงขั้นตอนที่ต้องใช้เวลานานซึ่งค่าของตัวแปรบางตัวมีความสำคัญเหนือสิ่งอื่น มันจะเรียกว่าตัวถอดรหัสลำดับความสำคัญในสำนวนวิศวกรรมฮาร์ดแวร์
smci

คำตอบ:


407

ตกลงสองขั้นตอนนี้ - อันดับแรกคือการเขียนฟังก์ชั่นที่แปลตามที่คุณต้องการ - ฉันได้นำตัวอย่างมารวมกันโดยใช้โค้ดหลอกของคุณ:

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.apply (lambda row: label_race(row), axis=1)

หมายเหตุตัวระบุ Axis = 1 ซึ่งหมายความว่าแอปพลิเคชันจะทำที่แถวแทนที่จะเป็นระดับคอลัมน์ ผลลัพธ์อยู่ที่นี่:

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

หากคุณพอใจกับผลลัพธ์เหล่านั้นให้เรียกใช้อีกครั้งโดยบันทึกผลลัพธ์ลงในคอลัมน์ใหม่ใน dataframe ดั้งเดิมของคุณ

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

dataframe ที่เป็นผลลัพธ์จะมีลักษณะเช่นนี้ (เลื่อนไปทางขวาเพื่อดูคอลัมน์ใหม่):

      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

69
แค่ทราบว่า: ถ้าคุณเพียงแค่ให้อาหารแถวในฟังก์ชั่นของคุณคุณสามารถทำได้:df.apply(label_race, axis=1)
พอล H

1
หากฉันต้องการทำสิ่งที่คล้ายกับแถวอื่นฉันสามารถใช้ฟังก์ชันเดียวกันได้หรือไม่ ตัวอย่างเช่นจากผลลัพธ์หาก ['race_label'] == "สีขาว" กลับมาเป็น 'สีขาว' เป็นต้น แต่ถ้า ['race_label'] == 'ไม่ทราบ' ให้ส่งคืนค่าจากคอลัมน์ ['rno_defined'] ฉันคิดว่าฟังก์ชั่นเดียวกันนี้ใช้งานได้ แต่ฉันไม่สามารถหาวิธีรับค่าจากคอลัมน์อื่นได้
เดฟ

2
คุณสามารถเขียนฟังก์ชันใหม่ที่ดูที่ฟิลด์ 'race_label' และส่งผลลัพธ์ไปยังฟิลด์ใหม่หรือ - และฉันคิดว่านี่อาจจะดีกว่าในกรณีนี้แก้ไขฟังก์ชันต้นฉบับเปลี่ยนreturn 'Other'บรรทัดสุดท้ายreturn row['rno_defined']ที่ควร แทนที่ค่าจากคอลัมน์นั้นในกรณีที่ชุดคำสั่ง if / then ไม่พบรายการที่ตรงกัน (เช่นที่ปัจจุบันคุณเห็น 'อื่น ๆ ')
โทมัสคิมเบอร์

9
คุณสามารถทำให้ง่ายขึ้น: df.apply(lambda row: label_race (row),axis=1)todf.apply(label_race, axis=1)
user48956

5
ในเวอร์ชันที่ใหม่กว่าถ้าคุณได้รับ 'SettingWithCopyWarning' คุณควรดูวิธีการ 'กำหนด' ดู: stackoverflow.com/a/12555510/3015186
np8

218

เนื่องจากนี่เป็นผลลัพธ์ครั้งแรกของ Google สำหรับ 'pandas new column from others' นี่เป็นตัวอย่างง่ายๆ:

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

หากคุณได้รับSettingWithCopyWarningคุณสามารถทำได้ด้วยวิธีนี้:

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'

ที่มา: https://stackoverflow.com/a/12555510/243392

และถ้าชื่อคอลัมน์ของคุณมีช่องว่างคุณสามารถใช้ไวยากรณ์ดังนี้:

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

และนี่คือเอกสารประกอบการสมัครและกำหนด


1
คำตอบสั้น ๆ กลั่นลงไปที่จำเป็น!
Frode Akselsen

1
ฉันได้รับSettingWithCopyWarningเมื่อฉันทำdf['c'] = df.apply(lambda row: row.a + row.b, axis=1) นั่นเป็นปัญหาจริงที่นี่หรือฉันไม่ควรกังวลเกี่ยวกับมัน?
เนท

2
@Nate ฉันไม่เคยได้รับคำเตือนนั้น - มันอาจจะขึ้นอยู่กับข้อมูลใน dataframe? แต่ฉันทำลายคำตอบโดยอิงจากคำตอบอื่นในปี 2560
Brian Burns

57

คำตอบข้างต้นเป็นที่ถูกต้องสมบูรณ์ แต่การแก้ปัญหา vectorized numpy.selectอยู่ในรูปแบบของ สิ่งนี้ช่วยให้คุณกำหนดเงื่อนไขจากนั้นกำหนดผลลัพธ์สำหรับเงื่อนไขเหล่านั้นได้อย่างมีประสิทธิภาพมากกว่าการใช้apply:


ก่อนกำหนดเงื่อนไข:

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

ในที่สุดใช้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

ทำไมnumpy.selectต้องใช้มากกว่าapply? นี่คือการตรวจสอบประสิทธิภาพบางอย่าง:

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)

การใช้numpy.selectทำให้เราได้รับการปรับปรุงอย่างมากมายและความคลาดเคลื่อนจะเพิ่มขึ้นเมื่อข้อมูลเติบโตขึ้นเท่านั้น


8
โซลูชันนี้มีการประเมินค่าต่ำเกินไป ฉันรู้ว่าฉันสามารถทำสิ่งที่คล้ายกันกับการใช้งานได้ แต่กำลังมองหาทางเลือกอื่นเนื่องจากฉันต้องทำการดำเนินการนั้นกับไฟล์หลายพันไฟล์ ดีใจมากที่ฉันพบโพสต์ของคุณ
mlx

ฉันมีปัญหาในการสร้างสิ่งที่คล้ายกัน ฉันได้รับข้อความแสดงข้อผิดพลาด "ค่าความจริงของซีรีส์นั้นไม่ชัดเจน ... " รหัสของฉันคือ Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] เงื่อนไข = [df_merge ['state_alpha'] ใน Kansas_City] ผลลัพธ์ = [' Kansas City '] df_merge [' Region '] = np.select (เงื่อนไข, เอาท์พุต,' อื่น ๆ ') สามารถช่วยอะไรได้บ้าง?
Shawn Schreier

3
นี่ควรเป็นคำตอบที่ยอมรับได้ อีกอันนั้นใช้ได้ แต่เมื่อคุณทำงานในข้อมูลที่ใหญ่กว่าอันนี้ก็เป็นอันเดียวที่ทำงานได้และมันก็ทำงานได้อย่างรวดเร็วอย่างน่าอัศจรรย์
TheProletariat

29

.apply()ใช้ในฟังก์ชั่นเป็นพารามิเตอร์แรก; ผ่านlabel_raceฟังก์ชั่นดังนี้:

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

คุณไม่จำเป็นต้องสร้างฟังก์ชั่นแลมบ์ดาให้ผ่านฟังก์ชั่น


12

ลองนี้

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 

ใช้แทน.locapply

มันช่วยปรับปรุง vectorization

.loc ทำงานในลักษณะที่ง่ายแถวหน้ากากตามเงื่อนไขใช้ค่ากับแถวตรึง

สำหรับรายละเอียดเพิ่มเติมเยี่ยมชม. loc เอกสาร

การวัดประสิทธิภาพ:

คำตอบที่ยอมรับ:

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 ต่อลูป (หมายถึง± std. dev. จาก 7 รัน, 1 ลูปแต่ละครั้ง)

คำตอบที่ฉันเสนอ:

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 ต่อลูป (หมายถึง± std. dev. จาก 7 รัน, 10 ลูปแต่ละอัน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.