我有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
要查看跟踪打印输出,请取消注释打印指令在函数中。