加速datafram中字符串的整数编码

2024-09-28 17:30:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个如下的数据帧,由字符串值组成。你知道吗

    0   1   2
0   o   jj  ovg
1   j   jj  jjy
2   y   yk  yku
3   v   vf  vfs
4   i   iw  iwd

我有一个函数,它用整数值对每列进行编码,并计算每列中唯一元素的数量。我使用了cat.codesnunique函数。请参见下面的计时结果和代码片段。你知道吗

很明显,这些行动需要很多时间。我怎样才能加快速度?你知道吗

Line #      Hits         Time  Per Hit   % Time         Line Contents
=====================================================================================================================
25           1    7529434.0   7529434.0     79.9      df = df.apply(lambda x: x.astype('category').cat.codes)
26                                               
27                                                    # calculate the number of unique keys for each row
28           1    1825214.0   1825214.0     19.4      len_arr = df.nunique(axis=0).values

编辑答案的计时结果

df.apply(lambda x: pd.factorize(x)[0])
#100 loops, best of 3: 6.24 ms per loop
%timeit df.apply(lambda x: pd.factorize(x)[0])
#100 loops, best of 3: 4.93 ms per loop

%timeit df1.nunique(axis=0).values
#100 loops, best of 3: 2.34 ms per loop
%timeit df1.apply(lambda x: len(pd.factorize(x)[1]))
#100 loops, best of 3: 2.64 ms per loop

编辑2 更多有趣的计时结果:

# results with 100 rows
%timeit original()
#100 loops, best of 3: 7 ms per loop
%timeit WeNYoBen()
#100 loops, best of 3: 2.4 ms per loop
%timeit jezrael()
#100 loops, best of 3: 4.03 ms per loop
%timeit piRSquared()
#100 loops, best of 3: 2.29 ms per loop

# results with 10000 rows
%timeit original()
#100 loops, best of 3: 16.6 ms per loop
%timeit WeNYoBen()
#10 loops, best of 3: 23 ms per loop
%timeit jezrael()
#100 loops, best of 3: 6.14 ms per loop
%timeit piRSquared()
#100 loops, best of 3: 19.1 ms per loop

Tags: oflambdaloopdfmspdbestapply
3条回答

pd.factorize

重点是捕获factorize的两个输出,并在整数编码和nunique计算中使用它们,而无需factorize两次。你知道吗

运行此命令以获取编码和唯一值

e, u = zip(*map(pd.factorize, map(df.get, df)))

将编码转换为数据帧

pd.DataFrame([*zip(*e)], df.index, df.columns)

   0  1  2
0  0  0  0
1  1  0  1
2  2  1  2
3  3  2  3
4  4  3  4

将唯一值的长度转换为序列

pd.Series([*map(len, u)], df.columns)

0    5
1    4
2    5
dtype: int64

总之,两个对象的赋值是

e, u = zip(*map(pd.factorize, map(df.get, df)))    
df_ = pd.DataFrame([*zip(*e)], df.index, df.columns)
c = pd.Series([*map(len, u)], df.columns)

对于那些使用传统Python的人来说,没有[*it]语法

e, u = zip(*map(pd.factorize, map(df.get, df)))
df_ = pd.DataFrame(list(zip(*e)), df.index, df.columns)
c = pd.Series(list(map(len, u)), df.columns)

我认为使用listmap就足够了

l=list(map(set,df.values.T))
l
Out[71]: 
[{'i', 'j', 'o', 'v', 'y'},
 {'iw', 'jj', 'vf', 'yk'},
 {'iwd', 'jjy', 'ovg', 'vfs', 'yku'}]

list(map(len,l))
Out[74]: [5, 4, 5]

np.unique的用法

def yourfunc(x):
    _,indices = np.unique(x, return_inverse=True)
    return indices
df.apply(yourfunc)
Out[102]: 
   0  1  2
0  2  1  2
1  1  1  1
2  4  3  4
3  3  2  3
4  0  0  0

对第二个数组的长度使用^{}

a = df.apply(lambda x: len(pd.factorize(x)[1]))
print (a)
0    5
1    4
2    5
dtype: int64

对于整数:

b = df.apply(lambda x: pd.factorize(x)[0])
print (b)
   0  1  2
0  0  0  0
1  1  0  1
2  2  1  2
3  3  2  3
4  4  3  4

避免调用函数两次:

out = {}
def f(x):
    a, b = pd.factorize(x)
    out[x.name] = len(b)
    return a

b = df.apply(f)
print (b)
   0  1  2
0  0  0  0
1  1  0  1
2  2  1  2
3  3  2  3
4  4  3  4

a = pd.Series(out)
print (a)
0    5
1    4
2    5
dtype: int64

相关问题 更多 >