将许多具有条件的特性二进制化



我有Pandas数据帧,它具有数百个分类功能(以数字表示(。我只想在列中保留顶部值。我已经知道,每列中只有3或4个最频繁的值,但我想自动选择它。我需要两种方法:

1( 只留下3个最频繁的值注意事项:没有一列具有1、2或3个唯一值(每列约有20个唯一值(,所以不要考虑它。例如,如果你有几个第三位,请全部保留。例如:

#使用value_counts((列1后
1 35
2 23
3 10
4 9
8 8
6 8

#在第2列使用value_counts((后
0 23
2 15
1 15#两个第二位
4 9
5 3
6 2

#在列1上使用value_counts((后的结果
1 35
2 23
3 10
其他25#9+8+8

#在第2列上使用value_counts((后的结果
0 23
2 15
1 15
4 9
其他5#3+2

2(根据需要在每列中保留尽可能多的值,以便剩余值的数量小于您决定保留的最后一个值的数量。例如:

#使用value_counts((列1后
1 35
2 23
3 10
4 3
8 2
6 1

#在第2列上使用value_counts((后
0 23
2 15
1 9
4 8
5 3
6 2

#在列1上使用value_counts((后的结果
1 35
2 23
3 10
其他6#3+2+1

#在第2列上使用value_counts((后的结果
0 23
2 15
1 9
4 8
其他5#3+2

请两者都做。谢谢

我将展示我想在工作中使用的2列数据限制:在此解决方案中,第2、第3和第4位的同时平局不会收集到同一单元格中。根据您的目的,您可能需要进一步自定义此行为。

示例数据

共有2列,每列26个类。一列是分类的,另一列是数字的。选择样本数据是为了展示领带的效果。

import pandas as pd
import numpy as np
np.random.seed(2)  # reproducibility
df = pd.DataFrame(np.random.randint(65, 91, (1000, 2)), columns=["str", "num"])
df["str"] = list(map(chr, df["str"].values))
print(df)
    str  num
0     I   80
1     N   73
2     W   76
3     S   76
4     I   72
..   ..  ...
995   M   80
996   Q   70
997   P   66
998   I   87
999   F   83
[1000 rows x 2 columns]

所需功能

def count_top_n(df, n_top):
    # name of output columns
    def gen_cols(ls_str):
        for s in ls_str:
            yield s
            yield f"{s}_counts"
    df_count = pd.DataFrame(np.zeros((n_top+1, df.shape[1]*2), dtype=object),
                            index=range(1, n_top+2),
                            columns=list(gen_cols(df.columns.values)))  # df.shape[1] = #cols
    # process each column
    for i, col in enumerate(df):
        # count
        tmp = df[col].value_counts()
        assert len(tmp) > n_top, f"ValueError: too few classes {len(tmp)} <= {n_top} = n_top)"
        # case 1: no ties at the 3rd place
        if tmp.iat[n_top - 1] != tmp.iat[n_top]:
            # fill in classes
            df_count.iloc[:n_top, 2*i] = tmp[:n_top].index.values
            df_count.iloc[n_top, 2*i] = "(rest)"
            # fill counts
            df_count.iloc[:n_top, 2*i+1] = tmp[:n_top]
            df_count.iloc[n_top, 2*i+1] = tmp[n_top:].sum()
        
        # case 2: ties
        else:
            # new termination location
            n_top_new = (tmp >= tmp.iat[n_top]).sum()
            # fill in classes
            df_count.iloc[:n_top-1, 2*i] = tmp.iloc[:n_top-1].index.values
            df_count.iloc[n_top-1, 2*i] = list(tmp.iloc[n_top-1:n_top_new].index.values)
            df_count.iloc[n_top, 2*i] = "(rest)"
            # fill counts
            df_count.iloc[:n_top-1, 2*i+1] = tmp.iloc[:n_top-1].values
            df_count.iloc[n_top-1, 2*i+1] = list(tmp.iloc[n_top-1:n_top_new].values)
            df_count.iloc[n_top, 2*i+1] = tmp.iloc[n_top_new:].values.sum()
    return df_count

输出:

生成一个人类可读的表。请注意,对于列str,在第2、第3和第4位存在平局。

print(count_top_n(df, 3))
      str str_count       num num_count
1       V        52        71        51
2       Q        46        86        47
3  [B, K]  [46, 46]  [90, 67]  [46, 46]
4  (rest)       810    (rest)       810

让我们用您的逻辑来尝试一个udf:

def my_count(s):
    x = s.value_counts()
    if len(x) > 3:
        ret = x.iloc[:3].copy()
        ret.loc['other'] = x.iloc[3:].sum()
    else:
        ret = x
    return ret
df[['col1']].apply(my_count)

输出:

       col1
1        35
2        23
3        10
other     6

使用以下函数:

def myFilter(col, maxOther = 0):
    unq = col.value_counts()
    if maxOther == 0:    # Return 3 MFV
        thr = unq.unique()[:3][-1]
        otherCnt = unq[unq < thr].sum()
        rv = col[col.isin(unq[unq >= thr].index)]
    else:    # Drop last LFV, no more than maxOther
        otherCnt = 0
        for i in unq[::-1]:
            if otherCnt + i >= maxOther: break
            otherCnt += i
        thrInd = unq.size - i + 1
        rv = col[col.isin(unq[:thrInd].index)]
    rv = rv.reset_index(drop=True)
    # print(f'  Trace {col.name}nunq:n{unq}notherCnt: {otherCnt}')
    return rv

我的假设是,两种变体之间的区别:

  • 返回3个最频繁值(MFV(
  • 删除最后不太频繁的(其他(值

maxOther参数控制。其默认值0意味着";第一变体";。

因此,测试这两种变体称之为:

  • CCD_ 2
  • CCD_ 3

要查看跟踪打印输出,请取消注释打印指令在函数中。

最新更新