表形式データで Pandas を使用したことがある場合は、データをインポートし、クリーンアップして変換し、それをモデルへの入力として使用するプロセスに精通しているかもしれません。ただし、コードをスケールして運用環境に導入する必要がある場合、Pandas パイプラインがクラッシュし始め、動作が遅くなる可能性が高くなります。この記事では、Pandas コードの実行を高速化し、データ処理の効率を向上させ、よくある落とし穴を回避するのに役立つ 2 つのヒントを紹介します。
Pandas では、ベクトル化操作は、ボックスの列を使用せずに、より簡潔な方法でデータ全体を処理できる効率的なツールです。行ごとにループします。
ブロードキャストはベクトル化操作の重要な要素であり、これによりさまざまな形状のオブジェクトを直感的に操作できるようになります。
例 1: 3 つの要素を持つ配列 a にスカラー b を乗算すると、ソースと同じ形状の配列が得られます。
例 2: 加算演算を実行する場合、形状 (4,1) の配列 a と形状 (3,) の配列 b を加算すると、結果は次の配列になります。形状 (4,3)。
これについては、特に大規模な行列の乗算が一般的な深層学習において、多くの記事で議論されています。この記事では 2 つの簡単な例について説明します。
まず、特定の整数が列に出現する回数を数えたいとします。考えられる方法は 2 つあります。
"""计算DataFrame X 中 "column_1" 列中等于目标值 target 的元素个数。参数:X: DataFrame,包含要计算的列 "column_1"。target: int,目标值。返回值:int,等于目标值 target 的元素个数。"""# 使用循环计数def count_loop(X, target: int) -> int:return sum(x == target for x in X["column_1"])# 使用矢量化操作计数def count_vectorized(X, target: int) -> int:return (X["column_1"] == target).sum()
次に、日付列を含む DataFrame があり、それを指定された日数だけオフセットしたいとします。ベクトル化された演算を使用した計算は次のとおりです。
def offset_loop(X, days: int) -> pd.DataFrame:d = pd.Timedelta(days=days)X["column_const"] = [x + d for x in X["column_10"]]return Xdef offset_vectorized(X, days: int) -> pd.DataFrame:X["column_const"] = X["column_10"] + pd.Timedelta(days=days)return X
反復を行う最初の最も直感的な方法は、Python for を使用することです。ループ。
def loop(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:res = []i_remove_col = df.columns.get_loc(remove_col)i_words_to_remove_col = df.columns.get_loc(words_to_remove_col)for i_row in range(df.shape[0]):res.append(remove_words(df.iat[i_row, i_remove_col], df.iat[i_row, i_words_to_remove_col]))return result
def apply(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:return df.apply(func=lambda x: remove_words(x[remove_col], x[words_to_remove_col]), axis=1).tolist()
df.apply の各反復で、提供された呼び出し可能関数は、インデックスが df.columns で値が行である Series を取得します。これは、パンダがループごとにシーケンスを生成する必要があり、コストがかかることを意味します。コストを削減するには、次のように、使用することがわかっている df のサブセットに対して apply を呼び出すことをお勧めします。
def apply_only_used_cols(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:return df[[remove_col, words_to_remove_col]].apply(func=lambda x: remove_words(x[remove_col], x[words_to_remove_col]), axis=1)
リストと組み合わせた itertuples を使用した反復は、確実に実行されます。より良く働きます。 itertuples は、行データを含む (名前付き) タプルを生成します。
def itertuples_only_used_cols(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:return [remove_words(x[0], x[1])for x in df[[remove_col, words_to_remove_col]].itertuples(index=False, name=None)]
zip は反復可能オブジェクトを受け取り、タプルを生成します。i 番目のタプルには、指定された反復可能オブジェクトの i 番目の要素がすべて順番に含まれます。
def zip_only_used_cols(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:return [remove_words(x, y) for x, y in zip(df[remove_col], df[words_to_remove_col])]
def to_dict_only_used_columns(df: pd.DataFrame) -> list[str]:return [remove_words(row[remove_col], row[words_to_remove_col])for row in df[[remove_col, words_to_remove_col]].to_dict(orient="records")]
これまでに説明した反復手法に加えて、他の 2 つの方法もコードのパフォーマンスの向上に役立ちます。そして並列化。キャッシュは、同じパラメーターを使用して pandas 関数を複数回呼び出す場合に特に便利です。たとえば、多くの重複値を含むデータセットにremove_wordsが適用された場合、functools.lru_cacheを使用して関数の結果を保存し、毎回再計算する必要がなくなります。 lru_cache を使用するには、remove_words の宣言に @lru_cache デコレーターを追加し、好みの反復方法を使用して関数をデータセットに適用するだけです。これにより、コードの速度と効率が大幅に向上します。次のコードを例に挙げます。
@lru_cachedef remove_words(...):... # Same implementation as beforedef zip_only_used_cols_cached(df: pd.DataFrame, remove_col: str, words_to_remove_col: str) -> list[str]:return [remove_words(x, y) for x, y in zip(df[remove_col], df[words_to_remove_col])]
このデコレータを追加すると、以前に遭遇した入力の出力を「記憶」する関数が生成され、すべてのコードを再度実行する必要がなくなります。
最後の切り札は、pandarallel を使用して、複数の独立した df ブロックにわたって関数呼び出しを並列化することです。このツールの使い方は簡単です。インポートして初期化し、すべての .applys を .Parallel_applys に変更するだけです。
rree以上がPandas コードの効率を向上させるための 2 つの素晴らしいヒントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。