numpy.ndarray.astype – 配列のデータ型を変更

ndarray.astypeは、配列のデータ型(dtype)を変更した新しい配列を生成するメソッドです。

なお、NumPyのdtypeについては『NumPyのdtypeの一覧と参照・指定・変更方法』で解説しています。この記事では型を指定する時の書き方なども触れているので、この記事とあわせてご確認ください。

それでは見ていきましょう。

目次

1. ndarray.astypeの使い方

まずは、ndarray.astypeの書き方やオプション引数を確認しましょう。

基本的には必須引数であるdtypeの指定だけで事足ります。それ以外のオプション引数を指定するケースは通常はあまりないと思いますが、こういう引数があるということは知っておいて損になりません。

ndarray.astypeメソッド

書き方:

ndarray.astype(dtype, order='K', casting='unsafe', subok=True, copy=True)

パラメーター:

引数   解説
dtypestr or dtype   dtypeを指定します。
order(optional)  {‘C’, ‘F’, ‘A’, ‘K’}
メモリレイアウト(配列の要素をメモリ内に格納する順序)を指定します。’C’はC言語方式です。’F’はFortran形式です。’A’は、元の配列がFortranであればFortran形式で、そうでなければ’C’言語方式で並べます。’K’は、元の配列のメモリ内の並び方に出来るだけ近づけるというものです。デフォルトは’K’です。
casting(optional) {‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’}キャスティング(型変更)のルールを指定します。実際に使うことはほぼありません。
subok(optional)bool元の配列のサブクラスを引き継ぐかどうかを指定します。デフォルトのTrueでは、元の配列のサブクラスを引き継ぎます。Falseでは、サブクラスは引き継がず、新しいndarrayとして生成されます。
copy(optional)boolデフォルトでは常に新しい配列を生成します。しかし、これをFalseにして、さらにdtype, order, subokが全て元の配列と同じである場合、元の配列をそのまま返します。

戻り値: 

ndarray: 元の配列のデータ型を変更した新しい配列

例外(Raises):

複素数に関する注意(ComplexWarning)
complex型からfloat型かint型に変更しようとするとエラーになります。これを避けるには、a.real.astype(t)を使います。

一緒に確認したい属性:

冒頭でもお伝えしていますが、全ての引数を使いこなせるようになる必要性はさほどありません。

このメソッドを使って、型を変換できるようになることが大切です。それでは、実際のコードで解説していきます。

1.1. 既存の配列のdtypeを変更した新しい配列を生成

ndarray.astypeは、既存の配列のdtypeを変更した新しい配列を生成します。早速、実際に見てみましょう。

型変換する時の書き方ですが、例えば、float型を指定する場合は、

  • float
  • np.float
  • ‘float’(文字列型)

のいずれの書き方でも可能です。

ビット数も変換したい場合は、例えば、

  • np.float32
  • ‘float32’
  • ‘f4’

のいずれかの書き方で可能です。最後の’f4’は型コードです。これらは、『NumPyのdtypeの一覧と参照・指定・変更方法』で解説しているので目を通しておきましょう。

それでは、まず、以下のコードで、dtypeがfloat型の配列を生成します。

In [1]:
import numpy as np
#  要素がfloat型の配列を生成
arr1 = np.array([1.0, 2.0, 2.5, 3.5])
print(arr1)
print(arr1.dtype)
[1.  2.  2.5 3.5]
float64

この配列をndarray.astypeでint型に変換したものが以下です。

In [2]:
#  arr1のdtypeをint型に変換した新しい配列を生成
arr2 = arr1.astype(int)
print(arr2)
print(arr2.dtype)
[1 2 2 3]
int64

float型からint型に変更されていますね。

ここで確認しておきたいのは、Python3標準ではfloat型からint型に変更した時は、小数点以下は四捨五入ではなく切り捨てになる点です。詳しく見ておきましょう。

まずは、-2から2.9までの数値を0.1刻みにしたshape(5, 10)の配列を生成しています。

