NumPy C-API の enum NPY_SELECTKIND を駆使して、高速かつ効率的な要素抽出を実現


主な値とその意味

  • NPY_SELECT_RANDOM
    ランダムな順序で要素を選択します。
  • NPY_SELECT_BACKWARDS
    逆順に要素を選択します。
  • NPY_SELECT_NEAREST
    最も近いメモリ位置に格納されている要素を選択します。
  • NPY_SELECT_STANDARD
    標準的なメモリ配置で格納されている要素のみを選択します。
  • NPY_SELECT_CONTIGUOUS
    隣接するメモリ位置に格納されている要素のみを選択します。

使用例

#include <numpy/ndarrayobject.h>

npy_intp indices[10];
npy_intp strides[2] = {1, 2};

// 隣接するメモリ位置に格納されている要素のみ抽出
NPY_SELECT_KIND(NPY_SELECT_CONTIGUOUS, indices, 10, strides, 2, array);

// 標準的なメモリ配置で格納されている要素のみ抽出
NPY_SELECT_KIND(NPY_SELECT_STANDARD, indices, 10, strides, 2, array);

// 最も近いメモリ位置に格納されている要素のみ抽出
NPY_SELECT_KIND(NPY_SELECT_NEAREST, indices, 10, strides, 2, array);

// 逆順に要素を選択
NPY_SELECT_KIND(NPY_SELECT_BACKWARDS, indices, 10, strides, 2, array);

// ランダムな順序で要素を選択
NPY_SELECT_KIND(NPY_SELECT_RANDOM, indices, 10, strides, 2, array);
  • NPY_SELECT_BACKWARDSNPY_SELECT_RANDOM は、要素の抽出順序を制御するために使用できます。
  • NPY_SELECT_NEAREST は、配列が断片化されている場合に役立ちます。
  • NPY_SELECT_CONTIGUOUSNPY_SELECT_STANDARD は、配列が C 言語の連続メモリに格納されている場合にのみ使用できます。
  • NumPy C-API を使用する前に、NumPy のドキュメントをよく読んでください。
  • 上記のコードはあくまで例であり、状況に応じて変更する必要があります。
  • NumPy C-API は複雑な API であり、習得には時間がかかる場合があります。
  • NumPy C-API には、enum NPY_SELECTKIND 以外にも多くの列挙型と関数があります。


例1:隣接するメモリ位置に格納されている要素のみ抽出

#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_C);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 隣接するメモリ位置に格納されている要素のみ抽出
  npy_intp indices[6];
  NPY_SELECT_KIND(NPY_SELECT_CONTIGUOUS, indices, 6, array->strides, array->ndim, array);

  // 抽出された要素を出力
  for (int i = 0; i < 6; i++) {
    printf("%d ", ((double *)PyArray_GET_ITEM(array, indices[i], 0))[0]);
  }

  printf("\n");

  // 配列と PyArray オブジェクトを解放
  PyArray_XDECREF(array);
  return 0;
}

例2:標準的なメモリ配置で格納されている要素のみ抽出

#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_F);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 標準的なメモリ配置で格納されている要素のみ抽出
  npy_intp indices[6];
  NPY_SELECT_KIND(NPY_SELECT_STANDARD, indices, 6, array->strides, array->ndim, array);

  // 抽出された要素を出力
  for (int i = 0; i < 6; i++) {
    printf("%d ", ((double *)PyArray_GET_ITEM(array, indices[i], 0))[0]);
  }

  printf("\n");

  // 配列と PyArray オブジェクトを解放
  PyArray_XDECREF(array);
  return 0;
}
#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_C);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 最も近いメモリ位置に格納されている要素のみ抽出
  npy_intp indices[6];
  NPY_SELECT_KIND(NPY_SELECT_NEAREST, indices, 6, array->strides, array->ndim, array);

  // 抽出された要素を出力
  for (int i = 0; i < 6; i++) {
    printf("%d ", ((double *)PyArray_GET_ITEM(array, indices[i], 0))[0]);
  }

  printf("\n");

  // 配列と PyArray オブジェクトを解放
  PyArray_XDECREF(array);
  return 0;
}


#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_C);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 偶数の要素のみ抽出
  npy_intp indices[3];
  PyArray_SelectFromIndex(indices, 3, array, NULL, NULL, 0, NPY_SELECT_CONTIGUOUS);

  // 抽出された要素を出力
  for (int i = 0; i < 3; i++) {
    printf("%d ", ((double *)PyArray_GET_ITEM(array, indices[i], 0))[0]);
  }

  printf("\n");

  // 配列と PyArray オブジェクトを解放
  PyArray_XDECREF(array);
  return 0;
}

要素の抽出順序を制御

enum NPY_SELECTKIND とともに、PyArray_SortIndices 関数を使用して、要素の抽出順序を制御することができます。

#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_C);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 要素を昇順に抽出
  npy_intp indices[6];
  NPY_SELECT_KIND(NPY_SELECT_CONTIGUOUS, indices, 6, array->strides, array->ndim, array);
  PyArray_SortIndices(indices, 6, array);

  // 抽出された要素を出力
  for (int i = 0; i < 6; i++) {
    printf("%d ", ((double *)PyArray_GET_ITEM(array, indices[i], 0))[0]);
  }

  printf("\n");

  // 配列と PyArray オブジェクトを解放
  PyArray_XDECREF(array);
  return 0;
}

複数の条件を組み合わせる

enum NPY_SELECTKIND とともに、PyArray_Where 関数を使用して、複数の条件を組み合わせて要素を抽出することができます。

#include <numpy/ndarrayobject.h>

int main() {
  // 2 次元の NumPy 配列を作成
  npy_intp dims[] = {2, 3};
  PyArrayObject *array = PyArray_SimpleNewFromData(NDARRAY_DOUBLE, dims, NULL, NPY_ORDER_C);

  // 配列の要素に値を設定
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ((double *)PyArray_GET_ITEM(array, i, j))[0] = i * 3 + j;
    }
  }

  // 偶数かつ 5 未満の要素のみ抽出
  npy_intp indices[2];
  PyArray_Where(indices, 2, PyArray_And(NULL, PyArray_Eq(array, 0, NPY_BOOL), PyArray_Less(array, 5, NPY_BOOL)), NULL, NULL);

  // 抽出された要素を出力
  for (int