NumPy データ処理: 実践ガイド
NumPyによるデータ処理とは、NumPyライブラリを使って数値配列の読み込み、形状変更、クリーニング、縮約を行う実践であり、その中核となるndarrayオブジェクトは同種のデータを固定サイズの連続したメモリブロックに格納します。2006年のリリース以来、NumPyはSciPy、pandas、scikit-learn、そしてほとんどの科学的Pythonの基盤となっているため、ここで身につけた習慣はあらゆる場面に波及します。
主なポイント
ndarrayはフラットなメモリバッファ上へのストライド付きビューです。ストライドを理解すれば、reshape、スライシング、transposeが安価である一方、copyやファンシーインデックスがそうでない理由が説明できます。- ベクトル化は単なるスタイルの問題ではありません。ループをインタプリタされたPythonからコンパイルされたCへ移すものであり、計算負荷の高い処理では通常1〜2桁の高速化が得られます。
- 大規模なシミュレーション配列では、メモリレイアウト(
C順 vsF順)とdtypeの選択がアルゴリズムの選択よりも重要になることがよくあります。10^8要素のfloat64配列は、コピーを行う前にすでに800 MBを消費します。 - RAMに収まらない配列には、
numpy.memmapやHDF5ベースのアクセス(h5py)を使うことで、numpyデータ処理の分析コードを書き直すことなくチャンク単位で処理できます。 - 再現性は、RNGを明示的に制御し(
np.random.default_rng(seed))、バージョンを固定し、結果とともにdtypeとshapeを記録することに依存します。
NumPyとは実際に何か(そして処理にとってなぜ重要か)
NumPyの中核となる抽象化は、shape、dtype、そして各軸に沿って次の要素に到達するために何バイトスキップするかを記述するストライドの集合を持つ、型付きの多次元配列です。この設計上の決定——論理的な形状を物理的なレイアウトから分離すること——が、NumPyを高速かつ柔軟にしています。(1000, 1000)のfloat64配列は8 MBの連続メモリを占めます。転置しても1バイトも移動せず、ストライドのメタデータを書き換えるだけです。スライシングも同じように動作します。a[::2]はコピーではなくビューを返します。
numpyデータ処理にとっての実践的な意味は、自分がビューを保持しているのかコピーを保持しているのかを知る必要があるということです。ビューは安価でメモリを共有するため、1つを変更すると元も変更されます。コピーは安全ですが、ピークメモリが2倍になります。NumPy自身の配列のビューとコピーに関するドキュメントが権威ある参考資料であり、後でサイレントなエイリアシングのバグをデバッグするよりも、一度注意深く読む価値があります。
dtypeにも同じくらい注意を払うべきです。シミュレーションの出力はデフォルトでfloat64として届くことがよくありますが、原子座標の分子動力学トラジェクトリは可視化にはfloat32以上を必要とすることはほとんどなく、整数カウント(原子インデックス、フレーム番号)はfloatではなくint32またはint64であるべきです。dtypeを半分にすればメモリトラフィックも半分になり、メモリ帯域幅に制約される操作ではこれが支配的なコストになることがよくあります。
中核となるデータ処理ワークフロー
科学技術計算における典型的なNumPyデータ処理パイプラインには5つのステップがあります。取り込み、検査、クリーニング、変換、縮約です。各ステップには慣用的なNumPyツールがあり、検査ステップを省略することが下流のエラーの最も一般的な原因です。
取り込み。 np.loadtxtとnp.genfromtxtはプレーンテキストを扱えますが、Pythonで行ごとに解析するため大きなファイルでは遅くなります。.npy/.npzを使ったnp.loadが高速なバイナリ経路です。ハイパフォーマンスコンピューティングの事実上の標準でありHDF Groupが維持しているHDF5には、h5pyまたはPyTablesを使います。これらはデータセットを部分読み込みをサポートする配列ライクなオブジェクトとして公開します。
関連: — ガイド付きターミナルと実際のデータセットを使用したプロジェクトベースのデータ サイエンス パス.
検査。 算術操作の前に、arr.shape、arr.dtype、np.isnan(arr).any()を確認します。素早いarr.min()、arr.max()、arr.mean()で単位の誤りやセンチネル値(環境データセットで欠損データのマーカーとして使われる-9999がよくある例です)が明らかになります。この3行のチェックはどんなテストスイートよりも多くのバグを見つけます。
クリーニング。 NumPyの欠損値はfloatではnp.nanで表され、NaNは設計上算術演算を通じて伝播します。それらを無視するにはnp.nanmean、np.nanstd、np.nansumを使うか、np.isnanで明示的にマスクします。整数配列はNaNを含められないことに注意してください——空白を含むCSVデータを読み込む際のよくある落とし穴です。
変換。 形状変更、ブロードキャスト、軸ごとの操作がここに属します。arr.reshape(-1, 3)はフラットな座標ストリームをxyzの三つ組に変換します。arr - arr.mean(axis=0)は各列を中心化します。np.einsumはそうでなければネストしたループが必要になるテンソル縮約を表現します。
一見の価値があります: — 大学が支援する Python およびデータサイエンス証明書の 1 つのサブスクリプション.
縮約。 sum、mean、std、argmin、percentileによる軸に沿った集約。グループ化された縮約には、np.add.atやnp.bincountが、連続したブロックではなくインデックスで累積する必要があるケースを処理します。
ベクトル化、ブロードキャスト、ループのコスト
ベクトル化とは、配列全体に対する操作を表現し、NumPyがそれをコンパイルされたループにディスパッチすることを意味します。典型的な例:10,000点間のペアワイズ距離をPythonの二重ループで計算すると10^8回程度のインタプリタ反復がかかりますが、ブロードキャスト形式np.sqrt(((a[:, None, :] - b[None, :, :])**2).sum(-1))は同じ処理をCで行い、その代償として大きな中間配列を実体化します。
ブロードキャスト規則は、numpyデータ処理においてこれを簡潔にする仕組みです。NumPyは右から形状を揃え、サイズ1の次元を引き伸ばします。(N, 3)配列から(3,)配列を引くと、すべての行からベクトルが引かれます。(N, 1)配列に(1, M)配列を掛けると(N, M)が生成されます。規則はNumPyブロードキャストガイドに文書化されており、これを内面化すれば数値コードからほとんどのforループがなくなります。
トレードオフはメモリです。ブロードキャストは入力よりもはるかに大きな一時配列を生成することがあります。これがボトルネックになったら、計算をチャンク化するか、optimize=Trueを付けたnp.einsumを使って中間配列の一部を削減します。ベクトル化に抵抗する真にループに縛られた問題には、Numbaの@njitデコレータがPythonループをマシンコードにコンパイルし、しばしば実用的な逃げ道となります。
比較:作業に適したツールの選択
| タスク | 慣用的なNumPy | 別のものに手を伸ばすべき時 |
|---|---|---|
| 50 GBのトラジェクトリを読み込む | np.memmapまたはh5pyのチャンク読み込み | 並列/クラウドアクセスにはDaskまたはZarr |
| グループ別集約 | np.bincount、np.add.at | ラベル付きの混合型データにはpandasのgroupby |
| ペアワイズ距離 | ブロードキャスト + einsum | SciPyのcdist/pdist(メモリ最適化) |
| 疎行列 | np.zeros(密) | SciPyのsparse——密はメモリの90%以上を浪費 |
| カスタムの要素ごとの数学 | ベクトル化されたufunc | ロジックが分岐的な場合はNumbaまたはCython |
| 再現可能なサンプリング | np.random.default_rng(seed) | —(これが正しい現代的なAPI) |
numpyデータ処理のパターン:NumPyは正しいデフォルトですが、密配列は疎データやラベル付きデータには誤った表現であり、シングルマシンの配列はアウトオブコアデータには誤った表現です。
メモリ、dtype、アウトオブコア処理
NumPyの科学技術計算とnumpyデータ処理では、拘束条件となるのは通常CPUではなくメモリです。3つの手法がこれに答えます。
第一に、dtypeを意図的に選びます。np.float32はfloat64に比べてメモリを半分にし、中間結果にはしばしば十分です。np.int8やnp.uint16はほとんどのインデックス配列やラベル配列をカバーします。NumPyは混合演算で暗黙的にアップキャストするため、算術後にarr.dtypeで確認してください。
第二に、メモリマップド配列を使います。np.memmapはディスク上のファイルをアドレス空間にマップし、100 GBの配列をRAMにあるかのようにスライスできるようにし、OSは必要なブロックだけをページインします。これはシーケンシャルアクセスパターンではうまく機能し、配列全体にわたるランダムアクセスではうまく機能しません。
第三に、チャンク単位で処理します。HDF5データセットを例えば10,000行のブロックで読み込み、実行平均やヒストグラムを累積することで、ファイルサイズに関係なくピークメモリを抑えられます。これはシーケンシングリードをストリーミングするバイオインフォマティクスパイプラインや、数十年にわたるモデル出力を縮約する気候ワークフローにおける標準的なパターンです。
微妙な点:arr.copy()とファンシーインデックス(arr[idx_array])はどちらもメモリを割り当てます。チャンクに対するタイトなループでは、これらの割り当てが実行時間を支配します。np.copytoやufuncのout=引数を使って事前に割り当てた出力バッファを再利用することで、メモリのチャーンを避けられます。
再現性と来歴
numpyデータ処理における再現可能な数値計算には、NumPyが直接関わる3つのもの——ランダム性、dtype、バージョン——の制御が必要です。
ランダム性:レガシーなグローバル状態np.random.seedは推奨されません。現代的なAPIはrng = np.random.default_rng(seed)で、これは関数間で状態が漏れない独立したGeneratorを返します。これは並列ワーカーがそれぞれ独立した再現可能なストリームを必要とする場合に重要です。
dtype:永続化する各配列のdtypeを記録します。float32で計算された結果とfloat64で計算された結果は最後の数桁が異なり、結果を比較するレビュアーはどちらが使われたかを知るべきです。np.saveで保存するとdtypeが保持されますが、CSVとして保存すると保持されません。
バージョン:NumPyの挙動は結果に影響する形でバージョン間で変化してきました——例えば、Windowsでのデフォルトの整数型や、特定の縮約の扱いです。環境ファイルでNumPyを固定し、出力メタデータにバージョンを保存することは、再現可能な研究ツールにおける一般的な慣行です。NumPyリリースノートがこれらの変更を文書化しています。
来歴については、結果の隣にshape、dtype、入力のハッシュを保存します。明示的な許容誤差を伴うnumpy.testing.assert_allcloseのようなツールは、回帰テストを脆いものではなく意味のあるものにします。
NumPyをより広いスタックと統合する
numpyデータ処理においてNumPyが単独で存在することはほとんどありません。pandasはNumPy配列をラベル付き軸でラップし、データが異種の列や意味のある行ラベルを持つ場合に適したツールです。.to_numpy()での変換はdtypeが一致する場合ゼロコピーです。SciPyは線形代数、最適化、信号処理のためにNumPy上に構築されています——基本的な求解を超えるものには一般にnumpy.linalgよりもscipy.linalgが好まれます。scikit-learnは全体を通じてNumPy配列を消費し返します。Matplotlibはそれらを直接プロットします。
相互運用性の契約は配列インターフェースであり、Python Array API標準として形式化されており、CuPy、JAX、PyTorchのようなライブラリがNumPy互換のAPIを公開できるようにしています。このサブセット上で分析コードを書くことで、最小限の変更でGPUに移植可能になります——シミュレーションがワークステーションのサイズを超える場合に真の利点です。
1つの注意点:これらのライブラリ間の暗黙的な変換はデータをコピーします。CuPyのcupy.asarrayでNumPy配列をGPUに移動するとPCIe経由で転送されます。データを単一のデバイスに常駐させ、転送をグループ化することは、操作ごとに往復するよりもはるかに高速です。
出典と参考文献
- データ処理 — Wikipedia: データ処理とは、意味のある情報を生み出すためのデジタルデータの収集と操作です。データ処理は情報処理の一形態であり…
よくある質問
NumPyデータ処理とは何ですか?
NumPyデータ処理は、NumPyライブラリのndarrayを使って数値データを読み込み、クリーニングし、変換し、縮約することを含みます。ファイルからの配列の読み込み、NaN対応関数による欠損値の処理、形状変更とブロードキャスト、軸に沿った集約をカバーします。ほとんどの科学的PythonライブラリがNumPyに依存しているため、数値パイプラインの基盤となる層です。
データ処理においてNumPyはpandasより速いですか?
NumPyは同種の数値配列に対して高速です。インデックスやdtypeディスパッチのオーバーヘッドなしに連続メモリを直接操作するためです。pandasは書くのが速く、混合型のラベル付き表形式データにより適しています。大規模な数値計算では、pandasのDataFrameを.to_numpy()でNumPy配列に変換してそこで処理するのが一般的な最適化です。
NumPyで欠損データをどう扱えばよいですか?
浮動小数点配列は欠損値をnp.nanとして表し、NumPyはそれらを扱うためにnp.nanmean、np.nanstd、np.nansum、np.isnanを提供します。整数配列はNaNを格納できないため、floatに変換するか、センチネル値とブールマスクを使います。np.maによるマスク配列はより構造化された代替手段を提供します。
NumPyはRAMより大きなデータを処理できますか?
はい、np.memmapを使ってディスクファイルをアドレス空間にマップするか、h5pyでHDF5データセットをチャンク単位で読み込むことで可能です。どちらのアプローチもピークメモリを抑えます。マシン間の並列または分散処理には、DaskとZarrが同じ配列モデルを単一ノードを超えて拡張します。
NumPyにおけるビューとコピーの違いは何ですか?
ビューは元の配列のメモリバッファを共有し、形状やストライドのメタデータのみを変更するため安価ですが、変更が伝播します。コピーは新しいメモリを割り当て、独立しています。スライシングとreshapeは通常ビューを返し、ファンシーインデックスと.copy()はコピーを返します。配列がビューかどうかを確認するにはarr.baseを使います。
NumPyの結果を再現可能にするにはどうすればよいですか?
レガシーなグローバルnp.random.seedの代わりにnp.random.default_rng(seed)を使い、環境でNumPyのバージョンを固定し、出力とともにdtypeとshapeを記録します。dtypeを正確に保持するにはCSVではなくnp.saveで配列を永続化します。テストには、np.testing.assert_allcloseと明示的な許容誤差で比較します。
よくある質問
NumPyのデータ処理とは何ですか?
NumPy データ処理には、NumPy ライブラリの ndarray を使用して数値データをロード、クリーンアップ、変換、削減することが含まれます。ファイルからの配列の読み取り、NaN 対応関数による欠損値の処理、再形成とブロードキャスト、軸に沿った集計について説明します。ほとんどの科学 Python ライブラリは NumPy に依存しているため、これは数値パイプラインの基本層です。
NumPy はデータ処理においてパンダより速いですか?
NumPy は、インデックスや dtype-dispatch のオーバーヘッドなしで連続したメモリ上で直接動作するため、同種の数値配列の場合は高速です。 pandas は書き込みが速く、混合タイプのラベル付き表形式データに適しています。大規模な数値計算の場合、.to_numpy() を使用して pandas DataFrame を NumPy 配列に変換し、そこで処理するのが一般的な最適化です。
NumPy で欠落したデータを処理するにはどうすればよいですか?
浮動小数点配列は欠損値を np.nan として表し、NumPy はそれらを処理するための np.nanmean、np.nanstd、np.nansum、および np.isnan を提供します。整数配列は NaN を格納できないため、float に変換するか、センチネル値とブール マスクを使用します。 np.ma を介したマスクされた配列は、より構造化された代替手段を提供します。
NumPy は RAM より大きなデータを処理できますか?
はい、np.memmap を使用してディスク ファイルをアドレス空間にマップするか、h5py を使用してチャンク内の HDF5 データセットを読み取ります。どちらのアプローチでも、ピーク時のメモリを制限します。マシン間での並列処理または分散処理を行うために、Dask と Zarr は同じアレイ モデルを単一ノードを超えて拡張します。
NumPy のビューとコピーの違いは何ですか?
ビューは元の配列のメモリ バッファを共有し、形状またはストライドのメタデータのみを変更するため、コストはかかりませんが、突然変異が伝播します。コピーは新しいメモリを割り当て、独立しています。通常、スライスと形状変更を行うとビューが返されます。派手なインデックス付けと .copy() はコピーを返します。 arr.base を使用して、配列がビューであるかどうかを確認します。
NumPy の結果を再現するにはどうすればよいですか?
従来のグローバル np.random.seed の代わりに np.random.default_rng(seed) を使用し、NumPy バージョンを環境に固定し、出力で dtype と Shape を記録します。 dtype を正確に保持するには、CSV ではなく np.save を使用して配列を永続化します。テストの場合は、np.testing.assert_allclose と明示的な許容誤差を比較します。
ブラウザでコーディングして Python を学習する
ブラウザーで直接コーディングするインタラクティブな Python およびデータ サイエンス コース