20

groupbyPython pandasの a の時間を改善したい。私はこのコードを持っています:

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)

実行時間を改善するにはどうすればよいですか?

4

2 に答える 2

44

続行する方法の 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
于 2016-07-01T12:10:26.427 に答える
4

DataFrameGroupBy.sizeメソッドで:

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

ほとんどの作業は、結果をソース DataFrame の列に戻すことに費やされます。

于 2016-07-01T23:01:23.523 に答える