通过Pandas中的值计数重新分配字符串

时间:2014-03-20 16:54:06

标签: python pandas scikit-learn

我在pandas数据框中有许多基于字符串的列,我希望在scikitlearn分类模型上使用这些列。我知道我必须使用oneHotEncoder来正确编码变量,但首先,我想减少列中的变化,取出列中出现的时间少于x%的字符串,或者不在列中的count前面的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

我没有在熊猫身上看到一个明显的方法来做到这一点,尽管我确实在R中对此有了更多的了解。任何想法?关于如何使Nones更加健壮的任何想法,可能会出现超过10%的时间或者位于前x个值中?

3 个答案:

答案 0 :(得分:4)

这有点扭曲,但这是一个复杂的问题。

首先,得到计数:

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')

最后,分配&#34;其他&#34;到那些包含坏索引的行:

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个值,以便找到前两个索引。

修改:你应该可以做这样的事情,替换&#34; b&#34;的所有实例。与filterByColumn

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

答案 1 :(得分: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']

答案 2 :(得分: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"