Pandas 一种热编码:将频率较低的类别捆绑在一起
Pandas One hot encoding: Bundling together less frequent categories
我正在对具有大约 18 种不同类型值的分类列进行热编码。我只想为那些出现超过某个阈值(比方说 1%)的值创建新列,并创建另一个名为 other values
的列,如果值不是那些频繁值,则该列具有 1。
我正在使用 Pandas 和 Sci-kit 学习。我探索了 pandas get_dummies
和 sci-kit learn 的 one hot encoder
,但无法弄清楚如何将频率较低的值捆绑到一列中。
像下面这样的东西怎么样:
创建数据框
df = pd.DataFrame(data=list('abbgcca'), columns=['x'])
df
x
0 a
1 b
2 b
3 g
4 c
5 c
6 a
替换出现频率低于给定阈值的值。我将创建该列的副本,这样我就不会修改原始数据框。第一步是创建 value_counts
的字典,然后用这些计数替换实际值,以便将它们与阈值进行比较。将低于该阈值的值设置为 'other values',然后使用 pd.get_dummies
获取虚拟变量
#set the threshold for example 20%
thresh = 0.2
x = df.x.copy()
#replace any values present less than the threshold with 'other values'
x[x.replace(x.value_counts().to_dict()) < len(x)*thresh] = 'other values'
#get dummies
pd.get_dummies(x)
a b c other values
0 1.0 0.0 0.0 0.0
1 0.0 1.0 0.0 0.0
2 0.0 1.0 0.0 0.0
3 0.0 0.0 0.0 1.0
4 0.0 0.0 1.0 0.0
5 0.0 0.0 1.0 0.0
6 1.0 0.0 0.0 0.0
或者您可以使用 Counter
它可能更干净一些
from collections import Counter
x[x.replace(Counter(x)) < len(x)*thresh] = 'other values'
计划
pd.get_dummies
正常进行一次热编码
sum() < threshold
标识聚合的列
- 我使用
pd.value_counts
和参数 normalize=True
来获得出现的百分比。
join
def hot_mess2(s, thresh):
d = pd.get_dummies(s)
f = pd.value_counts(s, sort=False, normalize=True) < thresh
if f.sum() == 0:
return d
else:
return d.loc[:, ~f].join(d.loc[:, f].sum(1).rename('other'))
考虑 pd.Series
s
s = pd.Series(np.repeat(list('abcdef'), range(1, 7)))
s
0 a
1 b
2 b
3 c
4 c
5 c
6 d
7 d
8 d
9 d
10 e
11 e
12 e
13 e
14 e
15 f
16 f
17 f
18 f
19 f
20 f
dtype: object
hot_mess(s, 0)
a b c d e f
0 1 0 0 0 0 0
1 0 1 0 0 0 0
2 0 1 0 0 0 0
3 0 0 1 0 0 0
4 0 0 1 0 0 0
5 0 0 1 0 0 0
6 0 0 0 1 0 0
7 0 0 0 1 0 0
8 0 0 0 1 0 0
9 0 0 0 1 0 0
10 0 0 0 0 1 0
11 0 0 0 0 1 0
12 0 0 0 0 1 0
13 0 0 0 0 1 0
14 0 0 0 0 1 0
15 0 0 0 0 0 1
16 0 0 0 0 0 1
17 0 0 0 0 0 1
18 0 0 0 0 0 1
19 0 0 0 0 0 1
20 0 0 0 0 0 1
hot_mess(s, .1)
c d e f other
0 0 0 0 0 1
1 0 0 0 0 1
2 0 0 0 0 1
3 1 0 0 0 0
4 1 0 0 0 0
5 1 0 0 0 0
6 0 1 0 0 0
7 0 1 0 0 0
8 0 1 0 0 0
9 0 1 0 0 0
10 0 0 1 0 0
11 0 0 1 0 0
12 0 0 1 0 0
13 0 0 1 0 0
14 0 0 1 0 0
15 0 0 0 1 0
16 0 0 0 1 0
17 0 0 0 1 0
18 0 0 0 1 0
19 0 0 0 1 0
20 0 0 0 1 0
pip install siuba
#( in python or anaconda prompth shell)
#use library as:
from siuba.dply.forcats import fct_lump, fct_reorder
#just like fct_lump of R :
df['Your_column'] = fct_lump(df['Your_column'], n= 10)
df['Your_column'].value_counts() # check your levels
#it reduces the level to 10, lumps all the others as 'Other'
R 有一个很好的功能 fct_lump 用于此目的,现在它被复制到 python,只需您 select 要保留的级别数和所有其他级别将是捆绑为 'others' .
改进版:
以前的解决方案在数据帧时不能很好地扩展
很大。
当您只想对一列执行 one-hot 编码并且您的原始数据帧有多于一列时,情况也会变得复杂。
这是一个更通用、可扩展(更快)的解决方案。
用两列一百万行的例子df
来说明:
import pandas as pd
import string
df = pd.DataFrame(
{'1st': [random.sample(["orange", "apple", "banana"], k=1)[0] for i in range(1000000)],\
'2nd': [random.sample(list(string.ascii_lowercase), k=1)[0] for i in range(1000000)]}
)
前 10 行 df.head(10)
是:
1st 2nd
0 banana t
1 orange t
2 banana m
3 banana g
4 banana g
5 orange a
6 apple x
7 orange s
8 orange d
9 apple u
统计数据df['2nd'].value_counts()
是:
s 39004
k 38726
n 38720
b 38699
t 38688
p 38646
u 38638
w 38611
y 38587
o 38576
q 38559
x 38558
r 38545
i 38497
h 38429
v 38385
m 38369
j 38278
f 38262
e 38241
a 38241
l 38236
g 38210
z 38202
c 38058
d 38035
第 1 步:定义阈值
threshold = 38500
第 2 步:关注您要对其进行 one-hot 编码的列,并将频率低于阈值的条目更改为 others
%timeit df.loc[df['2nd'].value_counts()[df['2nd']].values < threshold, '2nd'] = "others"
所用时间为 206 ms ± 346 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
。
第 3 步:照常应用 one-hot 编码
df = pd.get_dummies(df, columns = ['2nd'], prefix='', prefix_sep='')
前10行one-hot编码后df.head(10)
变为
1st b k n o others p q r s t u w x y
0 banana 0 0 0 0 0 0 0 0 0 1 0 0 0 0
1 orange 0 0 0 0 0 0 0 0 0 1 0 0 0 0
2 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
3 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
4 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
5 orange 0 0 0 0 1 0 0 0 0 0 0 0 0 0
6 apple 0 0 0 0 0 0 0 0 0 0 0 0 1 0
7 orange 0 0 0 0 0 0 0 0 1 0 0 0 0 0
8 orange 0 0 0 0 1 0 0 0 0 0 0 0 0 0
9 apple 0 0 0 0 0 0 0 0 0 0 1 0 0 0
第 4 步(可选):如果您希望 others
成为 df
的最后一列,您可以尝试:
df = df[[col for col in df.columns if col != 'others'] + ['others']]
这会将 others
移动到最后一列。
1st b k n o p q r s t u w x y others
0 banana 0 0 0 0 0 0 0 0 1 0 0 0 0 0
1 orange 0 0 0 0 0 0 0 0 1 0 0 0 0 0
2 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
3 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
4 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5 orange 0 0 0 0 0 0 0 0 0 0 0 0 0 1
6 apple 0 0 0 0 0 0 0 0 0 0 0 1 0 0
7 orange 0 0 0 0 0 0 0 1 0 0 0 0 0 0
8 orange 0 0 0 0 0 0 0 0 0 0 0 0 0 1
9 apple 0 0 0 0 0 0 0 0 0 1 0 0 0 0
我正在对具有大约 18 种不同类型值的分类列进行热编码。我只想为那些出现超过某个阈值(比方说 1%)的值创建新列,并创建另一个名为 other values
的列,如果值不是那些频繁值,则该列具有 1。
我正在使用 Pandas 和 Sci-kit 学习。我探索了 pandas get_dummies
和 sci-kit learn 的 one hot encoder
,但无法弄清楚如何将频率较低的值捆绑到一列中。
像下面这样的东西怎么样:
创建数据框
df = pd.DataFrame(data=list('abbgcca'), columns=['x'])
df
x
0 a
1 b
2 b
3 g
4 c
5 c
6 a
替换出现频率低于给定阈值的值。我将创建该列的副本,这样我就不会修改原始数据框。第一步是创建 value_counts
的字典,然后用这些计数替换实际值,以便将它们与阈值进行比较。将低于该阈值的值设置为 'other values',然后使用 pd.get_dummies
获取虚拟变量
#set the threshold for example 20%
thresh = 0.2
x = df.x.copy()
#replace any values present less than the threshold with 'other values'
x[x.replace(x.value_counts().to_dict()) < len(x)*thresh] = 'other values'
#get dummies
pd.get_dummies(x)
a b c other values
0 1.0 0.0 0.0 0.0
1 0.0 1.0 0.0 0.0
2 0.0 1.0 0.0 0.0
3 0.0 0.0 0.0 1.0
4 0.0 0.0 1.0 0.0
5 0.0 0.0 1.0 0.0
6 1.0 0.0 0.0 0.0
或者您可以使用 Counter
它可能更干净一些
from collections import Counter
x[x.replace(Counter(x)) < len(x)*thresh] = 'other values'
计划
pd.get_dummies
正常进行一次热编码sum() < threshold
标识聚合的列- 我使用
pd.value_counts
和参数normalize=True
来获得出现的百分比。
- 我使用
join
def hot_mess2(s, thresh):
d = pd.get_dummies(s)
f = pd.value_counts(s, sort=False, normalize=True) < thresh
if f.sum() == 0:
return d
else:
return d.loc[:, ~f].join(d.loc[:, f].sum(1).rename('other'))
考虑 pd.Series
s
s = pd.Series(np.repeat(list('abcdef'), range(1, 7)))
s
0 a
1 b
2 b
3 c
4 c
5 c
6 d
7 d
8 d
9 d
10 e
11 e
12 e
13 e
14 e
15 f
16 f
17 f
18 f
19 f
20 f
dtype: object
hot_mess(s, 0)
a b c d e f
0 1 0 0 0 0 0
1 0 1 0 0 0 0
2 0 1 0 0 0 0
3 0 0 1 0 0 0
4 0 0 1 0 0 0
5 0 0 1 0 0 0
6 0 0 0 1 0 0
7 0 0 0 1 0 0
8 0 0 0 1 0 0
9 0 0 0 1 0 0
10 0 0 0 0 1 0
11 0 0 0 0 1 0
12 0 0 0 0 1 0
13 0 0 0 0 1 0
14 0 0 0 0 1 0
15 0 0 0 0 0 1
16 0 0 0 0 0 1
17 0 0 0 0 0 1
18 0 0 0 0 0 1
19 0 0 0 0 0 1
20 0 0 0 0 0 1
hot_mess(s, .1)
c d e f other
0 0 0 0 0 1
1 0 0 0 0 1
2 0 0 0 0 1
3 1 0 0 0 0
4 1 0 0 0 0
5 1 0 0 0 0
6 0 1 0 0 0
7 0 1 0 0 0
8 0 1 0 0 0
9 0 1 0 0 0
10 0 0 1 0 0
11 0 0 1 0 0
12 0 0 1 0 0
13 0 0 1 0 0
14 0 0 1 0 0
15 0 0 0 1 0
16 0 0 0 1 0
17 0 0 0 1 0
18 0 0 0 1 0
19 0 0 0 1 0
20 0 0 0 1 0
pip install siuba
#( in python or anaconda prompth shell)
#use library as:
from siuba.dply.forcats import fct_lump, fct_reorder
#just like fct_lump of R :
df['Your_column'] = fct_lump(df['Your_column'], n= 10)
df['Your_column'].value_counts() # check your levels
#it reduces the level to 10, lumps all the others as 'Other'
R 有一个很好的功能 fct_lump 用于此目的,现在它被复制到 python,只需您 select 要保留的级别数和所有其他级别将是捆绑为 'others' .
改进版:
以前的解决方案在数据帧时不能很好地扩展 很大。
当您只想对一列执行 one-hot 编码并且您的原始数据帧有多于一列时,情况也会变得复杂。
这是一个更通用、可扩展(更快)的解决方案。
用两列一百万行的例子df
来说明:
import pandas as pd
import string
df = pd.DataFrame(
{'1st': [random.sample(["orange", "apple", "banana"], k=1)[0] for i in range(1000000)],\
'2nd': [random.sample(list(string.ascii_lowercase), k=1)[0] for i in range(1000000)]}
)
前 10 行 df.head(10)
是:
1st 2nd
0 banana t
1 orange t
2 banana m
3 banana g
4 banana g
5 orange a
6 apple x
7 orange s
8 orange d
9 apple u
统计数据df['2nd'].value_counts()
是:
s 39004
k 38726
n 38720
b 38699
t 38688
p 38646
u 38638
w 38611
y 38587
o 38576
q 38559
x 38558
r 38545
i 38497
h 38429
v 38385
m 38369
j 38278
f 38262
e 38241
a 38241
l 38236
g 38210
z 38202
c 38058
d 38035
第 1 步:定义阈值
threshold = 38500
第 2 步:关注您要对其进行 one-hot 编码的列,并将频率低于阈值的条目更改为 others
%timeit df.loc[df['2nd'].value_counts()[df['2nd']].values < threshold, '2nd'] = "others"
所用时间为 206 ms ± 346 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
。
第 3 步:照常应用 one-hot 编码
df = pd.get_dummies(df, columns = ['2nd'], prefix='', prefix_sep='')
前10行one-hot编码后df.head(10)
变为
1st b k n o others p q r s t u w x y
0 banana 0 0 0 0 0 0 0 0 0 1 0 0 0 0
1 orange 0 0 0 0 0 0 0 0 0 1 0 0 0 0
2 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
3 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
4 banana 0 0 0 0 1 0 0 0 0 0 0 0 0 0
5 orange 0 0 0 0 1 0 0 0 0 0 0 0 0 0
6 apple 0 0 0 0 0 0 0 0 0 0 0 0 1 0
7 orange 0 0 0 0 0 0 0 0 1 0 0 0 0 0
8 orange 0 0 0 0 1 0 0 0 0 0 0 0 0 0
9 apple 0 0 0 0 0 0 0 0 0 0 1 0 0 0
第 4 步(可选):如果您希望 others
成为 df
的最后一列,您可以尝试:
df = df[[col for col in df.columns if col != 'others'] + ['others']]
这会将 others
移动到最后一列。
1st b k n o p q r s t u w x y others
0 banana 0 0 0 0 0 0 0 0 1 0 0 0 0 0
1 orange 0 0 0 0 0 0 0 0 1 0 0 0 0 0
2 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
3 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
4 banana 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5 orange 0 0 0 0 0 0 0 0 0 0 0 0 0 1
6 apple 0 0 0 0 0 0 0 0 0 0 0 1 0 0
7 orange 0 0 0 0 0 0 0 1 0 0 0 0 0 0
8 orange 0 0 0 0 0 0 0 0 0 0 0 0 0 1
9 apple 0 0 0 0 0 0 0 0 0 1 0 0 0 0