Panda bersyarat penciptaan seri / kolom dataframe


314

Saya memiliki kerangka data di sepanjang baris di bawah ini:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

Saya ingin menambahkan kolom lain ke kerangka data (atau menghasilkan serangkaian) dengan panjang yang sama dengan kerangka data (= jumlah record / baris yang sama) yang menetapkan warna hijau jika Set = 'Z' dan 'red' jika Set = jika tidak .

Apa cara terbaik untuk melakukan ini?

Jawaban:


712

Jika Anda hanya memiliki dua pilihan untuk dipilih:

df['color'] = np.where(df['Set']=='Z', 'green', 'red')

Sebagai contoh,

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)

hasil panen

  Set Type  color
0   Z    A  green
1   Z    B  green
2   X    B    red
3   Y    C    red

Jika Anda memiliki lebih dari dua syarat, gunakannp.select . Misalnya, jika Anda ingin colormenjadi

  • yellow kapan (df['Set'] == 'Z') & (df['Type'] == 'A')
  • sebaliknya bluekapan(df['Set'] == 'Z') & (df['Type'] == 'B')
  • sebaliknya purplekapan(df['Type'] == 'B')
  • jika tidak black,

lalu gunakan

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
conditions = [
    (df['Set'] == 'Z') & (df['Type'] == 'A'),
    (df['Set'] == 'Z') & (df['Type'] == 'B'),
    (df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)

yang menghasilkan

  Set Type   color
0   Z    A  yellow
1   Z    B    blue
2   X    B  purple
3   Y    C   black

1
tidak berfungsi jika saya meletakkan dua kondisi di mana klausa dengan dan
Amol Sharma

2
df ['color'] = daftar (np.where (df ['Set'] == 'Z', 'green', 'red')) akan menekan peringatan panda: Nilai mencoba diset pada salinan dari sepotong dari DataFrame. Coba gunakan .loc [row_indexer, col_indexer] = nilai sebagai gantinya
denson

3
'hijau' dan 'merah' juga bisa diganti dengan kolom aritmatika. misalnya ,df['foo'] = np.where(df['Set']=='Z', df['Set'], df['Type'].shift(1))
Alejandro

apakah np.where membuat kolom baru? Saya menggunakan kode ini dan ketika saya melakukan df.color.head () saya mendapatkan: 'numpy.ndarray' objek tidak memiliki atribut 'head'
vvv

3
Sayang sekali saya tidak bisa memperbaiki ini beberapa kali. Satu upvote sepertinya tidak cukup.
Harper

120

Pemahaman daftar adalah cara lain untuk membuat kolom lain secara kondisional. Jika Anda bekerja dengan objek tipe dalam kolom, seperti dalam contoh Anda, daftar pemahaman biasanya mengungguli sebagian besar metode lain.

Pemahaman daftar contoh:

df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]

% tes timeit:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop

4
Perhatikan bahwa, dengan kerangka data yang jauh lebih besar (think- pd.DataFrame({'Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000})size), numpy.wheremelampaui map, tetapi pemahaman daftar adalah raja (sekitar 50% lebih cepat dari numpy.where).
blacksite

3
Dapatkah metode pemahaman daftar digunakan jika kondisi membutuhkan informasi dari banyak kolom? Saya mencari sesuatu seperti ini (ini tidak berfungsi):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
Mappi

2
Tambahkan iterrows ke dalam dataframe, maka Anda dapat mengakses beberapa kolom melalui baris: ['red' if (baris ['Set'] == 'Z') & (baris ['Type'] == 'B') else 'green 'untuk indeks, masukkan dalam df.iterrows ()]
cheekybastard

1
Perhatikan solusi bagus ini tidak akan berfungsi jika Anda perlu mengambil nilai pengganti dari seri lain dalam bingkai data, sepertidf['color_type'] = np.where(df['Set']=='Z', 'green', df['Type'])
Paul Rougieux

@cheekybastard Atau tidak, karena .iterrows()terkenal lamban dan DataFrame tidak boleh dimodifikasi saat iterasi.
AMC

21

Cara lain untuk mencapai hal ini adalah

df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

Pendekatan yang baik, ini dapat direkam untuk efisiensi yang lebih cepat (dalam kumpulan data yang lebih besar), meskipun akan membutuhkan langkah tambahan.
Yaakov Bressler

21

Berikut ini cara lain untuk menguliti kucing ini, menggunakan kamus untuk memetakan nilai baru ke tombol dalam daftar:

def map_values(row, values_dict):
    return values_dict[row]

values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}

df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})

df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))

Seperti apa itu:

df
Out[2]: 
  INDICATOR  VALUE  NEW_VALUE
0         A     10          1
1         B      9          2
2         C      8          3
3         D      7          4

Pendekatan ini bisa sangat kuat ketika Anda memiliki banyak ifelsepernyataan tipe-untuk membuat (yaitu banyak nilai unik untuk diganti).

Dan tentu saja Anda selalu bisa melakukan ini:

df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)

