『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

【NumPy】配列の形状変更

配列の形状変更

NumPy 配列を形状変更するメソッドや関数の一覧です。

ndarray.reshape()

ndarray.reshape() を使うと配列の形状を変更できます。

ndarray.reshape(shape, order='C')

shape には新しい配列の行数 m と列数 n をタプルやリスト、整数で渡します。
 
12要素の1次元配列を作成してから、reshape() メソッドで 3×4 の 2 次元配列に形状変更してみます。

# NDARRAY_RESHAPE

# In[1]

import numpy as np

# 12要素の1次元配列を作成
# [1 1 1 1 1 1 1 1 1 1 1 1]
arr = np.ones(12, dtype=np.int32)

# arrを3×4に形状変更
arr_re = arr.reshape((3, 4))

print(arr_re)
# [[1 1 1 1]
#  [1 1 1 1]
#  [1 1 1 1]]

ndarray.reshape() はほとんどの場合、ビュー を返します。実際、arr が変更されていないことを確認しておきましょう。

# In[2]

print(arr)
# [1 1 1 1 1 1 1 1 1 1 1 1]

ndarray.reshape() がビューではなく コピー を返す時もありますが、レアケースなので詳細は割愛します。
 
引数には並び替え可能な数値を渡す必要があります。すなわち要素数は m で割り切れて、かつ変更後の配列のインデックスはすべて埋められなければなりません。たとえば、要素数 11 の 1 次元配列を 3×4 の配列に変更しようとしても ValueEroor が発生します。

# In[3]

# 11要素の1次元配列を生成して3×4に形状変更を試みる
arr = np.ones(11, dtype=np.int32).reshape((3, 4))

print(arr)
# ValueError: cannot reshape array of size 11 into shape (3,4)

たとえば、1 次元配列を 2 次元配列に形状変更するとき、行数だけを指定して列数の指定を省略することもできます。その場合、省略したい引数に -1 を渡します。

# In[4]

# 要素数16の配列を2行の配列に形状変更
arr = np.arange(16, dtype=np.int32).reshape((2, -1))

print(arr)
# [[ 0  1  2  3  4  5  6  7]
#  [ 8  9 10 11 12 13 14 15]]

In[4] では、要素数 16 の配列を 2 行の 2 次元配列に変更していますが、reshape() の第 2 引数に -1 を指定することで、列数を 8 に自動調整しています。
 
多次元配列に対して reshape(-1) を実行すると、配列をフラットに (1 次元配列に形状変更) します。

# In[5]

# 配列をフラットにする
arr = arr.reshape(-1)

print(arr)
# [0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]

この手法は機械学習などで多用します。

ndarray.resize()

ndarray.resize() は配列の形状を変更します。

ndarray.resize(new_shape, refcheck=True)

new_shape には変更後の形状を整数やタプルで渡します。

# NDARRAY_RESIZE

# In[1]

import numpy as np

arr = np.array([[1, 2, 3, 4],
                [5, 6, 7, 8]])

# arrの形状を4×2に変更
arr.resize((4, 2))

print(arr)
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]

refcheck は参照カウントをチェックする引数で、デフォルトでは True に設定されています。デフォルトで要素が増減するような形状変更を試みるとエラーとなります。

# In[2]

# arrの形状を2×2に変更
arr.resize((2, 2))

print(arr)
# ValueError: cannot resize an array that references or is referenced by another array in this way.
# Use the np.resize function or refcheck=False

エラーメッセージにあるように、refcheck=False とすると配列の形状を自由に変えられます。たとえば、新しい配列がもとの配列より小さければ、余った要素は捨てられます。

# In[3]

# arrの形状を2×2に変更
arr.resize((2, 2), refcheck=False)

print(arr)
# [[1 2]
#  [3 4]]

変更された配列がもとの配列より大きければ、必要なだけ 0 で埋められます。

# In[4]

# arrの形状を2×3に変更
arr.resize((2, 3), refcheck=False)

print(arr)
# [[1 2 3]
#  [4 0 0]]

numpy.reshape()

numpy.reshape() は配列の形状を変更します。

numpy.reshape(arr, newshape, order='C')

arr には配列を、newshape には新しい配列の形状を整数、タプル、リストなどで渡します。

# NUMPY_RESHAPE

# In[1]

import numpy as np

# 2×3の配列を定義
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

# arrの形状を3×2に変更
arr_re = np.reshape(arr, (3, 2))

print(arr_re)
# [[1 2]
#  [3 4]
#  [5 6]]

numpy.reshape() はコピーを返すので、もとの配列 arr は変更されていません。

# In[2]

print(arr)
# [[1 2 3]
#  [4 5 6]]

もとの配列と新しい配列の要素数が一致していなければエラーを返します。

# In[3]

# 2×3配列は3×3配列に変更できない
arr_re = np.reshape(arr, (3, 3))
# ValueError: cannot reshape array of size 6 into shape (3,3)

形状を指定する数値に -1 を渡すと形状が自動調整されます。たとえば、numpy.reshape(arr, (3, -1)) は arr を 3 行の行列に形状変更し、列数は arr の要素数を 3 で割った値となります。

