Python 使用列的组合提高算术运算的性能
我有以下类型的数据帧-Python 使用列的组合提高算术运算的性能,python,pandas,numpy,numba,Python,Pandas,Numpy,Numba,我有以下类型的数据帧- df 我想进行以下操作- A_B A_C B_C -0.33 -0.5 -0.2 -0.11 -0.2 -0.09 A_B,A_C,B_C对应于- A_B: A-B/A+B A_C: A-C/A+C B_C: B-C/B+C 我正在使用- colnames = df.columns.tolist()[:-1] list_name=[] for i,c in enumerate(colnames): if i!=len(colnames):
df 我想进行以下操作-
A_B A_C B_C
-0.33 -0.5 -0.2
-0.11 -0.2 -0.09
A_B,A_C,B_C对应于-A_B: A-B/A+B
A_C: A-C/A+C
B_C: B-C/B+C
我正在使用-
colnames = df.columns.tolist()[:-1]
list_name=[]
for i,c in enumerate(colnames):
if i!=len(colnames):
for k in range(i+1,len(colnames)):
df[c+'_'+colnames[k]]=(df[c]-
df[colnames[k]])/(df[c]+df[colnames[k]])
list_name.append(c+'_'+colnames[k])
但问题是我的实际数据帧的大小是5*381
形状,因此A\u B、A\u C等的实际组合数是5*72390
形状,需要60分钟才能运行。
所以我试着把它转换成numpy数组,这样我就可以使用Numba对它进行优化,从而有效地计算它(),但我无法将它转换成numpy数组。
此外,解决这个问题的任何其他解决方案也受到欢迎 熊猫有一个内置的函数来实现这一点:df.values
import pandas as pd
df = pd.DataFrame({'A': [5, 20], 'B': [10, 25], 'C': [15,30]})
print(df.head())
# A B C
# 0 5 10 15
# 1 20 25 30
print(df.values)
# array([[ 5, 10, 15],
# [20, 25, 30]], dtype=int64)
以及随后的A_B、A_C和B_C的计算
def A_B(x):
return (x[0]-x[1])/(x[0]+x[1])
def A_C(x):
return (x[0]-x[2])/(x[0]+x[2])
def B_C(x):
return (x[1]-x[2])/(x[1]+x[2])
def combine(x):
return pd.DataFrame({'A_B': A_B(x), 'A_C': A_C(x), 'B_C': B_C(x)})
combine(df.values.T)
# A_B A_C B_C
# 0 -0.333333 -0.5 -0.200000
# 1 -0.111111 -0.2 -0.090909
使用:
首先将列的所有组合获取到2个列表中(a
表示元组的第一个值,b
表示第二个值):
然后用于按列表重复列:
df1 = df.loc[:, a]
print (df1)
A A B
0 5 5 10
1 20 20 25
df2 = df.loc[:, b]
print (df2)
B C C
0 10 15 15
1 25 30 30
将值转换为最终数据帧的numpy数组,并通过列表理解获取新列名:
c = [f'{x}_{y}' for x, y in zip(a, b)]
arr1 = df1.values
arr2 = df2.values
df = pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c)
print (df)
A_B A_C B_C
0 -0.333333 -0.5 -0.200000
1 -0.111111 -0.2 -0.090909
另一种解决方案非常类似,仅通过arange
按列的长度创建组合,最后一个新列名称通过索引创建:
from itertools import combinations
a, b = zip(*(combinations(np.arange(len(df.columns)), 2)))
arr = df.values
cols = df.columns.values
arr1 = arr[:, a]
arr2 = arr[:, b]
c = [f'{x}_{y}' for x, y in zip(cols[np.array(a)], cols[np.array(b)])]
df = pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c)
性能:
在5行381列中测试:
np.random.seed(2019)
df = pd.DataFrame(np.random.randint(10,100,(5,381)))
df.columns = ['c'+str(i+1) for i in range(df.shape[1])]
#print (df)
下面是一个使用NumPy的应用程序,它具有强大的切片功能
-
def func1(df):
a = df.values
n = a.shape[1]
L = n*(n-1)//2
idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() ))
start, stop = idx[:-1], idx[1:]
c = df.columns.values.astype(str)
d = 2*int(''.join(x for x in str(c.dtype) if x.isdigit()))+1
outc = np.empty(L,dtype='S'+str(2*d+1))
out = np.empty((a.shape[0],L))
for i,(s0,s1) in enumerate(zip(start, stop)):
outc[s0:s1] = np.char.add(c[i]+'_',c[i+1:])
out[:,s0:s1] = (a[:,i,None]-a[:,i+1:])/(a[:,i,None]+a[:,i+1:])
return pd.DataFrame(out,columns=outc)
样本运行-
In [361]: df
Out[361]:
A B C
0 5 10 15
1 20 25 30
In [362]: func1(df)
Out[362]:
A_B A_C B_C
0 -0.333333 -0.5 -0.200000
1 -0.111111 -0.2 -0.090909
5 x 381
随机数组上的计时-
In [147]: df = cdf(np.random.randint(10,100,(5,381)))
...: df.columns = ['c'+str(i+1) for i in range(df.shape[1])]
# @jezrael's soln
In [148]: %%timeit
...: a, b = zip(*(combinations(df.columns, 2)))
...: df1 = df.loc[:, a]
...: df2 = df.loc[:, b]
...: c = [x+'_'+y for x, y in zip(a, b)]
...: pd.DataFrame((df1.values-df2.values)/(df1.values+df2.values), columns=c)
10 loops, best of 3: 58.1 ms per loop
# From this post
In [149]: %timeit func1(df)
10 loops, best of 3: 22.6 ms per loop
是的,但是如何对获得的数组执行A-B/A+B
和类似的步骤。@Bing,您问过如何将其转换为np.ndarray
。鼓励人们自己解决问题。我们来这里是为了帮助和指引人们正确的方向,而不是去做s.o.工作。是的,但实际的问题是优化,而不是转换,如果你能观察到的话。这是天才,这真的是天才。@Bing-嗯,这要看情况而定。顺便说一句,是否有可能更改问题的标题,例如通过列名组合提高算术运算的性能
?为输出获得正确的列名是否很重要?@Divakar是的,这很重要。您是否能够测试您这边发布的方法?@Divakar是的,它工作得很好。谢谢。我还没有测试这个方法,测试后会再次更新。@Bing稍微修改了我的方法以提高性能。另外,添加了计时,希望它们能与您的测试用例结果相比较。只需修改不重复调用的答案。值,是否可以添加计时?谢谢。@jezrael似乎并没有太大的时间变化,事实上,随着新的变化,它似乎稍微慢了一点。在你这边试试看?@Divakar-它似乎还取决于行数的计时,用1000行测试,速度稍快一些(在python 3.6、0.24.1、win7下)
In [4]: %%timeit
...: a, b = zip(*(combinations(np.arange(len(df.columns)), 2)))
...: arr = df.values
...: cols = df.columns.values
...: arr1 = arr[:, a]
...: arr2 = arr[:, b]
...: c = [f'{x}_{y}' for x, y in zip(cols[np.array(a)], cols[np.array(b)])]
...: pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c)
...:
62 ms ± 7.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [5]: %%timeit
...: a, b = zip(*(combinations(df.columns, 2)))
...: df1 = df.loc[:, a]
...: df2 = df.loc[:, b]
...: arr1 = df1.values
...: arr2 = df2.values
...: c = [f'{x}_{y}' for x, y in zip(a, b)]
...: pd.DataFrame((arr1-arr2)/(arr1+arr2), columns=c)
...:
63.2 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [7]: %%timeit
...: func1(df)
...:
89.2 ms ± 331 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [8]: %%timeit
...: a, b = zip(*(combinations(df.columns, 2)))
...: df1 = df.loc[:, a]
...: df2 = df.loc[:, b]
...: c = [f'{x}_{y}' for x, y in zip(a, b)]
...: pd.DataFrame((df1.values-df2.values)/(df1.values+df2.values), columns=c)
...:
69.8 ms ± 6.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
def func1(df):
a = df.values
n = a.shape[1]
L = n*(n-1)//2
idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() ))
start, stop = idx[:-1], idx[1:]
c = df.columns.values.astype(str)
d = 2*int(''.join(x for x in str(c.dtype) if x.isdigit()))+1
outc = np.empty(L,dtype='S'+str(2*d+1))
out = np.empty((a.shape[0],L))
for i,(s0,s1) in enumerate(zip(start, stop)):
outc[s0:s1] = np.char.add(c[i]+'_',c[i+1:])
out[:,s0:s1] = (a[:,i,None]-a[:,i+1:])/(a[:,i,None]+a[:,i+1:])
return pd.DataFrame(out,columns=outc)
In [361]: df
Out[361]:
A B C
0 5 10 15
1 20 25 30
In [362]: func1(df)
Out[362]:
A_B A_C B_C
0 -0.333333 -0.5 -0.200000
1 -0.111111 -0.2 -0.090909
In [147]: df = cdf(np.random.randint(10,100,(5,381)))
...: df.columns = ['c'+str(i+1) for i in range(df.shape[1])]
# @jezrael's soln
In [148]: %%timeit
...: a, b = zip(*(combinations(df.columns, 2)))
...: df1 = df.loc[:, a]
...: df2 = df.loc[:, b]
...: c = [x+'_'+y for x, y in zip(a, b)]
...: pd.DataFrame((df1.values-df2.values)/(df1.values+df2.values), columns=c)
10 loops, best of 3: 58.1 ms per loop
# From this post
In [149]: %timeit func1(df)
10 loops, best of 3: 22.6 ms per loop