この記事でまとめたテクニックは、以前に 10 Pandas でまとめた一般的なテクニックとは異なります。頻繁には使用しないかもしれませんが、非常に難しい問題に遭遇したとき、これらのテクニックは、まれな問題を迅速に解決するのに役立ちます。
デフォルトでは、限られた数のオプションを持つ列にオブジェクト タイプが割り当てられます。しかし、これはメモリの観点からは効率的な選択ではありません。これらの列にインデックスを付け、実際の値ではなくオブジェクトへの参照のみを使用できます。 Pandas は、この問題を解決するために Categorical と呼ばれる Dtype を提供します。
たとえば、画像パスを含む大規模なデータ セットで構成されます。各行には、アンカー、ポジティブ、ネガティブの 3 つの列があります。
カテゴリカル列に Categorical を使用すると、メモリ使用量を大幅に削減できます。
# raw data +----------+------------------------+ |class |filename| +----------+------------------------+ | Bathroom | Bathroombath_1.jpg| | Bathroom | Bathroombath_100.jpg| | Bathroom | Bathroombath_1003.jpg | | Bathroom | Bathroombath_1004.jpg | | Bathroom | Bathroombath_1005.jpg | +----------+------------------------+ # target +------------------------+------------------------+----------------------------+ | anchor |positive|negative| +------------------------+------------------------+----------------------------+ | Bathroombath_1.jpg| Bathroombath_100.jpg| Dinningdin_540.jpg| | Bathroombath_100.jpg| Bathroombath_1003.jpg | Dinningdin_1593.jpg | | Bathroombath_1003.jpg | Bathroombath_1004.jpg | Bedroombed_329.jpg| | Bathroombath_1004.jpg | Bathroombath_1005.jpg | Livingroomliving_1030.jpg | | Bathroombath_1005.jpg | Bathroombath_1007.jpg | Bedroombed_1240.jpg | +------------------------+------------------------+----------------------------+
ファイル名列の値は頻繁にコピーされます。したがって、Categorical を使用すると、メモリ使用量を大幅に削減できます。
ターゲット データ セットを読み取って、メモリ内の違いを見てみましょう:
triplets.info(memory_usage="deep") # Column Non-Null Count Dtype # --- ------ -------------- ----- # 0 anchor 525000 non-null category # 1 positive 525000 non-null category # 2 negative 525000 non-null category # dtypes: category(3) # memory usage: 4.6 MB # without categories triplets_raw.info(memory_usage="deep") # Column Non-Null Count Dtype # --- ------ -------------- ----- # 0 anchor 525000 non-null object # 1 positive 525000 non-null object # 2 negative 525000 non-null object # dtypes: object(3) # memory usage: 118.1 MB
この違いは非常に大きく、繰り返し回数が増加するにつれて、その差は非線形に増加します。
行-列変換の問題は SQL でよく発生します. Pandas もそれを必要とすることがあります. Kaggle コンテストのデータセットを見てみましょう。 census_start .csv ファイル:
ご覧のとおり、これらは年ごとに保存されています。列 year と pct_bb があり、各行に対応する値がある場合、良くなりますよ、たくさん、そうですね。
cols = sorted([col for col in original_df.columns if col.startswith("pct_bb")]) df = original_df[(["cfips"] + cols)] df = df.melt(id_vars="cfips", value_vars=cols, var_name="year", value_name="feature").sort_values(by=["cfips", "year"])
結果を見てください。これははるかに優れています:
前回紹介したように、このメソッドは各行を反復処理して指定されたメソッドを呼び出すため、使用しないことをお勧めします。しかし、他に選択肢がない場合、速度を上げる方法はあるのでしょうか?
swifter や pandaralrew などのパッケージを使用してプロセスを並列化できます。
import pandas as pd import swifter def target_function(row): return row * 10 def traditional_way(data): data['out'] = data['in'].apply(target_function) def swifter_way(data): data['out'] = data['in'].swifter.apply(target_function)
import pandas as pd from pandarallel import pandarallel def target_function(row): return row * 10 def traditional_way(data): data['out'] = data['in'].apply(target_function) def pandarallel_way(data): pandarallel.initialize() data['out'] = data['in'].parallel_apply(target_function)
マルチスレッド化することで計算速度を向上させることができます。もちろん、クラスターがある場合は dask を使用するのが最適です。または pyspark
標準の整数データ型は Null 値をサポートしていないため、浮動小数点数に自動的に変換されます。したがって、データの整数フィールドに null 値が必要な場合は、null 値を表すために pandas.NA を使用するため、Int64 データ型の使用を検討してください。
できるだけ寄木細工を選択してください。 Parquet はデータ型を保持するため、データを読み取るときに dtype を指定する必要はありません。 Parquet ファイルはデフォルトで snappy を使用して圧縮されるため、必要なディスク容量はほとんどありません。以下にいくつかの比較を示します。
|file|size | +------------------------+---------+ | triplets_525k.csv| 38.4 MB | | triplets_525k.csv.gzip |4.3 MB | | triplets_525k.csv.zip|4.5 MB | | triplets_525k.parquet|1.9 MB | +------------------------+---------+
parquet を読むには、pyarrow や fastparquet などの追加パッケージが必要です。 chatgpt は、pyarrow が fastparquet よりも速いと言っていますが、小規模なデータセットでテストしたとき、fastparquet は pyarrow よりも高速でしたが、pandas 2.0 もデフォルトで pyarrow を使用するため、ここでは pyarrow を使用することをお勧めします。
絶対値の取得、カウント、合計での除算など、相対頻度の計算は複雑ですが、value_counts を使用すると、このタスクをより簡単に実行できます。このメソッドには、NULL 値を含めるか除外するオプションが用意されています。
df = pd.DataFrame({"a": [1, 2, None], "b": [4., 5.1, 14.02]}) df["a"] = df["a"].astype("Int64") print(df.info()) print(df["a"].value_counts(normalize=True, dropna=False), df["a"].value_counts(normalize=True, dropna=True), sep="nn")
これはもっと簡単ではありませんか?
注: Modin はまだ存在します。テスト段階。
Pandas はシングルスレッドですが、Modin はパンダをスケーリングすることでワークフローを高速化できます。パンダが非常に遅くなったり、過剰なメモリ使用量によって OOM が発生したりする大規模なデータ セットで特にうまく機能します。
!pip install modin[all] import modin.pandas as pd df = pd.read_csv("my_dataset.csv")
以下は modin の公式 Web サイトのアーキテクチャ図です。興味がある場合は、
複雑な半構造化データが頻繁に発生し、そのデータから個々の列を分離する必要がある場合は、次の方法を使用できます:
import pandas as pd regex = (r'(?P<title>[A-Za-z's]+),' r'(?P<author>[A-Za-zs']+),' r'(?P<isbn>[d-]+),' r'(?P<year>d{4}),' r'(?P<publisher>.+)') addr = pd.Series([ "The Lost City of Amara,Olivia Garcia,978-1-234567-89-0,2023,HarperCollins", "The Alchemist's Daughter,Maxwell Greene,978-0-987654-32-1,2022,Penguin Random House", "The Last Voyage of the HMS Endeavour,Jessica Kim,978-5-432109-87-6,2021,Simon & Schuster", "The Ghosts of Summer House,Isabella Lee,978-3-456789-12-3,2000,Macmillan Publishers", "The Secret of the Blackthorn Manor,Emma Chen,978-9-876543-21-0,2023,Random House Children's Books" ]) addr.str.extract(regex)
这个技巧有人一次也用不到,但是有人可能就是需要,比如:在分析中包含PDF文件中的表格时。通常的方法是复制数据,粘贴到Excel中,导出到csv文件中,然后导入Pandas。但是,这里有一个更简单的解决方案:pd.read_clipboard()。我们所需要做的就是复制所需的数据并执行一个方法。
有读就可以写,所以还可以使用to_clipboard()方法导出到剪贴板。
但是要记住,这里的剪贴板是你运行python/jupyter主机的剪切板,并不可能跨主机粘贴,一定不要搞混了。
假设我们有这样一个数据集,这是一个相当典型的情况:
import pandas as pd df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "category": [["foo", "bar"], ["foo"], ["qux"]]}) # let's increase the number of rows in a dataframe df = pd.concat([df]*10000, ignore_index=True)
我们想将category分成多列显示,例如下面的
先看看最慢的apply:
def dummies_series_apply(df): return df.join(df['category'].apply(pd.Series) .stack() .str.get_dummies() .groupby(level=0) .sum()) .drop("category", axis=1) %timeit dummies_series_apply(df.copy()) #5.96 s ± 66.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
sklearn的MultiLabelBinarizer
from sklearn.preprocessing import MultiLabelBinarizer def sklearn_mlb(df): mlb = MultiLabelBinarizer() return df.join(pd.DataFrame(mlb.fit_transform(df['category']), columns=mlb.classes_)) .drop("category", axis=1) %timeit sklearn_mlb(df.copy()) #35.1 ms ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
是不是快了很多,我们还可以使用一般的向量化操作对其求和:
def dummies_vectorized(df): return pd.get_dummies(df.explode("category"), prefix="cat") .groupby(["a", "b"]) .sum() .reset_index() %timeit dummies_vectorized(df.copy()) #29.3 ms ± 1.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
使用第一个方法(在StackOverflow上的回答中非常常见)会给出一个非常慢的结果。而其他两个优化的方法的时间是非常快速的。
我希望每个人都能从这些技巧中学到一些新的东西。重要的是要记住尽可能使用向量化操作而不是apply()。此外,除了csv之外,还有其他有趣的存储数据集的方法。不要忘记使用分类数据类型,它可以节省大量内存。感谢阅读!
以上がPandas のための 10 の代替データ処理手法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。