はじめに
後世の人たちが私のような愚かなはまり方をしないよう、この書を残す。
問題
データ型を指定せず文字列の配列を作ると、それはUなんたらというdtypeになる。
dtype=strと指定しても同様。
import numpy as np
arr=np.array(["A","AB","ABC","ABCD"],dtype=str)
print (arr)
print (arr.dtype)
['A' 'AB' 'ABC' 'ABCD']
<U4
不等号はリトルエンディアンを、UはUnicodeを意味し、数値は文字数を示す。つまり、変数の文字数が固定されているのだ。
後で加工された文字列の文字数がこの値を超過するとはみ出た分は失われてしまう。
arr[1]=arr[1]+"XYZ"
print (arr[1])
arr[2]="XYZ"+arr[2]
print (arr[2])
arr[3]="まったく別の文字列"
print (arr[3])
ABXY # "AB"+"XYZ"だが"ABXYZ"ではない
XYZA # "XYZ"+"ABC"だが"XYZABC"ではない
まったく # "まったく別の文字列"ではない
これはnp.fullやnp.emptyでも同様。
arr1=np.array(["A","B","C"])
arr2=np.array(["X","Y","Z"])
arr=np.empty(3,dtype=str)
for i in range(3):
arr[i]=arr1[i]+arr2[i]
print(arr)
['A' 'B' 'C']
np.emptyで確保されたのは1文字分だけなので、AXやBYやCZを格納することができないわけだ。
2文字以上の文字列でもってnp.fullしたときもdtype=strを指定すると1文字になってしまう。さすがにこれは意味不明だ。
# dtype=strを指定した場合
arr=np.full(3,"ABC",dtype=str)
print (arr)
print (arr.dtype)
['A' 'A' 'A'] # ["ABC" "ABC" "ABC"]になってくれない
<U1 # 1文字分しか確保されていない
# dtypeを明示しない場合
arr=np.full(3,"ABC")
print (arr)
print (arr.dtype)
['ABC' 'ABC' 'ABC'] # 正しく定義されるが
<U3 # 文字数が決まっているので加工しづらいことに変わりはない
対策
任意の文字数の文字列を格納できるようにするには、dtype=str でなくdtype=objectと指定する。
arr=np.array(["A","AB","ABC","ABCD"],dtype=object)
arr[1]=arr[1]+"XYZ"
print (arr[1])
arr[2]="XYZ"+arr[2]
print (arr[2])
arr[3]="まったく別の文字列"
print (arr[3])
ABXYZ
XYZABC
まったく別の文字列
次回予告
もともとは、Excelのセル番地を模した
[['A0' 'B0' 'C0' 'D0' 'E0']
['A1' 'B1' 'C1' 'D1' 'E1']
['A2' 'B2' 'C2' 'D2' 'E2']
['A3' 'B3' 'C3' 'D3' 'E3']]
といった配列を手打ちでなくプログラムで作成して、それを元にnumpyのスライスやOpenCVの画像トリミングについて勉強するつもりだった。
ところがうまくいかず、「文字列の連結は+だよな? 文字列と数値は連結できないのでstr()を使うんだよな?」と基本に立ち返っても駄目で、ググりまくって今回の発見に至った次第。
次こそはスライスの勉強をするぞ。