In [3]:
'''  floatをintに変換した場合は小数点以下は切り下げ、負の値は0になる  '''

arr_f = np.arange(-20, 30).reshape(5, 10)/10
print(arr_f)
[[-2.  -1.9 -1.8 -1.7 -1.6 -1.5 -1.4 -1.3 -1.2 -1.1]
 [-1.  -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1]
 [ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9]
 [ 1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9]
 [ 2.   2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9]]

この配列をndarray.astypeで、int型に変換したものが以下です。符号に関係なく、小数点以下が切り捨てになっている点をご確認ください。

In [4]:
arr_i = arr_f.astype(int)
print(arr_i)
[[-2 -1 -1 -1 -1 -1 -1 -1 -1 -1]
 [-1  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0  0  0]
 [ 1  1  1  1  1  1  1  1  1  1]
 [ 2  2  2  2  2  2  2  2  2  2]]

このようにfloat型をint型に変換する時は丸めのルールに注意が必要だということを覚えておきましょう。

なお、以下のコードのように異なるビット数を指定することもできます。

In [5]:
'''  ビット数の指定も可能  '''
#  float64の配列をfloat32に変換した新しい配列を生成
arr3= arr1.astype(np.float32)
print(arr3)
print(arr3.dtype, '\n')

#  float64の配列をint32に変換した新しい配列を生成
arr4 = arr1.astype('int32')
print(arr4)
print(arr4.dtype)
[1.  2.  2.5 3.5]
float32 

[1 2 2 3]
int32

ただし、ビット数が変わると、思いがけないところで数値の丸めのルールが変わってしまうので、本当に必要な時以外は、ビット数を変更しない方が無難です。

また、complex型(複素数)の配列をint型かfloat型に変更しようとすると、以下のようにエラーになります。

