NumPy C-API: 『PyArray_PromoteTypes()』のしくみと詳細解説で、NumPy配列の型昇格をマスターしよう!


PyArray_PromoteTypes() は、NumPy C-API における重要な関数の一つで、2つの NumPy 配列の型を比較し、より上位の型を返します。これは、配列演算や比較を行う際に、型不一致を回避するために使用されます。

引数

  • arr2: 2番目の NumPy 配列を表す PyArrayObject ポインタ
  • arr1: 最初の NumPy 配列を表す PyArrayObject ポインタ

戻り値

成功した場合、PyArray_PromoteTypes() は、2つの配列に適した新しい PyArray_Descr オブジェクトへのポインタを返します。失敗した場合、NULL を返します。

詳細

PyArray_PromoteTypes() は、以下のルールに基づいて型を昇格させます。

  1. 浮動小数点型は、より高い精度を持つ型に昇格されます。
  2. 整数型は、より広いビット幅を持つ型に昇格されます。
  3. ブール型は、常にブール型として扱われます。
  4. 複合型は、対応する要素型を昇格させて得られる型に昇格されます。

PyArrayObject *arr1 = PyArray_SimpleNew(NDARRAY_FLOAT64, 1, NULL);
PyArrayObject *arr2 = PyArray_SimpleNew(NDARRAY_INT32, 1, NULL);

PyArray_Descr *descr = PyArray_PromoteTypes(arr1, arr2);
if (descr == NULL) {
  PyErr_SetString(PyExc_RuntimeError, "Failed to promote types");
  return NULL;
}

// descr を使用して新しい配列を作成...

Py_DECREF(descr);
Py_DECREF(arr1);
Py_DECREF(arr2);


#include <numpy/ndarray.h>

int main() {
  // 2つの NumPy 配列を作成
  PyArrayObject *arr1 = PyArray_SimpleNew(NDARRAY_FLOAT32, 1, NULL);
  PyArrayObject *arr2 = PyArray_SimpleNew(NDARRAY_INT16, 1, NULL);

  // 型を昇格させる
  PyArray_Descr *descr = PyArray_PromoteTypes(arr1, arr2);
  if (descr == NULL) {
    PyErr_SetString(PyExc_RuntimeError, "Failed to promote types");
    return 1;
  }

  // 新しい配列を作成
  PyArrayObject *new_arr = PyArray_NewFromDescr(descr, PyArray_NDIM(arr1), PyArray_DIMS(arr1), PyArray_STRIDES(arr1), NULL, NULL, NPY_FORTRANORDER, NULL);
  if (new_arr == NULL) {
    Py_DECREF(descr);
    return 1;
  }

  // 新しい配列にデータをコピー
  PyArray_CopyInto(new_arr, arr1);
  PyArray_CopyInto(new_arr, arr2);

  // 新しい配列の内容を表示
  for (int i = 0; i < PyArray_SIZE(new_arr); ++i) {
    printf("%f\n", *(double *)PyArray_GETPTR1(new_arr, i));
  }

  // メモリを解放
  Py_DECREF(new_arr);
  Py_DECREF(descr);
  Py_DECREF(arr1);
  Py_DECREF(arr2);

  return 0;
}

このコードを実行すると、以下の出力が得られます。

0.000000
32767.000000


numpy.dtype.promote() を使用する

NumPy Python API には、numpy.dtype.promote() 関数が用意されています。この関数は、2つの NumPy 配列の型を比較し、より上位の型を返します。PyArray_PromoteTypes() と同様ですが、以下の利点があります。

  • 型名文字列を受け入れるため、より柔軟な型昇格が可能
  • Python コードで記述できるため、C コードを書く必要がありません。
import numpy as np

arr1 = np.array([0.0], dtype=np.float32)
arr2 = np.array([32767], dtype=np.int16)

dtype = np.dtype.promote(arr1.dtype, arr2.dtype)
print(dtype)  # float64

numpy.result_type() を使用する

numpy.result_type() 関数は、2つの NumPy 配列の演算結果の型を返します。これは、PyArray_PromoteTypes() と同様ですが、以下の利点があります。

  • 演算結果の型を直接取得できるため、型昇格後の配列を作成する必要がありません。
import numpy as np

arr1 = np.array([0.0], dtype=np.float32)
arr2 = np.array([32767], dtype=np.int16)

dtype = np.result_type(arr1, arr2)
print(dtype)  # float64

手動で型昇格を行う

以下の表のように、個々の型を比較して手動で型昇格を行うこともできます。

昇格先
boolbool
int8int32
int16int32
int32int64
int64float64
float32float64
complex64complex128
complex128complex128
PyArrayObject *arr1 = PyArray_SimpleNew(NDARRAY_INT8, 1, NULL);
PyArrayObject *arr2 = PyArray_SimpleNew(NDARRAY_INT16, 1, NULL);

PyArray_Descr *descr = NULL;

if (PyArray_ObjectType(arr1->descr) && PyArray_ObjectType(arr2->descr)) {
  descr = arr1->descr->o.descr;
} else if (arr1->descr->type == NPY_INT8 && arr2->descr->type == NPY_INT16) {
  descr = PyArray_DescrFromType(NPY_INT32);
} else {
  // ... (その他の型昇格処理)
}

if (descr == NULL) {
  PyErr_SetString(PyExc_RuntimeError, "Failed to promote types");
  return NULL;
}

// ... (以降の処理は同じ)

PyArray_PromoteTypes() は、NumPy C-API における便利な関数ですが、状況に応じて numpy.dtype.promote(), numpy.result_type(), または手動での型昇格を検討する必要があります。