Python pandas 中的 Groupby:快速方式

问题描述 投票:0回答:3

我想改进 python pandas 中

groupby
的时间。 我有这个代码:

df["Nbcontrats"] = df.groupby(['Client', 'Month'])['Contrat'].transform(len)

目标是计算客户一个月内拥有多少份合同,并将此信息添加到新列中 (

Nbcontrats
)。

  • Client
    :客户端代码
  • Month
    :数据提取月份
  • Contrat
    :合约号

我想改善时间。下面我仅使用真实数据的子集:

%timeit df["Nbcontrats"] = df.groupby(['Client', 'Month'])['Contrat'].transform(len)
1 loops, best of 3: 391 ms per loop

df.shape
Out[309]: (7464, 61)

如何提高执行时间?

python pandas numpy pandas-groupby
3个回答
49
投票

这是一种继续方法:

  • 将输入数据帧中的相关列 (

    ['Client', 'Month']
    ) 切出到 NumPy 数组中。这主要是一个以性能为中心的想法,因为我们稍后将使用 NumPy 函数,这些函数经过优化以与 NumPy 数组一起使用。

  • ['Client', 'Month']
    中的两列数据转换为单个
    1D
    数组,这将是相当于将两列中的元素视为对的线性索引。因此,我们可以假设
    'Client'
    中的元素表示行索引,而
    'Month'
    元素是列索引。这就像从
    2D
    1D
    。但是,问题在于决定执行此类映射的二维网格的形状。为了覆盖所有对,一个安全的假设是假设一个 2D 网格,由于 Python 中基于 0 的索引,其尺寸比每列的最大值大一。因此,我们将得到线性索引。

  • 接下来,我们根据每个线性索引在其他索引中的唯一性来标记它们。我认为这将对应于使用

    grouby
    获得的密钥。我们还需要获取沿该一维数组的整个长度的每个组/唯一键的计数。最后,使用这些标签对计数进行索引应该为每个元素映射相应的计数。

这就是整个想法!这是实现 -

# Save relevant columns as a NumPy array for performing NumPy operations afterwards
arr_slice = df[['Client', 'Month']].values

# Get linear indices equivalent of those columns
lidx = np.ravel_multi_index(arr_slice.T,arr_slice.max(0)+1)

# Get unique IDs corresponding to each linear index (i.e. group) and grouped counts
unq,unqtags,counts = np.unique(lidx,return_inverse=True,return_counts=True)

# Index counts with the unique tags to map across all elements with the counts
df["Nbcontrats"] = counts[unqtags]

运行时测试

1)定义函数:

def original_app(df):
    df["Nbcontrats"] = df.groupby(['Client', 'Month'])['Contrat'].transform(len)

def vectorized_app(df):
    arr_slice = df[['Client', 'Month']].values
    lidx = np.ravel_multi_index(arr_slice.T,arr_slice.max(0)+1)
    unq,unqtags,counts = np.unique(lidx,return_inverse=True,return_counts=True)
    df["Nbcontrats"] = counts[unqtags]

2)验证结果:

In [143]: # Let's create a dataframe with 100 unique IDs and of length 10000
     ...: arr = np.random.randint(0,100,(10000,3))
     ...: df = pd.DataFrame(arr,columns=['Client','Month','Contrat'])
     ...: df1 = df.copy()
     ...: 
     ...: # Run the function on the inputs
     ...: original_app(df)
     ...: vectorized_app(df1)
     ...: 

In [144]: np.allclose(df["Nbcontrats"],df1["Nbcontrats"])
Out[144]: True

3)最后给他们计时:

In [145]: # Let's create a dataframe with 100 unique IDs and of length 10000
     ...: arr = np.random.randint(0,100,(10000,3))
     ...: df = pd.DataFrame(arr,columns=['Client','Month','Contrat'])
     ...: df1 = df.copy()
     ...: 

In [146]: %timeit original_app(df)
1 loops, best of 3: 645 ms per loop

In [147]: %timeit vectorized_app(df1)
100 loops, best of 3: 2.62 ms per loop

5
投票

使用

DataFrameGroupBy.size
方法:

df.set_index(['Client', 'Month'], inplace=True)
df['Nbcontrats'] = df.groupby(level=(0,1)).size()
df.reset_index(inplace=True)

大部分工作是将结果分配回源 DataFrame 的列中。


0
投票

尝试swifter:一个以最快的可用方式将任何功能有效地应用于pandas数据框或系列的包。

import swifter
df["Nbcontrats"] = df.swifter.groupby(['Client', 'Month'])['Contrat'].transform(len)
© www.soinside.com 2019 - 2024. All rights reserved.