我仅修改get_diff
。要点是将stack
移到get_diff
的外面,并采用stack
的优势,即降低NaN
以避免在get_diff
内部进行过滤。
我正在尝试根据信息对及其先前的值在数据框中创建一个新列。尽管我运行的代码是正确的,并且可以提供所需的结果,但是在大型数据帧上运行它的速度非常慢。因此,我怀疑我没有使用所有Python功能来完成此任务。在Python中,有没有更有效,更快速的方法?]]
为了让您了解具体情况,让我向您解释一下我要寻找的内容:
我有一个描述比赛结果的数据框,对于每个'date'
,您可以看到参加比赛的'type',其得分称为'xx'。我的代码所做的是获取每个'日期'的'类型'之间的得分'xx'的差,然后获得所有类型彼此竞争的先前比赛结果的差之和。过去('win_comp_past_difs')。
下面您将看到数据和模型及其输出。
## I. DATA AND MODEL ##
I.1。数据
import pandas as pd import numpy as np idx = [np.array(['Jan-18', 'Jan-18', 'Feb-18', 'Mar-18', 'Mar-18', 'Mar-18','Mar-18', 'Mar-18', 'May-18', 'Jun-18', 'Jun-18', 'Jun-18','Jul-18', 'Aug-18', 'Aug-18', 'Sep-18', 'Sep-18', 'Oct-18','Oct-18', 'Oct-18', 'Nov-18', 'Dec-18', 'Dec-18',]),np.array(['A', 'B', 'B', 'A', 'B', 'C', 'D', 'E', 'B', 'A', 'B', 'C','A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'A', 'B', 'C'])] data = [{'xx': 1}, {'xx': 5}, {'xx': 3}, {'xx': 2}, {'xx': 7}, {'xx': 3},{'xx': 1}, {'xx': 6}, {'xx': 3}, {'xx': 5}, {'xx': 2}, {'xx': 3},{'xx': 1}, {'xx': 9}, {'xx': 3}, {'xx': 2}, {'xx': 7}, {'xx': 3}, {'xx': 6}, {'xx': 8}, {'xx': 2}, {'xx': 7}, {'xx': 9}] df = pd.DataFrame(data, index=idx, columns=['xx']) df.index.names=['date','type'] df=df.reset_index() df['date'] = pd.to_datetime(df['date'],format = '%b-%y') df=df.set_index(['date','type']) df['xx'] = df.xx.astype('float')
看起来像这样:
(在大型数据框中非常慢]xx date type 2018-01-01 A 1.0 B 5.0 2018-02-01 B 3.0 2018-03-01 A 2.0 B 7.0 C 3.0 D 1.0 E 6.0 2018-05-01 B 3.0 2018-06-01 A 5.0 B 2.0 C 3.0 2018-07-01 A 1.0 2018-08-01 B 9.0 C 3.0 2018-09-01 A 2.0 B 7.0 2018-10-01 C 3.0 A 6.0 B 8.0 2018-11-01 A 2.0 2018-12-01 B 7.0 C 9.0
I.2。型号
# get differences of pairs, useful for win counts and win_difs def get_diff(x): teams = x.index.get_level_values(1) tmp = pd.DataFrame(x[:,None]-x[None,:],columns = teams.values,index=teams.values).stack() return tmp[tmp.index.get_level_values(0)!=tmp.index.get_level_values(1)] new_df = df.groupby('date').xx.apply(get_diff).to_frame() # group by players groups = new_df.groupby(level=[1,2]) # sum function def cumsum_shift(x): return x.cumsum().shift() # assign new values df['win_comp_past_difs'] = groups.xx.apply(cumsum_shift).sum(level=[0,1])
下面您将看到模型的输出是什么样子:
。xx win_comp_past_difs date type 2018-01-01 A 1.0 0.0 B 5.0 0.0 2018-02-01 B 3.0 NaN 2018-03-01 A 2.0 -4.0 B 7.0 4.0 C 3.0 0.0 D 1.0 0.0 E 6.0 0.0 2018-05-01 B 3.0 NaN 2018-06-01 A 5.0 -10.0 B 2.0 13.0 C 3.0 -3.0 2018-07-01 A 1.0 NaN 2018-08-01 B 9.0 3.0 C 3.0 -3.0 2018-09-01 A 2.0 -6.0 B 7.0 6.0 2018-10-01 C 3.0 -10.0 A 6.0 -10.0 B 8.0 20.0 2018-11-01 A 2.0 NaN 2018-12-01 B 7.0 14.0 C 9.0 -14.0
如果您难以理解用户定义的函数(def)的功能,请在下面让我向您解释
对于这个海豚,我将与数据框的groupby中的一组一起工作。
下面您将看到有关用户定义功能的工作方式的说明。
## II. EXPLANATION OF THE USER-DEFINED FUNCTION ##
所以,让您看一下用户定义函数的工作方式,让我选择groupby的特定组。
II.1选择特定的组
gb = df.groupby('date') gb2 = gb.get_group((list(gb.groups)[2]))
看起来像这样:
xx date type 2018-03-01 A 2.0 B 7.0 C 3.0 D 1.0 E 6.0
II.2创建竞争对手(团队)列表]]
teams = gb2.index.get_level_values(1)
II.3创建“类型”之间的“ xx”差异的数据框。
df_comp= pd.DataFrame(gb2.xx[:,None]-gb2.xx[None,:],columns = teams.values,index=teams.values)
看起来像这样:
A B C D E A 0.0 -5.0 -1.0 1.0 -4.0 B 5.0 0.0 4.0 6.0 1.0 C 1.0 -4.0 0.0 2.0 -3.0 D -1.0 -6.0 -2.0 0.0 -5.0 E 4.0 -1.0 3.0 5.0 0.0
至此,我使用stack()函数作为返回原始数据帧的中间步骤。其余的您可以在I.数据和模型中遵循它。
如果您能详细说明代码以使其更高效并更快地执行,我将不胜感激。
我正在尝试根据信息对及其先前的值在数据框中创建一个新列。尽管我运行的代码是正确的,并且可以提供所需的结果,但是当我...
我仅修改get_diff
。要点是将stack
移到get_diff
的外面,并采用stack
的优势,即降低NaN
以避免在get_diff
内部进行过滤。
新的get_diff_s
使用np.fill
将所有对角线值填充到NaN
,并返回一个数据框,而不是过滤后的序列。
def get_diff_s(x):
teams = x.index.get_level_values(1)
arr = x[:,None]-x[None,:]
np.fill_diagonal(arr, np.nan)
return pd.DataFrame(arr,columns = teams.values,index=teams.values)
df['win_comp_past_difs'] = (df.groupby('date').xx.apply(get_diff_s)
.groupby(level=1).cumsum().stack()
.groupby(level=[1,2]).shift().sum(level=[0, 1]))
Out[1348]:
xx win_comp_past_difs
date type
2018-01-01 A 1.0 0.0
B 5.0 0.0
2018-02-01 B 3.0 NaN
2018-03-01 A 2.0 -4.0
B 7.0 4.0
C 3.0 0.0
D 1.0 0.0
E 6.0 0.0
2018-05-01 B 3.0 NaN
2018-06-01 A 5.0 -10.0
B 2.0 13.0
C 3.0 -3.0
2018-07-01 A 1.0 NaN
2018-08-01 B 9.0 3.0
C 3.0 -3.0
2018-09-01 A 2.0 -6.0
B 7.0 6.0
2018-10-01 C 3.0 -10.0
A 6.0 -10.0
B 8.0 20.0
2018-11-01 A 2.0 NaN
2018-12-01 B 7.0 14.0
C 9.0 -14.0
Timing
:原始解决方案:(我将您的所有命令都链接成一行)
In [1352]: %timeit df.groupby('date').xx.apply(get_diff).groupby(level=[1,2]).a ...: pply(lambda x: x.cumsum().shift()).sum(level=[0,1]) 82.9 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
修订的解决方案:
In [1353]: %timeit df.groupby('date').xx.apply(get_diff_s).groupby(level=1).cum ...: sum().stack().groupby(level=[1,2]).shift().sum(level=[0,1]) 47.1 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
因此,在样本数据上,速度提高了约40%。但是,我不知道它如何在您的真实数据集中执行]
您的多层索引会产生巨大的开销。
我认为解决此问题的最佳方法是通过并行处理不同线程中的每个groupby
。我在这里有关于我的话题,可能会有帮助。
作为替代,您可以通过自己管理索引来减少索引开销。
f, s, t, d = [], [], [], [] for _, sub in df.groupby('date').xx: date = sub.index.get_level_values(0) i = sub.index.get_level_values(1) tmp = (sub.values[:, None] - sub.values).ravel() f.extend(np.repeat(i, len(i))) s.extend(np.tile(i, len(i))) t.extend(tmp) d.extend(np.repeat(date, len(i)))
然后过滤并处理
cumsum
+sum
。inter = pd.DataFrame({'i0': d, 'i1': f, 'i2': s, 'i3': t}).query('i1 != i2') df['rf'] = inter.assign(v=inter.groupby(['i1','i2']).i3.apply(lambda s: s.cumsum().shift())).set_index(['i0', 'i1']).v.sum(level=[0,1])
即使对于巨大的数据帧,第二个块也应该运行得非常快。繁重的处理工作在
groupby
中,这就是为什么映射减少/多处理方法可能超级有用的原因。在这种情况下,手动索引处理的增强速度大约快5倍
1 loop, best of 3: 3.5 s per loop 1 loop, best of 3: 738 ms per loop
想法是尝试为您提供一些改进方面的指导。这些操作是独立的,因此在不同的线程中执行每个迭代应该是可行的。您也可以考虑
numba
。
我仅修改get_diff
。要点是将stack
移到get_diff
的外面,并采用stack
的优势,即降低NaN
以避免在get_diff
内部进行过滤。
您的多层索引会产生巨大的开销。
我认为解决此问题的最佳方法是通过并行处理不同线程中的每个groupby
。我在这里有关于我的话题,可能会有帮助。