Pandasでグループ化されたDataFrameにPython関数を適用する-計算を高速化するための最も効率的なアプローチは何ですか?


9

私は非常に大きなPandas DataFrameを処理しています-私のデータセットは次のdf設定に似ています:

import pandas as pd
import numpy  as np

#--------------------------------------------- SIZING PARAMETERS :
R1 =                    20        # .repeat( repeats = R1 )
R2 =                    10        # .repeat( repeats = R2 )
R3 =                541680        # .repeat( repeats = [ R3, R4 ] )
R4 =                576720        # .repeat( repeats = [ R3, R4 ] )
T  =                 55920        # .tile( , T)
A1 = np.arange( 0, 2708400, 100 ) # ~ 20x re-used
A2 = np.arange( 0, 2883600, 100 ) # ~ 20x re-used

#--------------------------------------------- DataFrame GENERATION :
df = pd.DataFrame.from_dict(
         { 'measurement_id':        np.repeat( [0, 1], repeats = [ R3, R4 ] ), 
           'time':np.concatenate( [ np.repeat( A1,     repeats = R1 ),
                                    np.repeat( A2,     repeats = R1 ) ] ), 
           'group':        np.tile( np.repeat( [0, 1], repeats = R2 ), T ),
           'object':       np.tile( np.arange( 0, R1 ),                T )
           }
        )

#--------------------------------------------- DataFrame RE-PROCESSING :
df = pd.concat( [ df,
                  df                                                  \
                    .groupby( ['measurement_id', 'time', 'group'] )    \
                    .apply( lambda x: np.random.uniform( 0, 100, 10 ) ) \
                    .explode()                                           \
                    .astype( 'float' )                                    \
                    .to_frame( 'var' )                                     \
                    .reset_index( drop = True )
                  ], axis = 1
                )

注:最小限の例を示すために、(たとえばを使用してdf.loc[df['time'] <= 400, :])簡単にサブセット化できますが、とにかくデータをシミュレートするため、元のサイズの方が全体像がよくなると思いました。

によって定義されたグループごとに['measurement_id', 'time', 'group']、次の関数を呼び出す必要があります。

from sklearn.cluster import SpectralClustering
from pandarallel     import pandarallel

def cluster( x, index ):
    if len( x ) >= 2:
        data = np.asarray( x )[:, np.newaxis]
        clustering = SpectralClustering( n_clusters   =  5,
                                         random_state = 42
                                         ).fit( data )
        return pd.Series( clustering.labels_ + 1, index = index )
    else:
        return pd.Series( np.nan, index = index )

パフォーマンスを向上させるために、2つの方法を試しました。

パンダラレルパッケージ

最初のアプローチは、pandarallelパッケージを使用して計算を並列化することでした:

pandarallel.initialize( progress_bar = True )
df \
  .groupby( ['measurement_id', 'time', 'group'] ) \
  .parallel_apply( lambda x: cluster( x['var'], x['object'] ) )

ただし、RAMを大量に消費し、すべてのコアが計算で使用されるわけではないため、これは最適ではないようです(pandarallel.initialize()メソッドでコアの数を明示的に指定した場合でも)。また、その理由(RAMの不足など)を見つける機会がなかったにもかかわらず、計算がさまざまなエラーで終了することがあります。

PySparkパンダUDF

私はSparkパンダUDFも試してみましたが、Sparkはまったく初めてです。これが私の試みです:

import findspark;  findspark.init()

from pyspark.sql           import SparkSession
from pyspark.conf          import SparkConf
from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types     import *

spark = SparkSession.builder.master( "local" ).appName( "test" ).config( conf = SparkConf() ).getOrCreate()
df = spark.createDataFrame( df )

@pandas_udf( StructType( [StructField( 'id', IntegerType(), True )] ), functionType = PandasUDFType.GROUPED_MAP )
def cluster( df ):
    if len( df['var'] ) >= 2:
        data = np.asarray( df['var'] )[:, np.newaxis]
        clustering = SpectralClustering( n_clusters   =  5,
                                         random_state = 42
                                         ).fit( data )
        return pd.DataFrame( clustering.labels_ + 1,
                             index = df['object']
                             )
    else:
        return pd.DataFrame( np.nan,
                             index = df['object']
                             )

