python pandas 中的 Groupby:快速方法
Groupby in python pandas: Fast Way
我想改善 python pandas 中 groupby
的时间。
我有这个代码:
df["Nbcontrats"] = df.groupby(['Client', 'Month'])['Contrat'].transform(len)
objective是计算一个客户在一个月内有多少合同,并将此信息添加到新列中(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)
如何缩短执行时间?
这是一种继续进行的方法:
将输入数据框中的相关列 (['Client', 'Month']
) 切片到 NumPy 数组中。这主要是一个以性能为中心的想法,因为我们稍后将使用 NumPy 函数,这些函数经过优化以与 NumPy 数组一起使用。
将 ['Client', 'Month']
中的两列数据转换为单个 1D
数组,将两列中的元素视为对,这将是一个等效的线性索引。因此,我们可以假设 'Client'
中的元素表示行索引,而 'Month'
中的元素是列索引。这就像从 2D
到 1D
。但是,问题在于决定二维网格的形状以执行此类映射。为了覆盖所有对,一个安全的假设是假设一个二维网格,由于 Python 中基于 0 的索引,其维度比每列的最大值多一维。因此,我们将得到线性指数。
接下来,我们根据每个线性索引的独特性对其进行标记。我认为这将对应于使用 grouby
获得的密钥。我们还需要沿该一维数组的整个长度获取每个 group/unique 键的计数。最后,使用这些标签对计数进行索引应该为每个元素映射相应的计数。
这就是关于它的全部想法!这是实现 -
# 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
使用DataFrameGroupBy.size
方法:
df.set_index(['Client', 'Month'], inplace=True)
df['Nbcontrats'] = df.groupby(level=(0,1)).size()
df.reset_index(inplace=True)
大部分工作是将结果分配回源 DataFrame 的列。
我想改善 python pandas 中 groupby
的时间。
我有这个代码:
df["Nbcontrats"] = df.groupby(['Client', 'Month'])['Contrat'].transform(len)
objective是计算一个客户在一个月内有多少合同,并将此信息添加到新列中(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)
如何缩短执行时间?
这是一种继续进行的方法:
将输入数据框中的相关列 (
['Client', 'Month']
) 切片到 NumPy 数组中。这主要是一个以性能为中心的想法,因为我们稍后将使用 NumPy 函数,这些函数经过优化以与 NumPy 数组一起使用。将
['Client', 'Month']
中的两列数据转换为单个1D
数组,将两列中的元素视为对,这将是一个等效的线性索引。因此,我们可以假设'Client'
中的元素表示行索引,而'Month'
中的元素是列索引。这就像从2D
到1D
。但是,问题在于决定二维网格的形状以执行此类映射。为了覆盖所有对,一个安全的假设是假设一个二维网格,由于 Python 中基于 0 的索引,其维度比每列的最大值多一维。因此,我们将得到线性指数。接下来,我们根据每个线性索引的独特性对其进行标记。我认为这将对应于使用
grouby
获得的密钥。我们还需要沿该一维数组的整个长度获取每个 group/unique 键的计数。最后,使用这些标签对计数进行索引应该为每个元素映射相应的计数。
这就是关于它的全部想法!这是实现 -
# 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
使用DataFrameGroupBy.size
方法:
df.set_index(['Client', 'Month'], inplace=True)
df['Nbcontrats'] = df.groupby(level=(0,1)).size()
df.reset_index(inplace=True)
大部分工作是将结果分配回源 DataFrame 的列。