快速替代 运行 Pandas DataFrame 中所有行的基于 numpy 的函数
Fast alternative to run a numpy based function over all the rows in Pandas DataFrame
我有一个按以下方式创建的 Pandas 数据框:
import pandas as pd
def create(n):
df = pd.DataFrame({ 'gene':["foo",
"bar",
"qux",
"woz"],
'cell1':[433.96,735.62,483.42,10.33],
'cell2':[94.93,2214.38,97.93,1205.30],
'cell3':[1500,90,100,80]})
df = df[["gene","cell1","cell2","cell3"]]
df = pd.concat([df]*n)
df = df.reset_index(drop=True)
return df
看起来像这样:
In [108]: create(1)
Out[108]:
gene cell1 cell2 cell3
0 foo 433.96 94.93 1500
1 bar 735.62 2214.38 90
2 qux 483.42 97.93 100
3 woz 10.33 1205.30 80
然后我有一个函数可以获取每个基因(行)的值
计算某个分数:
import numpy as np
def sparseness(xvec):
n = len(xvec)
xvec_sum = np.sum(np.abs(xvec))
xvecsq_sum = np.sum(np.square(xvec))
denom = np.sqrt(n) - (xvec_sum / np.sqrt(xvecsq_sum))
enum = np.sqrt(n) - 1
sparseness_x = denom/enum
return sparseness_x
实际上我需要在 40K 行上应用此函数。目前它正在运行
使用 Pandas 'apply':
非常慢
In [109]: df = create(10000)
In [110]: express_df = df.ix[:,1:]
In [111]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 8.32 s per loop
实现它的更快替代方案是什么?
一种更快的方法是实现该函数的矢量化版本,它直接在二维 ndarray 上运行。这是非常可行的,因为 numpy 中的许多函数可以在二维 ndarray 上运行,使用 axis
参数控制。一个可能的实现:
def sparseness2(xs):
nr = np.sqrt(xs.shape[1])
a = np.sum(np.abs(xs), axis=1)
b = np.sqrt(np.sum(np.square(xs), axis=1))
sparseness = (nr - a/b) / (nr - 1)
return sparseness
res_arr = sparseness2(express_df.values)
res2 = pd.Series(res_arr, index=express_df.index)
一些测试:
from pandas.util.testing import assert_series_equal
res1 = express_df.apply(sparseness, axis=1)
assert_series_equal(res1, res2) #OK
%timeit sparseness2(express_df.values)
# 1000 loops, best of 3: 655 µs per loop
这是一种矢量化方法,它使用 np.einsum
在整个数据帧中一次执行所有这些操作。现在,这个 np.einsum
对于这种乘法和求和目的来说应该是非常有效的。在我们的例子中,我们可以使用它对 xvec_sum
情况进行一维求和,对 xvecsq_sum
情况进行平方和求和。实现看起来像这样 -
def sparseness_vectorized(A):
nsqrt = np.sqrt(A.shape[1])
B = np.einsum('ij->i',np.abs(A))/np.sqrt(np.einsum('ij,ij->i',A,A))
denom = nsqrt - B
enum = nsqrt - 1
return denom/enum
运行时测试 -
本节比较了迄今为止列出的所有解决问题的方法,包括问题中的方法。
In [235]: df = create(1000)
...: express_df = df.ix[:,1:]
...:
In [236]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 1.36 s per loop
In [237]: %timeit sparseness2(express_df.values)
1000 loops, best of 3: 247 µs per loop
In [238]: %timeit sparseness_vectorized(express_df.values)
1000 loops, best of 3: 231 µs per loop
In [239]: df = create(5000)
...: express_df = df.ix[:,1:]
...:
In [240]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 6.66 s per loop
In [241]: %timeit sparseness2(express_df.values)
1000 loops, best of 3: 1.14 ms per loop
In [242]: %timeit sparseness_vectorized(express_df.values)
1000 loops, best of 3: 1.06 ms per loop
我有一个按以下方式创建的 Pandas 数据框:
import pandas as pd
def create(n):
df = pd.DataFrame({ 'gene':["foo",
"bar",
"qux",
"woz"],
'cell1':[433.96,735.62,483.42,10.33],
'cell2':[94.93,2214.38,97.93,1205.30],
'cell3':[1500,90,100,80]})
df = df[["gene","cell1","cell2","cell3"]]
df = pd.concat([df]*n)
df = df.reset_index(drop=True)
return df
看起来像这样:
In [108]: create(1)
Out[108]:
gene cell1 cell2 cell3
0 foo 433.96 94.93 1500
1 bar 735.62 2214.38 90
2 qux 483.42 97.93 100
3 woz 10.33 1205.30 80
然后我有一个函数可以获取每个基因(行)的值 计算某个分数:
import numpy as np
def sparseness(xvec):
n = len(xvec)
xvec_sum = np.sum(np.abs(xvec))
xvecsq_sum = np.sum(np.square(xvec))
denom = np.sqrt(n) - (xvec_sum / np.sqrt(xvecsq_sum))
enum = np.sqrt(n) - 1
sparseness_x = denom/enum
return sparseness_x
实际上我需要在 40K 行上应用此函数。目前它正在运行 使用 Pandas 'apply':
非常慢In [109]: df = create(10000)
In [110]: express_df = df.ix[:,1:]
In [111]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 8.32 s per loop
实现它的更快替代方案是什么?
一种更快的方法是实现该函数的矢量化版本,它直接在二维 ndarray 上运行。这是非常可行的,因为 numpy 中的许多函数可以在二维 ndarray 上运行,使用 axis
参数控制。一个可能的实现:
def sparseness2(xs):
nr = np.sqrt(xs.shape[1])
a = np.sum(np.abs(xs), axis=1)
b = np.sqrt(np.sum(np.square(xs), axis=1))
sparseness = (nr - a/b) / (nr - 1)
return sparseness
res_arr = sparseness2(express_df.values)
res2 = pd.Series(res_arr, index=express_df.index)
一些测试:
from pandas.util.testing import assert_series_equal
res1 = express_df.apply(sparseness, axis=1)
assert_series_equal(res1, res2) #OK
%timeit sparseness2(express_df.values)
# 1000 loops, best of 3: 655 µs per loop
这是一种矢量化方法,它使用 np.einsum
在整个数据帧中一次执行所有这些操作。现在,这个 np.einsum
对于这种乘法和求和目的来说应该是非常有效的。在我们的例子中,我们可以使用它对 xvec_sum
情况进行一维求和,对 xvecsq_sum
情况进行平方和求和。实现看起来像这样 -
def sparseness_vectorized(A):
nsqrt = np.sqrt(A.shape[1])
B = np.einsum('ij->i',np.abs(A))/np.sqrt(np.einsum('ij,ij->i',A,A))
denom = nsqrt - B
enum = nsqrt - 1
return denom/enum
运行时测试 -
本节比较了迄今为止列出的所有解决问题的方法,包括问题中的方法。
In [235]: df = create(1000)
...: express_df = df.ix[:,1:]
...:
In [236]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 1.36 s per loop
In [237]: %timeit sparseness2(express_df.values)
1000 loops, best of 3: 247 µs per loop
In [238]: %timeit sparseness_vectorized(express_df.values)
1000 loops, best of 3: 231 µs per loop
In [239]: df = create(5000)
...: express_df = df.ix[:,1:]
...:
In [240]: %timeit express_df.apply(sparseness, axis=1)
1 loops, best of 3: 6.66 s per loop
In [241]: %timeit sparseness2(express_df.values)
1000 loops, best of 3: 1.14 ms per loop
In [242]: %timeit sparseness_vectorized(express_df.values)
1000 loops, best of 3: 1.06 ms per loop