Tetapi pendekatan itu lebih dari tiga kali lebih lambat dari applypendekatan dari atas, pada mesin saya.

Dan Anda juga bisa melakukan ini, menggunakan dict.get:

df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]

Saya suka jawaban ini karena ini menunjukkan bagaimana melakukan beberapa penggantian nilai
Monica Heddneck

Tetapi pendekatan itu lebih dari tiga kali lebih lambat dari pendekatan yang diterapkan dari atas, pada mesin saya. Bagaimana Anda membandingkan ini? Dari pengukuran cepat saya, .map()solusinya ~ 10 kali lebih cepat daripada .apply().
AMC

Pembaruan: Pada 100.000.000 baris, 52 nilai string, .apply()membutuhkan waktu 47 detik, dibandingkan hanya 5,91 detik untuk .map().
AMC

19

Berikut ini lebih lambat daripada pendekatan waktu di sini , tetapi kami dapat menghitung kolom tambahan berdasarkan konten lebih dari satu kolom, dan lebih dari dua nilai dapat dihitung untuk kolom tambahan.

Contoh sederhana hanya menggunakan kolom "Set":

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Contoh dengan lebih banyak warna dan lebih banyak kolom yang diperhitungkan:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    elif row["Type"] == "C":
        return "blue"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C   blue

Sunting (21/06/2019): Menggunakan plydata

Dimungkinkan juga untuk menggunakan plydata untuk melakukan hal-hal semacam ini (ini tampaknya lebih lambat daripada menggunakan assigndan apply, meskipun).

from plydata import define, if_else

Sederhana if_else:

df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Bersarang if_else:

df = define(df, color=if_else(
    'Set=="Z"',
    '"red"',
    if_else('Type=="C"', '"green"', '"blue"')))

print(df)                            
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B   blue
3   Y    C  green

10

Mungkin ini telah dimungkinkan dengan pembaruan Pandas yang lebih baru, tetapi saya pikir yang berikut ini adalah jawaban terpendek dan mungkin terbaik untuk pertanyaan itu, sejauh ini. Anda dapat menggunakan .locmetode ini dan menggunakan satu kondisi atau beberapa tergantung pada kebutuhan Anda.

Ringkasan Kode:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"

#practice!
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Penjelasan:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))

# df so far: 
  Type Set  
0    A   Z 
1    B   Z 
2    B   X 
3    C   Y

tambahkan kolom 'warna' dan setel semua nilai ke "merah"

df['Color'] = "red"

Terapkan satu kondisi Anda:

df.loc[(df['Set']=="Z"), 'Color'] = "green"


# df: 
  Type Set  Color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

atau beberapa kondisi jika Anda ingin:

df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Anda dapat membaca tentang operator logis Pandas dan pemilihan bersyarat di sini: Operator logis untuk pengindeksan boolean di Pandas


2
Yang terbaik sejauh ini. Anda mungkin dapat menambahkan lebih banyak kondisi yang akan menjadi kodedf.loc[(df['Set']=="Z") & (df['Type']=="A"), 'Color'] = "green"
Salvador Vigo

2
Ini harus menjadi jawaban yang diterima. Sebenarnya idiomatis dan bisa dikembangkan.
AMC

1

Satu liner dengan .apply()metode adalah sebagai berikut:

df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')

Setelah itu, dfbingkai data terlihat seperti ini:

>>> print(df)
  Type Set  color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

0

Jika Anda bekerja dengan data besar, pendekatan memo yang terbaik:

# First create a dictionary of manually stored values
color_dict = {'Z':'red'}

# Second, build a dictionary of "other" values
color_dict_other = {x:'green' for x in df['Set'].unique() if x not in color_dict.keys()}

# Next, merge the two
color_dict.update(color_dict_other)

# Finally, map it to your column
df['color'] = df['Set'].map(color_dict)

Pendekatan ini akan menjadi tercepat ketika Anda memiliki banyak nilai berulang. Aturan umum saya adalah untuk memo ketika: data_size> 10**4& n_distinct<data_size/4

Ex Memoize dalam kasus 10.000 baris dengan nilai berbeda 2.500 atau kurang.


Baiklah, jadi dengan hanya 2 nilai berbeda untuk dipetakan, 100.000.000 baris, dibutuhkan 6,67 detik untuk berjalan tanpa "memoisasi", dan 9,86 detik dengan.
AMC

100.000.000 baris, 52 nilai berbeda, di mana 1 dari peta tersebut ke nilai output pertama, dan 51 lainnya semuanya sesuai dengan yang lain: 7,99 detik tanpa memoisasi, 11,1 detik dengan.
AMC

Apakah nilai Anda dalam urutan acak? Atau apakah mereka kembali ke belakang? Panda kecepatan tinggi bisa jadi disebabkan oleh caching @AMC
Yaakov Bressler

1
Apakah nilai Anda dalam urutan acak? Atau apakah mereka kembali ke belakang? Nilai acak, dipilih menggunakan random.choices().
AMC
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.