NumPyのデータ型dtype一覧とastypeによる変換(キャスト)
NumPy配列ndarray
はデータ型dtype
を保持しており、np.array()
でndarray
オブジェクトを生成する際に指定したり、astype()
メソッドで変更したりすることができる。
基本的には一つのndarray
オブジェクトに対して一つのdtype
が設定されていて、すべての要素が同じデータ型となる。
一つのndarray
で複数のデータ型を扱うためのStructured Data(構造化データ)という仕組みも用意されているが、ここでは触れない。複数のデータ型を含む配列(数値の列と文字列の列を含む二次元配列など)を処理するにはpandasのほうが便利。
- 関連記事: pandasの記事一覧
ここでは、
- NumPyの主要なデータ型
dtype
一覧 - 数値型の取り得る範囲(最小値・最大値)の確認
np.iinfo()
np.finfo()
- 文字列の文字数についての注意
object
型: Pythonオブジェクトへのポインターを格納astype()
によるデータ型dtype
の変換(キャスト)astype()
で変換(キャスト)する際の注意点- 暗黙の型変換
について、サンプルコードとともに説明する。
pandasのデータ型dtype
とastype()
については以下の記事を参照。
NumPyの主要なデータ型dtype一覧
主要なデータ型dtype
は以下の通り。特に整数、浮動小数点数においてそれぞれの型が取り得る値の範囲は後述。
データ型dtype |
型コード | 説明 |
---|---|---|
int8 |
i1 |
符号あり8ビット整数型 |
int16 |
i2 |
符号あり16ビット整数型 |
int32 |
i4 |
符号あり32ビット整数型 |
int64 |
i8 |
符号あり64ビット整数型 |
uint8 |
u1 |
符号なし8ビット整数型 |
uint16 |
u2 |
符号なし16ビット整数型 |
uint32 |
u4 |
符号なし32ビット整数型 |
uint64 |
u8 |
符号なし64ビット整数型 |
float16 |
f2 |
半精度浮動小数点型(符号部1ビット、指数部5ビット、仮数部10ビット) |
float32 |
f4 |
単精度浮動小数点型(符号部1ビット、指数部8ビット、仮数部23ビット) |
float64 |
f8 |
倍精度浮動小数点型(符号部1ビット、指数部11ビット、仮数部52ビット) |
float128 |
f16 |
四倍精度浮動小数点型(符号部1ビット、指数部15ビット、仮数部112ビット) |
complex64 |
c8 |
複素数(実部・虚部がそれぞれfloat32 ) |
complex128 |
c16 |
複素数(実部・虚部がそれぞれfloat64 ) |
complex256 |
c32 |
複素数(実部・虚部がそれぞれfloat128 ) |
bool |
? |
ブール型(True or False ) |
unicode |
U |
Unicode文字列 |
object |
O |
Pythonオブジェクト型 |
データ型名の末尾の数字はbit
で表し、型コード末尾の数字はbyte
で表す。同じ型でも値が違うので注意。
また、bool
型の型コード?
は不明という意味ではなく文字通り?
が割り当てられている。
各種メソッドの引数でデータ型dtype
を指定するとき、例えばint64
型の場合は、
np.int64
- 文字列
'int64'
- 型コードの文字列
'i8'
のいずれでもOK。
import numpy as np
a = np.array([1, 2, 3], dtype=np.int64)
print(a.dtype)
# int64
a = np.array([1, 2, 3], dtype='int64')
print(a.dtype)
# int64
a = np.array([1, 2, 3], dtype='i8')
print(a.dtype)
# int64
ビット精度の数値を省略してint
やfloat
, str
のようなPythonの型で指定することもできる。
この場合、等価なdtype
に自動的に変換されるが、どのdtype
に変換されるかは環境(Python2かPython3か、32ビットか64ビットかなど)によって異なる。
以下の例はPython3、64ビット環境のもの。uint
というPythonの型はないが便宜上まとめて挙げておく。
Pythonの型 | 等価なdtype の例 |
---|---|
int |
int64 |
float |
float64 |
str |
unicode |
(uint ) |
uint64 |
引数で指定する場合はint
でも文字列'int'
でもOK。Pythonの型ではないuint
は文字列'uint'
のみ可。
print(int is np.int)
# True
a = np.array([1, 2, 3], dtype=int)
print(a.dtype)
# int64
a = np.array([1, 2, 3], dtype='int')
print(a.dtype)
# int64
数値型の取り得る範囲(最小値・最大値)の確認
整数int
, uint
や浮動小数点数float
の各データ型の取り得る値の範囲はnp.iinfo()
, np.finfo()
で確認できる。
np.iinfo()
整数int
, uint
に対してはnp.iinfo()
を使う。
引数に型オブジェクトを指定するとnumpy.iinfo
型が返される。
print()
で概要を出力したり、max
属性やmin
属性などで最大値や最小値などを数値として取得できる。
ii64 = np.iinfo(np.int64)
print(type(ii64))
# <class 'numpy.iinfo'>
print(ii64)
# Machine parameters for int64
# ---------------------------------------------------------------
# min = -9223372036854775808
# max = 9223372036854775807
# ---------------------------------------------------------------
#
print(ii64.max)
# 9223372036854775807
print(type(ii64.max))
# <class 'int'>
print(ii64.min)
# -9223372036854775808
print(ii64.bits)
# 64
引数にはデータ型dtype
を示す文字列なども指定可能。符号なし整数のuint
にも対応している。
print(np.iinfo('int16'))
# Machine parameters for int16
# ---------------------------------------------------------------
# min = -32768
# max = 32767
# ---------------------------------------------------------------
#
print(np.iinfo('i4'))
# Machine parameters for int32
# ---------------------------------------------------------------
# min = -2147483648
# max = 2147483647
# ---------------------------------------------------------------
#
print(np.iinfo(int))
# Machine parameters for int64
# ---------------------------------------------------------------
# min = -9223372036854775808
# max = 9223372036854775807
# ---------------------------------------------------------------
#
print(np.iinfo('uint64'))
# Machine parameters for uint64
# ---------------------------------------------------------------
# min = 0
# max = 18446744073709551615
# ---------------------------------------------------------------
#
値そのものを引数に指定することもできる。
i = 100
print(type(i))
# <class 'int'>
print(np.iinfo(i))
# Machine parameters for int64
# ---------------------------------------------------------------
# min = -9223372036854775808
# max = 9223372036854775807
# ---------------------------------------------------------------
#
ui = np.uint8(100)
print(type(ui))
# <class 'numpy.uint8'>
print(np.iinfo(ui))
# Machine parameters for uint8
# ---------------------------------------------------------------
# min = 0
# max = 255
# ---------------------------------------------------------------
#
NumPy配列ndarray
はダメ。dtype
属性でデータ型を取得するか、要素を取得して指定する。
a = np.array([1, 2, 3], dtype=np.int8)
print(type(a))
# <class 'numpy.ndarray'>
# print(np.iinfo(a))
# ValueError: Invalid integer data type 'O'.
print(np.iinfo(a.dtype))
# Machine parameters for int8
# ---------------------------------------------------------------
# min = -128
# max = 127
# ---------------------------------------------------------------
#
print(np.iinfo(a[0]))
# Machine parameters for int8
# ---------------------------------------------------------------
# min = -128
# max = 127
# ---------------------------------------------------------------
#
np.finfo()
浮動小数点数float
にはnp.finfo()
を使う。
使い方はnp.iinfo()
と同じ。引数には型オブジェクト(np.float64
)や文字列('float64'
, 'f8'
)や値(0.1
)などを指定可能。
print()
で概要を出力したり、各属性の値を数値として取得したりできる。
fi64 = np.finfo(np.float64)
print(type(fi64))
# <class 'numpy.finfo'>
print(fi64)
# Machine parameters for float64
# ---------------------------------------------------------------
# precision = 15 resolution = 1.0000000000000001e-15
# machep = -52 eps = 2.2204460492503131e-16
# negep = -53 epsneg = 1.1102230246251565e-16
# minexp = -1022 tiny = 2.2250738585072014e-308
# maxexp = 1024 max = 1.7976931348623157e+308
# nexp = 11 min = -max
# ---------------------------------------------------------------
#
print(fi64.max)
# 1.7976931348623157e+308
print(type(fi64.max))
# <class 'numpy.float64'>
print(fi64.min)
# -1.7976931348623157e+308
print(fi64.eps)
# 2.220446049250313e-16
print(fi64.bits)
# 64
print(fi64.iexp)
# 11
print(fi64.nmant)
# 52
上の例のように、np.iinfo()
よりも多くの情報、例えば、eps
でイプシロン、iexp
, nmant
で指数部および仮数部のビット数などが取得できる。詳細は上記の公式ドキュメントを参照。
文字列の文字数についての注意
str
やunicode
などで文字列として指定する場合のdtype
は<U1
のようになる。
a_str = np.array([1, 2, 3], dtype=str)
print(a_str)
print(a_str.dtype)
# ['1' '2' '3']
# <U1
先頭の<
, >
は、それぞれリトルエンディアン、ビッグエンディアンを表している。ほとんどの場合、特に気にする必要はない。
末尾の数字は文字数を表し、この例のようにコンストラクタでdtype
をstr
やunicode
と指定した場合、要素の中で最大の文字数となる。
それぞれの要素に対してこの文字数分のメモリしか確保されていないので、それ以上の文字数の文字列は保持できず切り捨てられる。あらかじめ十分な文字数の型を指定しておけばOK。
a_str[0] = 'abcde'
print(a_str)
# ['a' '2' '3']
a_str10 = np.array([1, 2, 3], dtype='U10')
print(a_str10.dtype)
# <U10
a_str10[0] = 'abcde'
print(a_str10)
# ['abcde' '2' '3']
object型: Pythonオブジェクトへのポインターを格納
object
型は特殊なデータ型で、Pythonオブジェクトへのポインターを格納する。
各要素のデータの実体はそれぞれメモリ領域を確保するので、一つの配列ndarray
内に複数の型のデータ(へのポインタ)をもつことができる。
a_object = np.array([1, 0.1, 'one'], dtype=object)
print(a_object)
print(a_object.dtype)
# [1 0.1 'one']
# object
print(type(a_object[0]))
print(type(a_object[1]))
print(type(a_object[2]))
# <class 'int'>
# <class 'float'>
# <class 'str'>
文字数の変更も可能。
a_object[2] = 'oneONE'
print(a_object)
# [1 0.1 'oneONE']
なお、このような複数の型を持つ配列はPython標準のリストlist
型でも実現できる。
list
とNumPy配列ndarray
は演算子に対する振る舞いが異なり、ndarray
の場合は各要素に対する演算が簡単にできるが、あえてNumPyでこのようなデータを作成して処理するメリットは少ないかもしれない。
l = [1, 0.1, 'oneONE']
print(type(l[0]))
print(type(l[1]))
print(type(l[2]))
# <class 'int'>
# <class 'float'>
# <class 'str'>
print(a_object * 2)
# [2 0.2 'oneONEoneONE']
print(l * 2)
# [1, 0.1, 'oneONE', 1, 0.1, 'oneONE']
astype()によるデータ型dtypeの変換(キャスト)
NumPy配列ndarray
のメソッドastype()
でデータ型dtype
を変換(キャスト)することができる。
dtype
が変更された新たなndarray
が生成され、もとのndarray
は変化しない。
import numpy as np
a = np.array([1, 2, 3])
print(a)
print(a.dtype)
# [1 2 3]
# int64
a_float = a.astype(np.float32)
print(a_float)
print(a_float.dtype)
# [1. 2. 3.]
# float32
print(a)
print(a.dtype)
# [1 2 3]
# int64
上述のように、dtype
は様々な書き方で指定できる。
a_float = a.astype(float)
print(a_float)
print(a_float.dtype)
# [1. 2. 3.]
# float64
a_str = a.astype('str')
print(a_str)
print(a_str.dtype)
# ['1' '2' '3']
# <U21
a_int = a.astype('int32')
print(a_int)
print(a_int.dtype)
# [1 2 3]
# int32
astype()で変換(キャスト)する際の注意点
floatからintへのキャスト
float
からint
へキャストする場合は小数点以下切り捨てとなる。負の値は0への丸め。
a = np.arange(50).reshape((5, 10)) / 10 - 2
print(a)
print(a.dtype)
# [[-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]]
# float64
a_int = a.astype('int64')
print(a_int)
print(a_int.dtype)
# [[-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]]
# int64
np.round()
を使うと偶数への丸めになる。一般的な四捨五入ではなく0.5
が1
ではなく0
に丸められたりする。
Python標準のround()
と同じ仕様。
print(np.round(a).astype(int))
# [[-2 -2 -2 -2 -2 -2 -1 -1 -1 -1]
# [-1 -1 -1 -1 -1 0 0 0 0 0]
# [ 0 0 0 0 0 0 1 1 1 1]
# [ 1 1 1 1 1 2 2 2 2 2]
# [ 2 2 2 2 2 2 3 3 3 3]]
以下のような関数を指定すると0.5
が1
となり、四捨五入を実現できる。
my_round_int = lambda x: np.round((x * 2 + 1) // 2)
print(my_round_int(a).astype(int))
# [[-2 -2 -2 -2 -2 -1 -1 -1 -1 -1]
# [-1 -1 -1 -1 -1 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 3 3 3 3 3]]
上の関数は負の値-0.5
が0
となる。-0.5
を-1
としたい場合は以下のような関数にする。
def my_round(x, digit=0):
p = 10 ** digit
s = np.copysign(1, x)
return (s * x * p * 2 + 1) // 2 / p * s
print(my_round(a).astype(int))
# [[-2 -2 -2 -2 -2 -2 -1 -1 -1 -1]
# [-1 -1 -1 -1 -1 -1 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 3 3 3 3 3]]
暗黙の型変換
astype()
による明示的な型変換だけでなく、演算によって暗黙の型変換が行われる場合がある。
例えば/
演算子による除算は浮動小数点数float
を返す。
a = np.array([1, 2, 3])
print(a)
print(a.dtype)
# [1 2 3]
# int64
print((a / 1).dtype)
# float64
print((a / 1.0).dtype)
# float64
+
, -
, *
, //
, **
では、整数int
同士の場合はint
、浮動小数点数float
が含まれる場合はfloat
となる。
print((a + 1).dtype)
# int64
print((a + 1.0).dtype)
# float64
print((a - 1).dtype)
# int64
print((a - 1.0).dtype)
# float64
print((a * 1).dtype)
# int64
print((a * 1.0).dtype)
# float64
print((a // 1).dtype)
# int64
print((a // 1.0).dtype)
# float64
print((a ** 1).dtype)
# int64
print((a ** 1.0).dtype)
# float64
ここまでの例はnumpy.ndarray
とスカラー値との演算だが、numpy.ndarray
同士の演算でも同様。
また、整数int
同士でもビット数が異なると型変換が発生する。
ones_int16 = np.ones(3, np.int16)
print(ones_int16)
# [1 1 1]
ones_int32 = np.ones(3, np.int32)
print(ones_int32)
# [1 1 1]
print((ones_int16 + ones_int32).dtype)
# int32
この例のように、基本的にはデータ量が多い方の型に変換されると考えておけばよい。
ただし、場合によっては、元のnumpy.ndarray
のいずれとも異なる型になることがある。ビット数が重要な処理の場合はastype()
で明示的に所望の型に変換しておいたほうが安心だろう。
ones_float16 = np.ones(3, np.float16)
print(ones_float16)
# [1. 1. 1.]
print((ones_int16 + ones_float16).dtype)
# float32
なお、要素への値の代入ではnumpy.ndarray
の型変換は行われない。
例えば、整数int
のnumpy.ndarray
に浮動小数点数float
の値を代入しても、numpy.ndarray
のデータ型はint
のまま。代入した値は小数点以下切り捨てとなる。
ones_int16[0] = 10.9
print(ones_int16)
# [10 1 1]
print(ones_int16.dtype)
# int16