我有一个很长的数据框架,包含数字列和分类列。
id dur proto state att_cat
0 1 0.121460 tcp FIN Normal
1 2 0.649902 tcp FIN Normal
2 3 1.623047 tcp FIN Normal
3 4 1.681641 tcp FIN Normal
4 5 0.449463 tcp CON Normal
5 6 0.000009 udp INT Generic
6 7 0.505859 udp CON Normal
7 8 0.000009 udp INT Generic
8 9 0.000009 udp INT Generic
9 10 0.000009 tcp INT Generic
10 11 0.222761 unas ECO Normal
11 12 1.461278 tcp CON Normal
12 13 1.065289 arp FIN Normal
13 14 2.782646 udp CON Normal
14 15 1.457923 tcp FIN Normal
15 16 0.000009 udp INT Generic
17 18 0.125550 arp INT Generic
18 19 0.000009 tcp INT Generic
19 20 0.000009 tcp CON Generic
在att_cat列中,我要对不同的标签进行分组。当我这样做时,然后我观察到,例如,列proto有一些标签比其他标签出现得更频繁,例如,对于att_cat=Normal, tcp比udp和其他更频繁(不显示在数据框中);对于Generic,最常见的是udp。
我想,对于att_cat中的每个标签,在proto(或任何分类列)中保留n更频繁的标签,其余的,用'att_cat_proto'代替。
例如,对于特性proto:
Normal = {tcp: 7, udp: 2, arp: 1, unas: 1}
Generic = {udp: 4, tcp: 3, arp: 1, unas: 0}
对于特性'state'
Normal = {FIN: 5, CON: 4, INT: 1, ECO: 1}
Generic = {INT: 6, CON: 1, FIN: 1, ECO: 0}
如果我将proto特性的Normal和Generic的n=2和Normal的n=2和Generic的n=1固定为,那么期望的结果应该是:
id dur proto state att_cat
0 1 0.121460 tcp FIN Normal
1 2 0.649902 tcp FIN Normal
2 3 1.623047 tcp FIN Normal
3 4 1.681641 tcp Normal_State Normal
4 5 0.449463 tcp CON Normal
5 6 0.000009 udp INT Generic
6 7 0.505859 udp CON Normal
7 8 0.000009 udp INT Generic
8 9 0.000009 udp INT Generic
9 10 0.000009 tcp Generic_State Generic
10 11 0.222761 Normal_Proto Normal_State Normal
11 12 1.461278 tcp CON Normal
12 13 1.065289 Normal_Proto FIN Normal
13 14 2.782646 udp CON Normal
14 15 1.457923 tcp FIN Normal
15 16 0.000009 udp INT Generic
17 18 0.125550 Generic_Proto INT Generic
18 19 0.000009 tcp INT Generic
19 20 0.000009 tcp Generic_State Generic
到目前为止,我已经尝试了如下操作:
def rare_labels(df,target,cat_var,n):
df = df.copy()
# Selects the low frequency labels
cat_frame = df[cat_var]
most_freq = cat_frame.value_counts().index[:n].to_list()
less_freq = np.setdiff1d(cat_frame.unique(),most_freq)
# Substitute the low frequency labels by a common label
df[cat_var] = df.groupby(target).*******
# Returns the dataframe
return df
但是我被分配的部分卡住了。
在执行大量操作时,有一种方便的处理groupby
赋值的方法。尽管apply
类似于一个基本循环,但对于groupby
,它只对每个组(在本例中为2 "Generic")和"Normal"),而不是每行
修正了变量n.的问题(一开始没有发现)
import pandas as pd
import numpy as np
def renamer(grp, restrictions):
cat = grp.att_cat.iloc[0]
for column in ["proto", "state"]:
n = restrictions.loc[cat, column]
unique_labels = grp[column].unique()
value_count = grp[column].value_counts()
most_freq = value_count.index[:n].to_list()
least_freq = np.setdiff1d(unique_labels, most_freq)
mask = np.zeros(len(grp), dtype=bool)
for label in least_freq:
mask |= (grp[column] == label)
grp[column][mask] = f"{cat}_{column}"
return grp
restrictions = {
"proto": [2, 2],
"state": [2, 1]
}
restrictions = pd.DataFrame(restrictions, index=["Normal", "Generic"])
df = pd.read_csv("data.csv")
df.groupby('att_cat').apply(renamer, restrictions).to_markdown()
输出:
| | id | dur | proto | state | att_cat |
|---:|-----:|---------:|:--------------|:--------------|:----------|
| 0 | 1 | 0.12146 | tcp | FIN | Normal |
| 1 | 2 | 0.649902 | tcp | FIN | Normal |
| 2 | 3 | 1.62305 | tcp | FIN | Normal |
| 3 | 4 | 1.68164 | tcp | Normal_state | Normal |
| 4 | 5 | 0.449463 | tcp | CON | Normal |
| 5 | 6 | 9e-06 | udp | INT | Generic |
| 6 | 7 | 0.505859 | udp | CON | Normal |
| 7 | 8 | 9e-06 | udp | INT | Generic |
| 8 | 9 | 9e-06 | udp | INT | Generic |
| 9 | 10 | 9e-06 | tcp | Generic_state | Generic |
| 10 | 11 | 0.222761 | Normal_proto | Normal_state | Normal |
| 11 | 12 | 1.46128 | tcp | CON | Normal |
| 12 | 13 | 1.06529 | Normal_proto | FIN | Normal |
| 13 | 14 | 2.78265 | udp | CON | Normal |
| 14 | 15 | 1.45792 | tcp | FIN | Normal |
| 15 | 16 | 9e-06 | udp | INT | Generic |
| 16 | 18 | 0.12555 | Generic_proto | INT | Generic |
| 17 | 19 | 9e-06 | tcp | INT | Generic |
| 18 | 20 | 9e-06 | tcp | Generic_state | Generic |