続行する方法の 1 つを次に示します。
関連する列 ( ['Client', 'Month']
) を入力データフレームから NumPy 配列に切り出します。これは、NumPy 配列で動作するように最適化された NumPy 関数を後で使用するため、主にパフォーマンスに焦点を当てたアイデアです。
2 つの列のデータ['Client', 'Month']
を 1 つの1D
配列に変換します。これは、2 つの列の要素をペアとして考慮した線形インデックスに相当します。'Client'
したがって、からの要素は行インデックスを表し、要素は列インデックスであると仮定でき'Month'
ます。これは から2D
に行くようなもの1D
です。しかし、問題は、そのようなマッピングを実行するための 2D グリッドの形状を決定することです。すべてのペアをカバーするために、1 つの安全な仮定は、Python の 0 ベースのインデックス付けにより、次元が各列に沿って最大値より 1 大きい 2D グリッドを想定することです。したがって、線形インデックスが得られます。
次に、とりわけ一意性に基づいて各線形インデックスにタグを付けます。これは、代わりに取得したキーに対応すると思いますgrouby
。また、その 1D 配列の全長に沿って各グループ/一意のキーの数を取得する必要があります。最後に、これらのタグを使用してカウントにインデックスを付けると、各要素にそれぞれのカウントがマップされます。
それはそれについての全体的なアイデアです!これが実装です-
# 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