Tech カテゴリのイメージ

ValueError: setting an array element with a sequence の原因と直し方[NumPy]

NumPyを使っているとこれが出ることがある。

ValueError: setting an array element with a sequence.

新しいバージョンではもう少し親切な文言になることもある。

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions.

言っているのは、数値が入るはずの場所に列(リストや配列)が来ている、ということになる。NumPyの配列は全要素が同じ型で、形も長方形でなければならないので、そこから外れると拒否される。

1. 長さが揃っていない入れ子リスト

いちばん多い原因になる。

import numpy as np
a = np.array([[1, 2, 3], [4, 5]]) # ValueError

3個と2個で長さが違うので、長方形の配列にできない。NumPyの古いバージョンではdtype=objectの配列が黙って作られていたが、いまはエラーになる。

対処は目的によって3つに分かれる。

長さを揃えるのが本筋になる。足りない分を埋める。

rows = [[1, 2, 3], [4, 5]]
n = max(len(r) for r in rows)
a = np.array([r + [0] * (n - len(r)) for r in rows])

欠損として扱いたいならnp.nanで埋める。ただしnanは浮動小数点なので、配列全体がfloatになる。

a = np.array([r + [np.nan] * (n - len(r)) for r in rows])

どうしても不揃いのまま持ちたいなら、明示的にobject配列にする。ただし数値演算の高速化は効かなくなる。

a = np.empty(len(rows), dtype=object)
a[:] = rows

2. 配列の1要素にリストを代入している

形が合っているつもりでも、代入先が数値ひとつのときに起きる。

a = np.zeros(3)
a[0] = [1, 2] # ValueError

aはfloatが3個並んだ配列なので、1要素にリストは入らない。2次元が必要なら最初からそう作る。

a = np.zeros((3, 2))
a[0] = [1, 2] # OK

スライスの形を確認するのが早い。

print(a.shape, a[0].shape)

3. dtypeを指定したが中身が合わない

数値のdtypeを指定した配列に、文字列や列が混ざっている場合になる。

a = np.array([1, 2, [3, 4]], dtype=float) # ValueError

データの出どころを疑ったほうがいい。CSVやJSONから読んだ値に、想定外の入れ子が混ざっていることが多い。

for i, v in enumerate(data):
if not np.isscalar(v):
print(i, type(v), v)

これで犯人の位置が分かる。

4. pandasの列にリストが入っている

DataFrameの列の中身がリストになっていると、valuesto_numpyで取り出した時点で問題になる。

df["tags"] = [[1, 2], [3], [4, 5, 6]]
np.array(df["tags"].to_list()) # ValueError

列を展開するか、長さを揃える。

df = df.explode("tags") # 行に展開する

同じ性質の問題はdrop_duplicatesgroupbyでも起きる。そちらはunhashable type: ‘list’にまとめた。

5. 機械学習ライブラリに渡すとき

scikit-learnやPyTorchに特徴量を渡すときによく出る。サンプルごとに特徴の数が違うと、この時点で弾かれる。

X = [[1.0, 2.0], [3.0], [4.0, 5.0]]
model.fit(X, y) # ValueError

系列の長さが可変なら、パディングして長さを揃えるか、そもそも固定長の特徴に変換する設計にする必要がある。

形を先に確認する習慣

原因が読めないときは、渡す直前で形を出してしまうのが確実になる。

print(type(data), len(data))
print([len(r) if hasattr(r, "__len__") else "scalar" for r in data][:20])

長さの一覧を見れば、どこだけ違うかが一目で分かる。要素数が多いなら集計する。

from collections import Counter
print(Counter(len(r) for r in data))

📘 特徴量の形をそろえる作業そのものについては、Kaggleで勝つデータ分析の技術

まとめ

・原因は「数値が入る場所に列が来ている」こと。NumPyの配列は長方形で同じ型である必要がある
・不揃いな入れ子リストが最頻の原因。0やnanで埋めて長さを揃えるのが本筋
・どうしても不揃いのまま持つならdtype=object。ただし高速化は効かなくなる
・1要素への代入で出る場合は、配列の次元が足りていない。shapeを確認する
・pandasの列にリストが入っている場合はexplodeで展開する
・渡す直前に長さの一覧をCounterで数えると、犯人がすぐ分かる

※本記事にはアフィリエイト広告を含みます。