【问题标题】:Replace less frequent labels by categories按类别替换频率较低的标签
【发布时间】:2021-10-15 22:43:57
【问题描述】:

我有一个非常长的数据框,其中包含数字列和分类列。

    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 中的每个标签,将 n 个更频繁的标签保留在 proto(或任何分类列)中,其余的,用“att_cat_proto”替换它们。

例如,对于特性proto

Normal = {tcp: 7, udp: 2, arp: 1, unas: 1}
Generic = {udp: 4, tcp: 3, arp: 1, unas: 0}

对于特征“状态”

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 n=1 表示 state 的 Generic,那么所需的结果应如下所示:

    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

但我被困在分配部分。

【问题讨论】:

标签: python pandas dataframe replace group-by


【解决方案1】:

当需要执行大量操作时,可以方便地处理groupby 赋值。尽管apply 就像一个基本循环,但对于groupby,它只为每个组(在本例中为2 个“通用”和“正常”)而不是每一行调用。

修正了变量 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   |

【讨论】:

  • 谢谢@woblob。问题如下:数据框比帖子中显示的要大得多,实际上,在“att_cat”中还有许多其他标签。对于这些标签中的每一个,无论是在原型中还是在状态中,都有比其他标签更频繁的标签。我想要做的是,对于 att_cat 的每个标签,将 n 个最频繁的标签保留在 proto 或 service 中,而较少出现的标签只用一个标签替换它们,所以最后我们在 proto 中没有很多标签或频率非常低的服务。这就是为什么 n 取决于 att_cat 标签。
猜你喜欢
  • 2019-09-20
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-15
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多