Catégorisation basée sur les priorités à l'aide de pandas / python
J'ai facturé les données liées dans le Dataframe ci-dessous et les listes de codes
df = pd.DataFrame({
'invoice':[1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7],
'code':[101,104,105,101,106,106,104,101,104,105,111,109,111,110,101,114,112],
'qty':[2,1,1,3,2,4,7,1,1,1,1,4,2,1,2,2,1]
})
+---------+------+-----+
| invoice | code | qty |
+---------+------+-----+
| 1 | 101 | 2 |
+---------+------+-----+
| 1 | 104 | 1 |
+---------+------+-----+
| 2 | 105 | 1 |
+---------+------+-----+
| 2 | 101 | 3 |
+---------+------+-----+
| 2 | 106 | 2 |
+---------+------+-----+
| 3 | 106 | 4 |
+---------+------+-----+
| 3 | 104 | 7 |
+---------+------+-----+
| 3 | 101 | 1 |
+---------+------+-----+
| 4 | 104 | 1 |
+---------+------+-----+
| 4 | 105 | 1 |
+---------+------+-----+
| 4 | 111 | 1 |
+---------+------+-----+
| 5 | 109 | 4 |
+---------+------+-----+
| 5 | 111 | 2 |
+---------+------+-----+
| 6 | 110 | 1 |
+---------+------+-----+
| 6 | 101 | 2 |
+---------+------+-----+
| 6 | 114 | 2 |
+---------+------+-----+
| 7 | 104 | 2 |
+---------+------+-----+
les listes de codes sont,
Soda = [101,102]
Hot = [103,109]
Juice = [104,105]
Milk = [106,107,108]
Dessert = [110,111]
Ma tâche consiste à ajouter une nouvelle categorycolonne en fonction de ce qui est spécifié ci-dessous Order of Priority.
Priorité n ° 1: si une facture a plus de 10, la quantité doit être classée comme
Mega. par exemple: sommeqtydeinvoice 3 is 12Priorité n ° 2: du
rest of the invoice. si l'uncodedes élémentsinvoicefigure dans laMilkliste, la catégorie doit l'êtreHealthy. par exemple: dansinvoice 2code 106est dansMilk. par conséquent, la facture complète est classée commeHealthy. Indépendamment des autres éléments (code 101 & 105) sont présents dans la facture. Au fur et à mesure que les priorités sont appliquées à lafullfacture.Priorité n ° 3: à partir de
rest of the invoice, si l'uncodedes élémentsinvoiceest dans laJuiceliste, cela a2 parts
(3.1) si la somme de cette quantité de jus est equal to 1, alors la catégorie devrait être OneJuice. par exemple: invoice 1has code 104et qty 1.this invoice 1obtiendra OneJuiceindépendamment des autres éléments ( code 101) présents dans la facture. Au fur et à mesure que les priorités sont appliquées à la fullfacture.
(3.2) si la somme de cette quantité de jus est greater than 1, la catégorie devrait être ManyJuice. par exemple: invoice 4a code 104 & 105 et qty 1 + 1 = 2.
Priorité n ° 4: à partir du
rest of the invoice, si l'unecodedes factures est dans laHotliste, elle doit être classée commeHotLovers. Indépendamment des autres éléments sont présents dans la facture.Priorité n ° 5: à partir du
rest of the invoice, si l'unecodedes factures est dans laDessertliste, elle doit être classée commeDessertLovers.Enfin, le reste de toute la facture doit être classé comme
Others.
Ma sortie souhaitée est comme ci-dessous.
+---------+------+-----+---------------+
| invoice | code | qty | category |
+---------+------+-----+---------------+
| 1 | 101 | 2 | OneJuice |
+---------+------+-----+---------------+
| 1 | 104 | 1 | OneJuice |
+---------+------+-----+---------------+
| 2 | 105 | 1 | Healthy |
+---------+------+-----+---------------+
| 2 | 101 | 3 | Healthy |
+---------+------+-----+---------------+
| 2 | 106 | 2 | Healthy |
+---------+------+-----+---------------+
| 3 | 106 | 4 | Mega |
+---------+------+-----+---------------+
| 3 | 104 | 7 | Mega |
+---------+------+-----+---------------+
| 3 | 101 | 1 | Mega |
+---------+------+-----+---------------+
| 4 | 104 | 1 | ManyJuice |
+---------+------+-----+---------------+
| 4 | 105 | 1 | ManyJuice |
+---------+------+-----+---------------+
| 4 | 111 | 1 | ManyJuice |
+---------+------+-----+---------------+
| 5 | 109 | 4 | HotLovers |
+---------+------+-----+---------------+
| 5 | 111 | 2 | HotLovers |
+---------+------+-----+---------------+
| 6 | 110 | 1 | DessertLovers |
+---------+------+-----+---------------+
| 6 | 101 | 2 | DessertLovers |
+---------+------+-----+---------------+
| 6 | 114 | 2 | DessertLovers |
+---------+------+-----+---------------+
| 7 | 104 | 2 | ManyJuice |
+---------+------+-----+---------------+
jusqu'à présent, j'ai essayé ci-dessous. Ça marche. mais assez naïf et pas du tout pythonique. aussi quand j'ai appliqué ceci à l'ensemble de données original, le code est très très lent.
# Calculating Priority No.1
L = df.groupby(['invoice'])['qty'].transform('sum') >= 10
df_Large = df[L]['invoice'].to_frame()
df_Large['category'] = 'Mega'
df_Large.drop_duplicates(['invoice'], inplace=True)
# Calculating Priority No.2
df_1 = df[~L] # removing Priority No.1 calculated above
M = (df_1['code'].isin(Milk)
.groupby(df_1['invoice'])
.transform('any'))
df_Milk = df_1[M]['invoice'].to_frame()
df_Milk['category'] = 'Healthy'
df_Milk.drop_duplicates(['invoice'], inplace=True)
# Calculating Priority No.3
# 3.a Part -1
df_2 = df[~L & ~M] # removing Priority No.1 & 2 calculated above
J_1 = (df_2['code'].isin(Juice)
.groupby(df_2['invoice'])
.transform('sum') == 1)
df_SM = df_2[J_1]['invoice'].to_frame()
df_SM['category'] = 'OneJuice'
df_SM.drop_duplicates(['invoice'], inplace=True)
# 3.b Part -2
J_2 = (df_2['code'].isin(Juice)
.groupby(df_2['invoice'])
.transform('sum') > 1)
df_MM = df_2[J_2]['invoice'].to_frame()
df_MM['category'] = 'ManyJuice'
df_MM.drop_duplicates(['invoice'], inplace=True)
# Calculating Priority No.4
df_3 = df[~L & ~M & ~J_1 & ~J_2] # removing Priority No.1, 2 & 3 (a & b) calculated above
H = (df_3['code'].isin(Hot)
.groupby(df_3['invoice'])
.transform('any'))
df_Hot = df_3[H]['invoice'].to_frame()
df_Hot['category'] = 'HotLovers'
df_Hot.drop_duplicates(['invoice'], inplace=True)
# Calculating Priority No.5
df_4 = df[~L & ~M & ~J_1 & ~J_2 & ~H ] # removing Priority No.1, 2, 3 (a & b) and 4 calculated above
D = (df_4['code'].isin(Dessert)
.groupby(df_4['invoice'])
.transform('any'))
df_Dessert = df_4[D]['invoice'].to_frame()
df_Dessert['category'] = 'DessertLovers'
df_Dessert.drop_duplicates(['invoice'], inplace=True)
# merge all dfs
category = pd.concat([df_Large,df_Milk,df_SM,df_MM,df_Hot,df_Dessert], axis=0,sort=False, ignore_index=True)
# Final merge to the original dataset
df = df.merge(category,on='invoice', how='left').fillna(value='Others')
Donc, besoin d'aide pour nettoyer ce code pour la vitesse / l'efficacité et la manière pythonique.
Réponses
Vous pouvez essayer d'utiliser np.select
df['category'] = np.select([
df.groupby('invoice')['qty'].transform('sum') >= 10,
df['code'].isin(Milk).groupby(df.invoice).transform('any'),
(df['qty']*df['code'].isin(Juice)).groupby(df.invoice).transform('sum') == 1,
(df['qty']*df['code'].isin(Juice)).groupby(df.invoice).transform('sum') > 1,
df['code'].isin(Hot).groupby(df.invoice).transform('any'),
df['code'].isin(Dessert).groupby(df.invoice).transform('any')
],
['Mega','Healthy','OneJuice','ManyJuice','HotLovers','DessertLovers'],
'Other'
)
print(df)
Production
invoice code qty category
0 1 101 2 OneJuice
1 1 104 1 OneJuice
2 2 105 1 Healthy
3 2 101 3 Healthy
4 2 106 2 Healthy
5 3 106 4 Mega
6 3 104 7 Mega
7 3 101 1 Mega
8 4 104 1 ManyJuice
9 4 105 1 ManyJuice
10 4 111 1 ManyJuice
11 5 109 4 HotLovers
12 5 111 2 HotLovers
13 6 110 1 DessertLovers
14 6 101 2 DessertLovers
15 6 114 2 DessertLovers
16 7 104 2 ManyJuice
Micro-Benchmark
pd.show_versions()
commit : None
python : 3.7.5.final.0
python-bits : 64
OS : Linux
OS-release : 4.4.0-18362-Microsoft
machine : x86_64
processor : x86_64
byteorder : little
LC_ALL : None
LANG : C.UTF-8
LOCALE : en_US.UTF-8
pandas : 0.25.3
numpy : 1.17.4
Les données ont été créées avec
def make_data(n):
return pd.DataFrame({
'invoice':np.arange(n)//3,
'code':np.random.choice(np.arange(101,112),n),
'qty':np.random.choice(np.arange(1,8), n, p=[10/25,10/25,1/25,1/25,1/25,1/25,1/25])
})
Résultats
perfplot.show(
setup=make_data,
kernels=[get_category, get_with_np_select],
n_range=[2**k for k in range(8, 20)],
logx=True,
logy=True,
equality_check=False,
xlabel='len(df)')