NumPyの多次元配列 使いにくすぎ問題
引用元:https://news.ycombinator.com/item?id=43996431
配列が2次元超えるなら,Xarray[1]おすすめだよ.NumPyに次元名を付けられて,ブロードキャストとかアライメントが自動になるから,記事の不満はほぼ解決!NumPyより弱い分野もあるけど,NumPyに戻るの簡単だからヘルパー関数作ればOK.僕はNumPy嫌いじゃないけど,多次元データにはXarrayが便利だと思うな.3次元からXarray使うと効果あるよ.[1]https://xarray.dev
Xarrayマジ最高!PandasとNumPyの良いとこ取りだよ.da.sel(…)みたいなインデクシング超分かりやすい.次元名のおかげでブロードキャストも曖昧にならないよ.地理空間データやArviz[1]使ったベイズモデリングにも便利.配列をDatasetにまとめて,共通の座標で一括選択できるのもいいね.[1]https://www.arviz.org/en/latest/
賛成!僕の場合,XarrayがほとんどNumPyに取って代わったおかげで,生産性がすっごく上がったんだ.
TensorflowとかKeras,Pytorchでもこれみたいなのあるかな?最近はそんなに使ってないけど,前は今説明してくれたこと全部,デバッグが大変なやり方でやる必要があったんだよね.
TorchにはNamed Tensorsっていうのがあるよ.似た感じで使えるはず:https://docs.pytorch.org/docs/stable/named_tensor.html ドキュメントにはプロトタイプ機能って書いてあって,もう何年もそんな感じだから,実運用向けかどうかは正直分からないな.
Xarrayのに比べてAPIがずっと悪いね.誰かPyTorchの上に作ってくれたらいいのに.
僕はeinopsがすごく好きだよ.NumPy,Pytorch,Keras/Tensorflowで使えるし,名前付きの転置とか繰り返し,einsum演算が簡単にできるんだ.
僕も同じ!最近einopsとjaxtypingを一緒によく使ってるけど,多次元配列のコード読むのも書くのもすごく楽になったよ.array_api_compatもいいよ.APIの網羅率は完璧じゃないけど,PyTorchとNumPy両方の配列で動くコード書けるのは結構満足度高いね.https://docs.kidger.site/jaxtyping/https://data-apis.org/array-api-compat/
pytorchだとNamed Tensorsが似てるかな.でもこれは仮の機能で,全部でサポートされてるわけじゃないんだ.https://docs.pytorch.org/docs/stable/named_tensor.html
このライブラリ紹介してくれてありがとう.試してみるよ.’array[:, :, None]’みたいな書き方,あれ使うのが本当に嫌だって思ってるの自分だけかなって,しばらくちょっとおかしいのかなって感じてたんだ.
そういう文脈で言うとさ、バイオシグナル処理だとNeuroPype[0]もNumPyをベースにしてるんだ。名前付き軸とか、各軸の要素データ(チャンネル名とか位置とか)を保存できる機能があるんだよ。[0] https://www.neuropype.io/docs/
xarrayって便利だよ
人生って時々巡り巡るよね。NumPyがだいたいNumericとNumarrayライブラリの合併から生まれたのを覚えてるよ。Numarrayの人たちがこの20年間、自分たちが正しい解決策だと証明し続けて、いつかElon Muskからお金をもらってXarray[0]に改名して、ついにNumPyを打ち負かし始めた—なんて想像したいね。[0] 上記のほとんどはフィクションだけど
Juliaに乗り換えたのはNumPyの構文が難しかったからだよ。MATLABからNumPyで生産性が落ちて、数学より『パフォーマンスエンジニアリング』に時間を使うようになった。Juliaではベクトル化もループも速くて、コードの読みやすさに集中できるようになった。ブログ記事の気持ち、すごくわかるな。np.linalg.solveを盲目的に使うのも違うと思うんだ。問題特化の線形代数カーネルを作る理由はたくさんある。
> MATLABからNumPyで普通のプログラマーになった気分で、数学より『パフォーマンスエンジニアリング』に時間を使うようになった。
ベクトル化しないとMATLABもPythonと同じくらい遅いよ。Pythonの遅さは問題でJuliaは速いけど、ニッチな用途以外ではJuliaは使えないね。PythonにはJITハックがあるけど、パフォーマンスの良い代替言語はないのが残念だ。
理由はすごくシンプルだよ。Juliaは科学計算のために設計された言語。NumPyは科学計算のために本当は設計されてない言語にフランケンシュタインみたいに接ぎ木されたライブラリなんだ。Juliaが何とかして勝って、ネットワーク効果でPythonで働くことを強いられてる人たちが解放されることを願うばかりだね。
”Juliaではベクトル化もループも速くて、コードの読みやすさに集中できるようになった”
これは現代Fortranにも言えることだね。
近い将来、AI翻訳のおかげで言語によるパフォーマンス差がなくなるんじゃないかな。現代AIは翻訳エンジンで、プログラミングコードの翻訳もできる。いずれPython/MATLABみたいなフロントエンドで書いて、裏でLLVMみたいな速いバックエンドで動くようになると思うんだ。MATLABからRustへのコード翻訳とかもうできるんだよ。全部うまく動かすための接着剤があれば、どの言語でも速く書けるようになるはず。
それが違うなら、Pythonは何のために設計されたんだろうね?
> 残念ながらJuliaはかなりニッチな用途以外では事実上使えないね。
なんで?エコシステムだけが理由?
LFortranとかの最近のFortranへの取り組み,マジでハンパなくすごいんだよ!
RはPythonの代わりには全然ならないね.しかもPythonより遅いし.
褒めるなら,GNU Fortranの開発者も入れとこうよ.オープンソースのFortranコンパイラ開発でマジで標準を作った人たちだから.
MATLABってそんなに違うの?ループは遅い(バージョンによるけど),一番速いのはバックスラッシュっていう黒魔術ボックスの完璧さだよ.
これってTypescriptをJavaScriptにトランスパイルするのと似てるけど,全然違う言語に変換するから,プログラマはコードのパフォーマンス特性を理解できなくなる(「このPythonループ,トランスパイラは最適化できるの?」とか),それにLLMの非決定性も加わる.うわー,それはちょっと嫌だな.
主にREPL/Notebook以外での使いにくさや,他のものとの連携のしにくさかな.
これ,15年間ずっと願ってることだけど,結局まだ全部Pythonでやってるんだよね.
Pythonの一番的確な表現は「C libsの糊」って聞いたよ.
言語ライブラリの間や,同じライブラリでもバージョン違いで十分曖昧さがあるから,トランスパイルは常に検証が必要になる.おもちゃの例なら動くけど,ちゃんとしたコードだと,生成されたトランスパイル結果は実行するたびに違う形でエラーになるよ.
何との連携? C向けのFFI、PyCall、RCallなんかは結構上手くいくよ。REPL中心だけど、他に欲しいものって何? VSCodeのデバッガー改善とか?
もっとコメントを表示(1)
たまたまこの記事見つけたんだ。
なんでそうなってるかうまく説明してるみたいだね。
https://news.ycombinator.com/item?id=44013455
matlabのJITはループに関してはnumbaと同程度だと思うな。
Python使う時って、性能より”外部ライブラリでGPU使うか”とか”O(log n)かO(n^2)か”を気にする感じ。
他の言語より10〜100倍遅いのは普通で、微妙な性能特性は気にしない言語なんだよ。
この記事みたいに”外部ライブラリを正しく呼んでるか”とか”アルゴリズムが指数時間か”以外はね。
元々はスクリプト言語だったはず。
PerlとかTclの代替としてね。
Rは得意な分野ならPythonよりかなり性能良いよ。
でもDSLであって汎用言語じゃないけどね。
”かなりニッチな目的”ってのが分からない。
Juliaは汎用言語で、bashスクリプトの代わりとか、web scraping、webプロジェクト、イラスト・アニメ生成、科学計算に使うよ。
言語も楽しいし、パッケージシステムが最高でライブラリを組み合わせるのが超楽なんだ。
1 https://lee-phillips.org/amazonJuliaBookRanks
2 https://www.admin-magazine.com/Archive/2025/853 https://lee-phillips.org/pluckit
パッケージ豊富で汎用だから、何でも早く始められるよ。
性能気にしないなら、MVPとかFlaskサーバー、画像認識とか、何でも気軽にPythonでできて最高。
ライブラリが便利で、すぐ何か作り始められるんだ。
性能は犠牲になるけどね。
いや、”Pythonは何でも二番目に得意”で、銀河連邦よりライブラリ多いから、それは絶対ないね。
へえ、いいね!
俺がFortranの世界に行くのは、Julia開発絡みの時だけなんだ。
その時に関連リンクとかプロジェクトを見るだけ。
だからGNU Fortranには詳しくないよ。
[1] https://fortran-lang.discourse.group/t/update-on-prima-a-jul…
[2] https://discourse.julialang.org/t/ann-julia-back-end-for-lfo…
Juliaは普及する前に、デプロイの話をもっと汎用的で分かりやすくする必要があるね。汎用性を約束して作られたのに、そのパッケージングは足かせになってるよ。
最後にMatlabを真剣に使ったのは10年以上前だけど、その頃にはJITコンパイルが実装されてて、単純なループの方がベクトル化よりずっと速いことが多かったな。それに間違いも少なかった。確か、’¥’は単に連立方程式を解くショートカットで、精度を損なうことなく適切なLAPACK関数に頭の中で翻訳できたんだ。FortranやC(Pythonでさえも)より、余分なコピーを作るのには少し注意が必要だったけど、何も魔法じゃなかったよ。
Matlab(とある程度Juliaも)と比べて、numpyへの俺の不満はこの2つの段落に集約されてるよ。
ある関数はaxes引数がある。違う名前の違うバージョンがある関数もある。規約に従うものもある。規約に従いつつaxes引数もあるものもある。そして、どんなベクトル化バージョンも提供してないものもある。
でもNumPyの最大の欠点はこれだよ。ある与えられた形状の配列で問題を解く関数を作ったとしよう。さて、それをより大きな配列の特定の次元にどう適用する?答えは:もっと複雑なやり方で、一から関数を書き直すんだ。プログラミングの基本原則は抽象化—単純な問題を解決して、その解決策をより複雑な問題のための構成要素として使うことだ。NumPyはそれを許してくれないんだ。
普段Matlabコードを書くときは、ベクトル化されたバージョンはだいたい動くし、何か変更が必要でも、かなり軽微で直感的だ。numpyだと、全ての関数のドキュメントを調べなきゃいけない気がするよ。特定の関数が期待するどんな形状にも配列を転置したりリシェイプしたりするんだ。ぜんぜん一貫性がない。
Matlabの配列の2次元以上のサポートはあまりにひどいから、記事で嘆かれてる状況に出くわすこと自体が珍しいくらいだよ。
ああ、もし誰かMatlabで多次元データを扱わなきゃいけない不運に見舞われたら、Tensor Toolboxとか、Tensorlabとか、N-way Toolboxを勧めるよ。
後方互換性のために洗練されてない部分があるのは確かだけど、それ以外に何言ってるのか分からないな。Matlabは2次元以上の配列も問題なくサポートしてるし、少なくとも20年以上前からそうだよ。
2番目の問題だけど、もし俺が正しく理解してたら、jaxのvmap
を試してみるといいかもね。
この記事の「特定の次元にどう適用すんの?」って文句、意味わかんねーんだけど?だってスライスしてスクイーズすればできるじゃん。何が問題なの?
もし「任意インデックスへの適用」みたいな変な話なら、もう言葉も出ないわ(だってそれ配列全体のことだし)。
NumPyの一番困る点は、ベクター化が分かりにくいことと、返り値の型が inconsistent なこと。特にpoly1dはひどくて、Pz0はpoly1dなのにz0Pは配列になったり、P.coef[0]とP[2]が同じものを指したりする。これはpoly1dが古いAPIってのもあるけど、ライブラリ全体にこういう罠が多い。サイレントな型変換とか、デバッグにとっては悪夢だよ。
これってdunder演算子メソッドのdispatchセマンティクスのせいなんだよね。Pythonのドキュメントはマジでこれの説明下手くそ。
君が言ってる返り値の型は、俺から見たら完全に予想通りだし、Pythonとしては慣習的なものだよ。これはNumPyの問題じゃないって。
俺が見るに、pythonデータサイエンスエコシステムの一番の問題は、何一つ標準化が全くないこと。ライブラリごとにフォーマットが違うから、データ処理じゃなくてフォーマット変換ばっかになるんだよね。
Juliaだと、色んなライブラリが自然に連携して動く。でもPythonだと、そういう連携のためにすごいboilerplateを書かないといけない。これが大変なんだ。
誰もarray-api(とかdata-apis全般)に触れてないね。これはpythonエコシステム全体で配列の扱い方を標準化しようとしてるプロジェクトなんだ。
https://github.com/data-apis/array-api
https://data-apis.org/blog/announcing_the_consortium/
良いアイデアっぽいけど、実現は難しそうだね。発表ブログ記事がもう5年も前だけど、実際このプロジェクトってどれくらい影響あったの?
Rは4つ(?)のクラスシステムで参戦だね。
Rをかばうと、クラスシステムは確かにそれぞれ違う特性持ってるし、言語に深く入り込んでるわけでもないんだ。
著者の指摘はまあ分かるな〜。MatlabからNumpyに乗り換えたとき、ちょこちょこ不満があったんだよね。データのスライスとか、MatlabやJuliaよりNumpyの方がまだ使いにくい感じ。でも、だからってMatlabの統計とか信号処理ツールのライセンス料出すかって言ったら、それはないわ。記事で言われてる問題って、だいたい3階以上のテンソルに関するものだよね。Numpyって元々行列のために作られたっぽいから、その辺で困ることがあっても不思議じゃないかも。確かにTorchみたいな専門のライブラリの方がいい。でもTorchもまた別の難しさがあるんだよな〜。うーん、著者の”他のすべての配列言語以外ではNumpyは最悪の配列言語”って結論、なんか合ってる気がしてきた。もしかしたら俺の想像力不足だけかな。
Numpyって最初からN次元配列を扱うためのものだったんだぜ。Ndのnumarrayの後継なんだからさ。
ちょっと賢ぶって、記事にあったベクトル化されたマルチヘッドアテンションの実装で、einsumと最適化設定でパフォーマンス上げようとしたんだ。ベクトル化版より2倍速くなったけど、残念ながらループ版が一番速かった。なんでこうなるかコード見て考えてみて(リンク省略)。たぶんeinsumがキャッシュ効率考慮してないのが原因かもね。
>確かにベクトル化版より2倍速くなったけど、がっかりなことに、ループ版がさらに速かったんだよ。
CPUでの話?それともGPU?
公平に言うとさ、import numpy as np
をimport cupy as np
に変えれば、コード変えずにGPUで動かせるけどね。全然パフォーマンス良くないけど。PyTorchの方がだいたい12倍速いんだよ。
俺がNumpyで一番問題だと思うのは、構文が長いことなんだよね。プログラミング言語として見たら、別にデメリットじゃないかも(むしろ良い点かも)しれないけど、実際使うとmatlabとかJuliaに比べて構文がめんどくさいんだよ。MatlabやJuliaのコードの方が読みやすいし分かりやすいし、数式の書き方とも合ってるし。
もっとコメントを表示(2)
本当の配列言語の構文は簡潔で表現豊かだよ。数式みたいに書けて、少ないコードで済むんだ。でもpython+numpyは違うと思う。Numpyはpythonに速度のためのベクトル化演算を追加するライブラリでしかない。これは本当の配列言語とは違うんだ。配列言語の構文の良いところは持ってないね。
numpyって自分のこと配列言語だって売り込んでんの?
記事はNumPyのベクトル化コードの話だよね。MATLABと比較が多いけど、配列を使える言語と配列言語は違うんだ。全部が配列ベースの言語で書く方が自然で操作も一貫してるよ。
MATLABのデフォルト処理は、ユーザーのためってより高速化のためだしね。ほとんどの開発者は配列言語を使わないけど、ベクトル化コードを書く人が少ないからかも。全てをベクトル化できるわけでもないし。でも、書くなら配列言語が良いと思うな。
うんうん。
MATLABのコード
A = [1、 2; 3、 4];
x = [5; 6];
y = A * x;
と、NumPyのこのイケてないバージョンを比べてみてよ。
import numpy as np
A = np.array([[1、 2]、 [3、 4]])
x = np.array([[5]、 [6]])
y = A @ x
NumPyの関数を使うなら、リストをnp.arrayでラップする必要はないんだよ(または、引数のどれかがすでにNumPy配列ならね。大体そうだけど)。
例:
from numpy import *
A = [[1、 2]、 [3、 4]]
x = [[5]、 [6]]
y = dot(A、 x)
それ良いね。でも、僕の理解だとNumPy専用関数だけしか使えないんでしょ?Pythonの基本演算子(+とか*とか)と同じ記号を使うと、解釈が変わっちゃうんだよね。例えばA+xだと、リストの連結になっちゃう。
コンテキストでできることとできないことを追跡しないといけないなんて、理想的じゃないと思うな。
その通り!これらの不満はnumpysaneでいくつか解決されてるよ:https://github.com/dkogan/numpysane/ 。numpysaneとgnuplotlibがあれば、NumPyも許容範囲になってきて、今では何にでもガシガシ使ってるよ。でも、そうだね;これら無しじゃ使えない感じ。
リンクありがとう!俺もこれらの問題にはブツブツ言ってたけど、NumPyの上にワークアラウンドのライブラリを探そうなんて思いもしなかったよ…
numpysaneのほとんどはPythonでループしてるみたいだね。あれは本当のベクトル化じゃないよ。
俺が問題だと思うのは、inplace操作を忘れるとあちこちメモリ確保しまくっちゃうのが簡単すぎるとこだね。cupyだとさらに酷くて、一連の操作で最終データを作るんじゃなく、操作ごとにデータのセットを作っちゃうんだ。回避策はあるけど、使いやすくないんだよね(cupy.fuse()は良いけど、使うのを覚えておかないといけないし、色々形状の配列が必要なものには機能しないし)。
強力型付け言語(F#とか)出身者として、これには同意するな。PyTorchとかNumPyはパワフルだけど、APIがマジで不親切。曖昧な型のオブジェクトを色々な組み合わせで受け付けて、ライブラリが実行時にブロードキャストとか魔法で勝手に処理を見つけてるんだ。
この手の”賢い”APIって、Pythonエコシステム全般のメリットであり呪いでもある気がするね。入門は超簡単だけど、マスターするのはめちゃくちゃ難しいんだよ。
Broadcastingは便利で強力な機能だよ.ちゃんと仕様があって覚えやすい.でもさ,現実の言語の型システムってBroadcastingを表現できるほど強力じゃないんだよね.
Broadcastingは良いんだけど,もっとハッキリしてるといいな.まあ,慣れてくると明示的すぎると逆に面倒になるのかもね.
N>3次元配列の扱いにくさ,わかるわ.高次元で考えるの難しいよね.著者の解決策は気になるけど,Eigen notationはそんな好きじゃないな.a[:,:,None]はいいんだけど,index arraysの挙動は戸惑うことあるね.あと,fftのaxisとfftshiftのaxesみたいに引数名が inconsistent なのもマジ勘弁.
>人間が高次元で考えるのが得意じゃないって話だけど,4次元配列とかって「グリッドのグリッド」って考えられないかな?例えば2×3×4×5の配列は,2×3の行列の中に4×5の行列が入ってる,みたいな.(APLの例:https://tryapl.org/?clear&q=%7B%E2%8D%B5%2C%E2%8D%A5%E2%8A%8…)
基本的には同意だよ.でもサブ行列の操作とかやろうとすると(この記事の主題でもあるけど)すぐにややこしくなるよね.正直,ネストしたループの方がかえって分かりづらいことも多いわ.まあ,自分がnumpyを圧倒的によく使うからかもしれないけどね.
>グリッドのグリッドはグリッドと同じだってば.4×5行列を2×3に並べたって,配列が持つ4次元空間の構造は失われるんだよ.点Aと点Bの距離を例に考えてみてよ.タクシー距離は5だよ.
>個人的に,np.einsumはNumPyの数少ない良いところだと思う.einsumって乗算しかできないのが結構残念なんだよね.Einstein notationにちゃんとのめり込めば(例えばhttps://tensora.drhagen.com/みたいに),もっと良くて高性能なものを作れると思うんだ.
俺はPythonのデータライブラリってあんまり好きじゃないんだよね.全体的にスタイルに一貫性がないっていうか.多分,だからRの方が「教室」としては良かったんだと思う.Juliaもいいし,Mathematicaも purely math にはいいよね(Mapleもいいぞ).
Numpyって結局BLAS/Lapackのインターフェースみたいなもんでしょ?大きな密行列向けで,細かい操作には向いてない.まあ,そういうもんだよ.個人的には,Jaxみたいな柔軟なライブラリで筆者が苦労したのが surprising だった.NumpyとJaxって得意分野違うし, mutual の領域に踏み込まない方が良いと思うけどね.
EinsumはTullio.jlで知って,魔法みたいだったよ.Tullio.jlだと数学のnotationに近い書き方(例:@tullio D[k, n] = …)ができるんだ.PEP 750のテンプレート文字列を使えば,numpy.einsumももっと書きやすくなるかもね.それが実現したら neat だな.