在Pandas中按值计数重分配字符串
我在一个叫做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()
...