res = df                                           \
        .groupBy( ['id_half', 'frame', 'team_id'] ) \
        .apply( cluster )                            \
        .toPandas()

残念ながら、パフォーマンスも満足のいくものではありませんでした。このトピックで読んだところ、これは、Pythonで記述されたUDF関数を使用する負担と、すべてのPythonオブジェクトをSparkオブジェクトに変換して戻すという関連する必要性にすぎない可能性があります。

だからここに私の質問があります:

  1. 可能性のあるボトルネックを排除し、パフォーマンスを向上させるために、私のアプローチのいずれかを調整できますか?(例:PySparkのセットアップ、最適でない操作の調整など)
  2. それらはより良い代替品ですか?パフォーマンスに関して、提供されたソリューションとどのように比較しますか?

2
あなたは夕暮れを研究しましたか?
Danila Ganchar

1
まだではありませんが、あなたの提案に感謝します-私はそれをやってみます
Kuba_

残念ながら私は動作しませんでしたdask(((私のコメントので、それは研究のためだけのアドバイスがある。
ダニラGanchar

パフォーマンスとは、計算を終了できる時間のことです。
Kuba_

回答:


1

Q「ボトルネックの可能性を排除してパフォーマンスを向上させるために、私のアプローチのいずれかを調整できますか?(例:PySparkのセットアップ、最適でない操作の調整など)

+1いずれかのコンピューティング戦略のセットアップ アドオンオーバーヘッドコストについて言及していただきました。これは常に損益分岐点を形成します。その後、非[SERIAL]戦略は、[TIME]ドメインのスピードアップが望まれるいくつかの有益な喜びを実現する可能性があります(ただし、他の場合、通常、[SPACE]ドメインのコストが許すか、実現可能なままです-はい、RAM。 ..このようなサイズのデバイス、予算、および他の同様の現実世界の制約の存在とアクセス)

まず、
離陸前の飛行前チェック
アムダールの法則
新しいオーバーヘッドに厳密な定式化は、現在これらのアドオンpSO + pTOオーバーヘッドの両方を組み込むことができ、ブレークイーブンを含む達成可能なスピードアップレベルの予測に反映しています。並列化することは(コスト/効果、効率の点で)意味があるため、

ここに画像の説明を入力してください

しかし、
それはここで私たちの中心的な問題ではありません
これが次に来る:

次に、ここで放射状ボルツマン関数カーネルを使用することになるの
計算コストを考えると、- コンポーネントは、定義により、いくつかの分離した作業単位を超えて-objectを分割することによる進歩はないようです。すべての-elementsを参照してください(参照:any-to-anyの値渡し-distributedトポロジの通信コストは、明白な理由により、-distributed処理の最悪のユースケースではないにしても、ひどいほど悪いです)(確かに不可解な、メモリレス/ステートレスでありながら、コンピューティングファブリックを除く)。SpectralClustering()~ exp( -gamma * distance( data, data )**2 )datadistance( data, data )data{ process | node }{ process | node }

知識をひけらかすアナリストのために、はい-これに追加します(そして、私たちはすでに言うかもしれない悪い状態)のコスト- 再び -すべてのツー任意のk平均 -processing、ここで約O( N^( 1 + 5 * 5 ) )行くこと、のためにN ~ len( data ) ~ 1.12E6+ひどくいくつかを持っている私たちの願いに対して、スマートで高速な処理。

だから何?

セットアップコストが無視されていないものの、増加した通信コストは、ほぼ上記のスケッチの試みを使用してから必ず無効に任意の改善のためにpure-から移動することになる[SERIAL]のいくつかのフォームへのプロセスフローだけ - [CONCURRENT]または真陽性[PARALLEL]いくつかの作業サブユニットのオーケストレーション、実装する必要がある(タンデムペアの) any-to-anyの値渡しトポロジに関連するオーバーヘッドが増加するため。

それが彼らのためではなかったら?

ええと、これはコンピューティングサイエンスの矛盾として聞こえます。たとえ可能であったとしても、事前に計算された多対多の距離の[TIME]コスト(これらの莫大な- ドメインの複雑性のコストは"事前に"かかります(どこ?方法?何かありますか?その他、避けられない待ち時間、完全な将来の多対多の距離行列のいくつかの(これまでのところ不明)増分ビルドによって潜在的な待ち時間マスキングを許可?))これらの主に存在するコストを他の場所に再配置します[TIME]-と- [SPACE]ドメイン。

Qもっと良い代替案はありますか?

私がこれまで気付いていた唯一の方法は、問題が別のQUBOで定式化された問題形式に再定式化できる場合は、試すことです(参照:Q uantum- U nconstrained- B inary- O ptimization 、良い知らせは、そうするためのツール、直接の知識の基盤、および実際的な問題解決の経験が存在し、より大きくなることです)

Qパフォーマンスに関して、提供されているソリューションとどのように比較しますか?

パフォーマンスは驚異的です-QUBOで定式化された問題には、O(1)一定の時間([TIME]-Domain)で有望な(!)ソルバーがあり、-Domainでいくぶん制限されてい[SPACE]ますサイズ)。


これは興味深い答えですが、要点を見逃しているようです。OPは単一のモデルではなく、複数の小さなモデルをトレーニングします。したがって、コアとなる観察はほとんど関係ありません。
user10938362

@ user10938362クレームされたプロパティ(小規模モデルのトレーニング)は、上記の投稿されたBig -O以外の処理コストの指標にどのように変換されますか?確かに多くの小さなモデルが理論的にだけ直線的に成長している合計約束(まだ)個々のビッグOコスト(Nで今より小さくはなく、他の要因で)処理を、まだ、あなたは、このすべての恐ろしく高価な合計に追加する必要がありますセットアップと終了の両方のオーバーヘッドコストとすべてのアドオン通信オーバーヘッドコストの追加コスト(パラメーター/データ/結果+通常、各ステップでのSER / DES処理コストのペア)
user3666197

0

これは答えではありませんが...

走れば

df.groupby(['measurement_id', 'time', 'group']).apply(
    lambda x: cluster(x['var'], x['object']))

(つまり、パンダだけの場合)、すでにいくつかのコアを使用していることがわかります。これは、sklearnjoblibデフォルトで作業を並列化するためです。あなたはできる DASKの賛成でスケジューラをスワップアウトし、おそらくスレッド間でデータを共有する上で、より効率を得るが、あまりにも長い間、あなたがやっている仕事は、次のようにCPUバウンドであるとして、あなたはそれをスピードアップするためにできることは何もありません。

要するに、これはアルゴリズムの問​​題です。それを計算するためのさまざまなフレームワークを検討する前に、実際に計算する必要があるものを理解してください。


あなたは説明してもらえますなぜあなたは言及しない「との間でデータを共有する... スレッド ...」が主催した作業分割後joblib-spawned プロセス、スレッドとは何の関係もないこと、少ない共有しましたか?議論を親切に説明していただきありがとうございます。
user3666197

正確には、jboblibは通常プロセスを使用しますが、代わりにdaskをバックエンドとして使用して、スレッドとプロセスの混合を選択できます。
mdurant

私は並列計算の初心者ですが、sklearnが並列化を使用していても、この設定では役に立たないのではないですか。つまり、各クラスタリング操作は10ポイントのみに適用されるため、sklearnによって実行される操作は非常に簡単です。繰り返しますが、ここでは間違っているかもしれませんが、元のデータのチャンクの処理を並列化する方法が本当の問題だと思います。
Kuba_

「それは、この設定では役に立たない」 -さて、あなたは1の代わりに、パフォーマンスの8つのCPUコア価値を使用する
mdurant

0

私はの専門家ではありませんDaskが、ベースラインとして次のコードを提供します。

import dask.dataframe as ddf

df = ddf.from_pandas(df, npartitions=4) # My PC has 4 cores

task = df.groupby(["measurement_id", "time", "group"]).apply(
    lambda x: cluster(x["var"], x["object"]),
    meta=pd.Series(np.nan, index=pd.Series([0, 1, 1, 1])),
)

res = task.compute()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.