在Pandas中按值计数重分配字符串

3 投票
3 回答
1908 浏览
提问于 2025-04-17 23:10

我在一个叫做pandas的数据表里有几个字符串类型的列,想用这些列来做scikitlearn的分类模型。我知道需要用oneHotEncoder来正确处理这些变量,但在此之前,我想先减少这些列中的变化,去掉那些在列中出现频率低于x%的字符串,或者不是出现次数最多的前x个字符串。

举个例子:

df1 = pd.DataFrame({'a':range(22), 'b':list('aaaaaaaabbbbbbbcccdefg'), 'c':range(22)})
df1
     a  b   c
0    0  a   0
1    1  a   1
2    2  a   2
3    3  a   3
4    4  a   4
5    5  a   5
6    6  a   6
7    7  a   7
8    8  b   8
9    9  b   9
10  10  b  10
11  11  b  11
12  12  b  12
13  13  b  13
14  14  b  14
15  15  c  15
16  16  c  16
17  17  c  17
18  18  d  18
19  19  e  19
20  20  f  20
21  21  g  21

你可以看到,a、b和c在列b中出现的频率超过10%,所以我想保留它们。另一方面,d、e、f和g出现的频率低于10%(实际上大约是5%),所以我想把它们归为“其他”:

df1['b']
0     a
1     a
2     a
3     a
4     a
5     a
6     a
7     a
8     b
9     b
10    b
11    b
12    b
13    b
14    b
15    c
16    c
17    c
18    other
19    other
20    other
21    other

我还想说,只保留出现频率排名前2的值,这样列b看起来就像这样:

df1['b']
0     a
1     a
2     a
3     a
4     a
5     a
6     a
7     a
8     b
9     b
10    b
11    b
12    b
13    b
14    b
15    other
16    other
17    other
18    other
19    other
20    other
21    other

在Pandas中我没有找到明显的方法来做到这一点,虽然我对R的了解要多一些。有没有什么想法?还有,如何处理那些可能出现超过10%频率或在前x个值中的空值(Nones)呢?

3 个回答

0

把你的数据框(我称它为x)赋值给一个变量,然后统计一下“b”这一列中每个值的数量。

d = {s: sum(x["b"].values==s) for s in set(x["b"].values)}

接着,你可以用这个筛选条件来给d[s]赋一个新值,如果它低于某个特定的阈值的话。

x[x["b"].values==s] = "Call me Peter Maffay"
1

这是我的解决方案:

def cleanupData(inputCol, fillString, cutoffPercent=None, cutoffNum=31):
    col=inputCol
    col.fillna(fillString, inplace=True)
    valueCounts=col.value_counts()
    totalAmount=sum(valueCounts)
    if cutoffPercent is not None and cutoffNum is not None:
        raise NameError("both cutoff percent and number have values. Please only give one of these values")
    if cutoffPercent is not None:
        cutoffAmount=cutoffPercent*totalAmount
        valuesToKeep=valueCounts[valueCounts>cutoffAmount]
        valuesToKeep=valuesToKeep.index.tolist()
        numValuesKept=len(valuesToKeep)
        print "keeping "+str(numValuesKept)+" unique values in the returned column"
    if cutoffNum is not None:
        valueNames=valueCounts.index.tolist()
        valuesToKeep=valueNames[0:cutoffNum]
    newlist=[]
    for row in col:
        if any(row in element for element in valuesToKeep):
            newlist.append(row)
        else:
            newlist.append("Other")
    return newlist
##


cleanupData(df1['b'], "Other", cutoffNum=2)   
['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'Other', 'Other', 'Other', 'Other', 'Other', 'Other', 'Other']
6

这个问题有点复杂,但我来给你简单说说。

首先,先获取计数:

In [24]: sizes = df1["b"].value_counts()

In [25]: sizes
Out[25]: 
b
a    8
b    7
c    3
d    1
e    1
f    1
g    1
dtype: int64

接下来,选择你不喜欢的索引:

In [27]: bad = sizes.index[sizes < df1.shape[0]*0.1]

In [28]: bad
Out[28]: Index([u'd', u'e', u'f', u'g'], dtype='object')

最后,把那些包含不好的索引的行标记为“其他”:

In [34]: df1.loc[df1["b"].isin(bad), "b"] = "other"

In [36]: df1
Out[36]: 
     a      b   c
0    0      a   0
1    1      a   1
2    2      a   2
3    3      a   3
4    4      a   4
5    5      a   5
6    6      a   6
7    7      a   7
8    8      b   8
9    9      b   9
10  10      b  10
11  11      b  11
12  12      b  12
13  13      b  13
14  14      b  14
15  15      c  15
16  16      c  16
17  17      c  17
18  18  other  18
19  19  other  19
20  20  other  20
21  21  other  21

[22 rows x 3 columns]

你可以用 sizes.sort() 来排序,然后从结果中取最后的 n 个值,这样就能找到前两个索引。

编辑:你可以做类似这样的操作,把所有的“b”替换成 filterByColumn

def filterDataFrame(df1, filterByColumn):
    sizes = df1[filterByColumn].value_counts()
    ...

撰写回答