# In[4]

# arrを3行の配列に形状変更する
arr_re = np.reshape(arr, (3, -1))

print(arr_re)
# [[1 2]
#  [3 4]
#  [5 6]]

多次元配列 arr に対して np.reshape(arr, -1) を実行すると、arr をフラットにします。

# In[5]

# arrをフラットにする
arr_re = np.reshape(arr, -1)

print(arr_re)
# [1 2 3 4 5 6]

numpy.resize()

numpy.resize() は配列の形状を変更します。

numpy.resize(arr, new_shape)

新しい配列がもとの配列 arr より大きければ、必要なだけ arr の要素を繰り返して埋めます。

# NUMPY_RESIZE

# In[1]

import numpy as np

# 2×2の配列を定義
arr = np.array([[1, 2],
                [3, 4]])

# arrの形状を3×3に変更
arr_re = np.resize(arr, (3, 3))

print(arr_re)
# [[1 2 3]
#  [4 1 2]
#  [3 4 1]]

numpy.resize() はコピーを返すので、渡した配列は変更されません。

# In[2]

print(arr)
# [[1 2]
#  [3 4]]

新しい配列がもとの配列よりも小さければ、余った要素は捨てられます。

# In[3]

# arrの形状を2×3に変更
arr_re = np.resize(arr_re, (2, 3))

print(arr_re)
# [[1 2 3]
#  [4 1 2]]

numpy.newaxisキーワード

numpy.newaxis キーワードを使って新しい次元を作成して要素をスライシングすることで、配列の形状を変更することもできます。以下のサンプルコードでは、1 次元配列 [1 2 3] に新しい次元を追加して行ベクトルと列ベクトルとよばれる 2 次元配列を生成します。

# NUMPY_NEWAXIS

# In[1]

import numpy as np

# 1次元配列を定義
x = np.array([1, 2, 3])

# 行ベクトルを作成
y = x[np.newaxis, :]

# 列ベクトルを作成
z = x[:, np.newaxis]

print("行ベクトル\n{}\n".format(y))
print("列ベクトル\n{}".format(z))

# 行ベクトル
# [[1 2 3]]
 
# 列ベクトル
# [[1]
  [2]
  [3]]

numpy.newaxis は特別なキーワードではなく、None への参照です。すなわち、x[np.newaxis, :] は x[None, :] と書き換えても機能します(つまり要素をもたない次元を追加しています)。

コメント

  1. HNaito より:

    次の記事で、「reshape(-1)がビューを生成する」という意味を調べて、reshape( ) はビューを返し、resize( ) はコピーを返すという違いがあることを知りました。
    こちらの記事でも両者の違いを明記していただけるとありがたいです。

    • あとりえこばと より:

      了解しました。
      なるべく早くご要望にお応えしたいと思います。
      少々お待ちください。m(_ _)m
       
      Matplotlib の Axes3D の視点変更(仰角・方位角の設定)の記事を投稿しました。
      「配列の連結・分割」に numpy.array() を追加しました。ぜひご一読ください。

  2. HNaito より:

    ndarray.resize( ) は配列作成直後の 1 回目は RESIZE-1プログラムのようにうまくいきますが、続けてその下のRESIZE-2プログラムを実行するとエラーになります。エラーメッセージによると、refcheck=False にするとエラーは回避できるようですが、配列サイズの変更には numpy.resize( ) を使っておいたほうが無難だと思いました。

    • あとりえこばと より:

      本当ですね。申し訳ないです。refcheck は参照カウントが 0 になっていることをチェックするオプションです。numpy.resize(arr) が arr のコピーを生成するのに対して、arr.resize() はビューを返します。arr がコードのどこかから参照されている場合は、形状を変えてしまうとまずいことになるので、安全弁として refcheck=True をデフォルトにしてあるのだと思います。明日はこちらの仕事に専念できるので、以前にご要望があったコピーやビューのことも含めて、記事を全般的に書き直しておきます。それから、近いうちに新着記事で参照カウントやガベージコレクションのことも扱います。

  3. HNaito より:

    下記は誤植と思われるので、ご確認をお願いいたします。
    NUMPY_RESAHPE プログラムの上の説明で、リストなどを渡します。 → リストなどで渡します。
    NUMPY_RESAHPE In[1] プログラムで、np.reshape(arr, (2, 3)) → np.reshape(arr, (3, 2))
    NUMPY_RESAHPE In[3] プログラムの出力表示で、alueError → ValueError

  4. HNaito より:

    「要素をもたない次元」というのは「0」ではないかと思い、
    np.array([]).ndim
    を実行したら「1」が返ってきました。
    なるほど、「要素をもたない次元を追加している」の意味が通りました。

    • あとりえこばと より:

      ちなみに、np.array([]).shape は (0,) となります。
      要素 0 個の 1 次元配列という意味です。
      np.array([[]]).shape は (1, 0), np.array([[],[]]).shape は (2, 0) です。
      それぞれ 2 次元配列で、空の配列 [] が要素として数えられています。