In [1]:
import numpy as np
arr1 = np.array([1, 2, 3], dtype=complex)
print(arr1)
print(arr1.dtype)
[1.+0.j 2.+0.j 3.+0.j]
complex128
In [2]:
arr2 = arr1.astype(int)
/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: ComplexWarning: Casting complex values to real discards the imaginary part
  """Entry point for launching an IPython kernel.

これを避けるには、ndarrayのreal属性に対してndarray.astypeを実行します。real属性は複素数の実数部分のデータを保持しているインスタンス変数で、「配列.real」で参照することができます。

In [3]:
arr3 = arr1.real.astype(int)
print(arr3)
[1 2 3]

1.2. メモリレイアウトの変更(order)

NumPyの配列には、メモリレイアウト(メモリに格納する際の要素の並べ方)というものがあります。オプション引数orderでこれを指定することができます。

NumPyのメモリレイアウトにはC言語方式とFortran方式という2つの方式があります。

以下のコードは、それを明示的に表したものです。

In [4]:
'''  C言語方式(デフォルト)  '''
arr_c = np.arange(6).reshape((2, 3), order='C')
print('C言語方式\n', arr_c, '\n')

'''  Fortran形式  '''
arr_f = np.arange(6).reshape((2, 3), order='F')
print('Fortan形式\n', arr_f)
C言語方式
 [[0 1 2]
 [3 4 5]] 

Fortan形式
 [[0 2 4]
 [1 3 5]]

このように、C言語方式では、要素を順番に横に並べていきます。Fortran方式では、順番に縦に並べていきます。

これは、以下のコードのようにflags属性で確認することができます。C_CONTIGUOUSがC言語方式のメモリレイアウトで、これがTrueとなっているので、配列arr_cのメモリレイアウトはC言語方式です。

In [5]:
#  C言語方式のメモリレイアウトであることを確認
arr_c.flags
Out[5]:
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

Fortran方式の場合は、以下のコードのように、F_CONTIGUOUSがTrueになります。

In [6]:
#  Fortran方式のメモリレイアウトであることを確認
arr_f.flags
Out[6]:
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

ndarray.astypeでは、このメモリレイアウトをオプション引数orderで指定することができます。

以下のコードでは、元々C言語方式のメモリレイアウトの配列arr_cを、astypeメソッドでdtypeをfloat型に変更し、さらにorder=’F’でメモリレイアウトをFortran方式に変更した新しい配列arr5を生成しています。

In [7]:
#  C言語方式をdtypeをfloat型にしてFortran方式に変換
arr5 = arr_c.astype(float, order='F')
print(arr5, '\n')
print(arr5.flags['F_CONTIGUOUS'])
[[0. 1. 2.]
 [3. 4. 5.]] 

True

配列の見た目は変わりませんが、メモリレイアウトは指定通りFortran方式に変更されていることが確認できますね。

同じように、「order=’C’」と書けば、C言語方式のメモリレイアウトに変更されます。

In [8]:
#  Fortran方式をdtypeをfloat型にしてC言語方式に変換
arr6 = arr_f.astype(float, order='C')
print(arr6, '\n')
print(arr6.flags['C_CONTIGUOUS'])
[[0. 2. 4.]
 [1. 3. 5.]] 

True

astype()では、他にも「order=’A’」と「order=’K’」(デフォルト値)を指定することができます。

‘A’を指定した場合は、元の配列がC言語方式ならC言語方式に、Fortran方式ならFortran方式にメモリレイアウトを変更します。デフォルト値の’K’は、元の配列に出来るだけ近いメモリレイアウトにします。

1.3. dtypeの変換ルールの指定(casting)

オプション引数castingでは、dtypeの変換ルールを指定することができます。なお、「casting(キャスティング)」はコンピューター用語で、「変換する・変更する」という意味合いの言葉です。

このオプション引数では、以下の5つのキャスティングルールを指定することができます。

  • ‘no’: データ型のキャスティングは不可。
  • ‘equiv’: バイト順の変更のみ許可。
  • ‘safe’: 値を保つキャスティングのみ許可。
  • ‘same_kind’: float64からfloat32のような同タイプの型のキャスティングのみ許可。
  • ‘unsafe’(デフォルト値): いかなるデータの変更も許される。

結論から言うと、これらを使うことはほとんど全くありません。これらを指定すると型変換がそもそもできなくなるからです。

ただし、’same_kind’は、もしかしたら使うケースがあるかもしれません。そのため、ここではそれだけ見ることにします。

以下の、float64型の配列を例に見てみましょう。

In [1]:
import numpy as np
arr1 = np.array([1.0, 2.0, 2.5, 3.5])
print(arr1)
print(arr1.dtype)
[1.  2.  2.5 3.5]
float64

「casting=’same_kind’」を指定すると、以下のコードのように、バイト数の変更のみ可能になります。しかし、別の型への変更の場合はエラーになります。

In [2]:
#  casting='same_kind'の場合、バイト数の変更のみ可能になる。
arr2 = arr1.astype(np.float32, casting='same_kind')
print(arr2)
print(arr2.dtype)

#  それ以外の変更の場合はエラー
arr3 = arr1.astype(int, casting="same_kind")
[1.  2.  2.5 3.5]
float32

TypeError: Cannot cast array from dtype('float64') to dtype('int64') according to the rule 'same_kind'

1.4. 元の配列のサブクラスを引き継ぐかの指定(subok)

subokは「sub(サブクラス)でok(いいか)?」というのを指定するオプション引数です。

NumPyのndarrayにはサブクラスとして、numpy.matrixとMarkedArrayクラスがあります。元となる配列が、これらのサブクラスである場合、デフォルトの「subok=True」では、そのサブクラスを受け継ぎます。「subok=False」を指定すると、サブクラスを引き継がずndarrayとして生成されます。

それでは、以下のコードをご覧ください。ndarrayのサブクラスであるmatrixを生成しています。

In [1]:
import numpy as np
arr1 = np.array(np.mat('1.0 2.0; 3.0 4.0'), subok=True)
arr1
Out[1]:
matrix([[1., 2.],
        [3., 4.]])

このmatrixの要素の型はfloat型です。

これを、「subok=True」でint型に変換すると、以下のコードのように元のサブクラスであるmatrixを引き継ぎます。

なお、「subok=True」はデフォルト値なので、わざわざ書く必要はありませんが、ここでは明示的に表すために書いています。

In [2]:
#  subok=True(デフォルト値)だと元の配列のサブクラスを引き継ぐ
arr2 = arr1.astype(int, subok=True)
arr2
Out[2]:
matrix([[1, 2],
        [3, 4]])

「subok=False」にすると、元の配列のサブクラスは引き継がず、ndarrayとして生成されます。

In [3]:
#  subok=Falseだとサブクラスは引き継がずndarrayとして生成
arr3 = arr1.astype(int, subok=False)
arr3
Out[3]:
array([[1, 2],
       [3, 4]])

1.5. コピーの指定(copy)

これは、ndarray.astypeで指定することは恐らくありません。しかし、引数として指定することができるので、一応解説しておきます。

オプション引数で「copy=False」とした場合で、かつ、元の配列と新しい配列で同じdtype, order, subokを指定した場合、これは同じデータに別の2つの変数名が付いているのと同じ状態になります。

以下のコードで確認頂くと、この場合、arr1とarr_cfではオブジェクトIDが同じであることが確認できます。

In [1]:
import numpy as np

'''  データ型を変更せずにcopy=Falseにした場合'''

#  元の配列
arr1 = np.array([1.5, 2.5, 3.5])
print(arr1)
print('オブジェクトID: ', id(arr1), '\n')

#  copy=True(デフォルト)
arr_ct = arr1.astype(float, copy=True)
print(arr_ct)
print('オブジェクトID: ', id(arr_ct), '\n')

#   copy=False
arr_cf  = arr1.astype(float, copy=False)
print(arr_cf)
print('オブジェクトID: ', id(arr_cf))
[1.5 2.5 3.5]
オブジェクトID:  4633076000 

[1.5 2.5 3.5]
オブジェクトID:  4633076080 

[1.5 2.5 3.5]
オブジェクトID:  4633076000

これは、変数arr1と変数arr_cfは全く同じ場所にある同じデータを保持していることを意味しています。

そのため、変数arr1を使って中身のデータを変更した場合も、変数arr_cfを使って中身のデータを変更した場合も、結局、どちらも同じデータを持っているので、その変更は両変数に反映されます。

In [2]:
'''  オブジェクトIDが同じ=変数名が2つあるが中身は同じ  '''

#  arr1の値の変更は両者に反映
arr1[0] = 10
print(arr1)
print(arr_cf, '\n')

#  arr_cfの値の変更も両者に反映
arr_cf[2] = 30
print(arr1)
print(arr_cf, '\n')
[10.   2.5  3.5]
[10.   2.5  3.5] 

[10.   2.5 30. ]
[10.   2.5 30. ] 

2. まとめ

以上のように、ndarray.astypeは既存の配列のデータ型を変換した新しい配列を生成します。

最後に重要なポイントをまとめておきたいと思います。

  • 型の指定の際は、例えばfloat型に変換するなら、float、np.float、’float’のいずれかの方法で書く
  • ビット数も変換したい場合は、np.float32、’float32′, ‘f4(型コード)’のいずれかの方法で書く。
  • int型をfloat型に変換すると、小数点以下は切り捨てになる。ただし、ビット数も変換すると、数値の丸めのルールが予期せぬところで変化してしまう。
  • complex型をint型やfloat型に変換しようとするとエラーになる。この場合は実数部分を変換したいとしたら、arr.real.astype()というように実数属性の部分を指定して型変換を行う。

以上の点を頭に入れておくと、ndarray.astypeを使いこなせるようになるでしょう。



よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

目次
閉じる