1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

numpy arrayの文字列化でハマった話

Last updated at Posted at 2024-09-20

numpy arrayをlist経由で文字列化する理由

Python 3.8+NumPy 1.XからPython 3.10+NumPy 2.Xに移行する時の話です。一般にはあまりやらないことだと思うのですが、numpy arrayをあえてPythonビルトインのlistにキャストして使いたい、という場面が時々あります。重宝していたのがnumpy arrayの文字列化の時です。numpy arrayを直接文字列化すると、スペース区切りで要素が並んでしまって使い勝手が悪かった一方で、listを文字列化するとカンマ区切りで要素をリストしてくれました。少なくともPython 3.8+NumPy 1.Xの世界では。

python3.8
>>> import numpy as np
>>> a = np.arange(10)
>>> print(a)
[0 1 2 3 4 5 6 7 8 9]
>>> print(list(a))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Python 3.10 + NumPy 2.X への移行

このような、print(list(a))みたいなコードを含むモジュールをそのままPython 3.10+NumPy 2.Xに移行すると、大変なことになります。

python3.10
>>> import numpy as np
>>> a = np.arange(10)
>>> print(a)
[0 1 2 3 4 5 6 7 8 9]
>>> print(list(a))
[np.int64(0), np.int64(1), np.int64(2), np.int64(3), np.int64(4), np.int64(5), np.int64(6), np.int64(7), np.int64(8), np.int64(9)]

Python 3.8までは、0のようにただ数値だけが表示されていたのが、どういうわけか型情報まで表示してしまいます。親切心からの変更なのかもしれませんが、困ったことになりました。

正しい文字列化のお作法

numpy arrayを文字列化するには、numpy.array2stringを使うのが正解だと思われます。

numpy.array2string - NumPy API reference

separator', 'を指定すると、期待した結果が得られるようになりました。

python3.10
>>> import numpy as np
>>> a = np.arange(10)
>>> print(np.array2string(a, separator=', '))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

listを経由するみたいな安易な方法を見つけてしまう前に、API referenceを読むべきでした。

2024/9/24 追記

np.ndarray.tolistもこの用途に適していることがわかりました。

ndarray.tolist - NumPy API reference

ドキュメントによると、リスト変換時に各要素を元の型になるべく近いビルトインの型に変換してくれます。この性質により、文字列化したときに目的の出力が得られます。

python3.10
>>> import numpy as np
>>> a = np.arange(10)
>>> print(a.tolist())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

どちらかというとarray2stringよりもtolistの方がいいかもしれません。理由は、np.array2stringは「気が利きすぎる」のです。問題となるのは長大なリストを取り扱う場合で、巨大な文字列が生成されるのを避けるために間の要素が省略されてしまいます。あと、各要素に割り当てる文字数も自動調整されます。

python3.10
>>> import numpy as np
>>> a = np.arange(10000)
>>> print(np.array2string(a, separator=', '))
[   0,    1,    2, ..., 9997, 9998, 9999]

また、1行の長さが一定の文字数を超えると改行が入ります。

python3.10
>>> import numpy as np
>>> a = np.arange(1000000000, 10000000001, 1000000000)
>>> print(np.array2string(a, separator=', '))
[ 1000000000,  2000000000,  3000000000,  4000000000,  5000000000,
  6000000000,  7000000000,  8000000000,  9000000000, 10000000000]

thresholdmax_line_widthパラメータにsys.maxsizeを設定することでこれらの挙動を無効化することはできますが、少なくとも上記のユースケースではシンプルにtolistを使う方がベターかと思いました。

もちろんこれはユースケースに依存する話で、array2stringの親切さが身に染みる場合もあるかと思います。用途に応じて使い分けるようにしたいと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?