Pythonでデータ分析を爆速化!NumPy C-API `int PyArray_Partition()` 関数の使い方


PyArray_Partition() 関数は、NumPy 配列を指定した基準に基づいて分割します。分割された要素は、2 つの新しい配列に格納されます。

構文

int PyArray_Partition(PyArrayObject *arr, PyArrayObject *partition, PyArrayObject *indices, NPY_ORDER order, int which)

引数

  • which: 分割基準の選択 (PARTITION_LT または PARTITION_GT)
  • order: 配列のメモリ配置順序 (C_CONTIGUOUS または FORTRAN_CONTIGUOUS)
  • indices: 分割後の要素のインデックスを格納する NumPy 配列
  • partition: 分割基準となる NumPy 配列
  • arr: 分割対象の NumPy 配列

戻り値

成功した場合、0 を返します。失敗した場合、負の値を返します。

詳細

PyArray_Partition() 関数は、クイックソート アルゴリズムを使用して配列を分割します。

  • indices 配列には、分割後の要素のインデックスが格納されます。
  • 分割された要素は、2 つの新しい配列に格納されます。
    • 左側の新しい配列には、partition の要素よりも小さいまたは大きい arr の要素が含まれます。
    • 右側の新しい配列には、残りの arr の要素が含まれます。
  • which パラメータによって比較の基準が決定されます。
    • PARTITION_LT: partition の要素が arr の要素よりも小さい場合、arr の要素は左側の新しい配列に格納されます。
    • PARTITION_GT: partition の要素が arr の要素よりも大きい場合、arr の要素は左側の新しい配列に格納されます。
  • partition 配列の各要素と arr 配列の対応する要素が比較されます。

#include <numpy/arrayobject.h>

int main() {
  // 初期化
  npy_intp dims[] = {5};
  PyArrayObject *arr = PyArray_SimpleNew(1, dims, NPY_INT32);
  PyArrayObject *partition = PyArray_SimpleNew(1, dims, NPY_INT32);
  PyArrayObject *indices = PyArray_SimpleNew(1, dims, NPY_INT32);

  // データのセット
  int *arr_data = (int *)PyArray_DATA(arr);
  int *partition_data = (int *)PyArray_DATA(partition);
  for (int i = 0; i < 5; i++) {
    arr_data[i] = i + 1;
    partition_data[i] = 3;
  }

  // 配列の分割
  int status = PyArray_Partition(arr, partition, indices, NPY_CORDER, PARTITION_LT);
  if (status < 0) {
    PyErr_PrintEx(0);
    return 1;
  }

  // 分割結果の表示
  printf("分割された配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", arr_data[i]);
  }
  printf("\n");

  printf("分割基準配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", partition_data[i]);
  }
  printf("\n");

  printf("インデックス配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", indices[i]);
  }
  printf("\n");

  // メモリ解放
  Py_XDECREF(arr);
  Py_XDECREF(partition);
  Py_XDECREF(indices);

  return 0;
}
  • PyArray_Partition() 関数は、メモリを割り当てたり解放したりするため、適切なメモリ管理が必要です。
  • 分割された要素は、元の配列とは異なる順序で格納されます。
  • partitionindices 配列は、arr 配列と同じサイズである必要があります。
  • PyArray_Partition() 関数は、NumPy 配列のみを操作できます。


例 1: 整数配列の分割

この例では、整数配列を基準値と比較して分割します。

#include <numpy/arrayobject.h>

int main() {
  // 初期化
  npy_intp dims[] = {5};
  PyArrayObject *arr = PyArray_SimpleNew(1, dims, NPY_INT32);
  PyArrayObject *partition = PyArray_SimpleNew(1, dims, NPY_INT32);
  PyArrayObject *indices = PyArray_SimpleNew(1, dims, NPY_INT32);

  // データのセット
  int *arr_data = (int *)PyArray_DATA(arr);
  int *partition_data = (int *)PyArray_DATA(partition);
  for (int i = 0; i < 5; i++) {
    arr_data[i] = i + 1;
    partition_data[i] = 3;
  }

  // 配列の分割
  int status = PyArray_Partition(arr, partition, indices, NPY_CORDER, PARTITION_LT);
  if (status < 0) {
    PyErr_PrintEx(0);
    return 1;
  }

  // 分割結果の表示
  printf("分割された配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", arr_data[i]);
  }
  printf("\n");

  printf("分割基準配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", partition_data[i]);
  }
  printf("\n");

  printf("インデックス配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", indices[i]);
  }
  printf("\n");

  // メモリ解放
  Py_XDECREF(arr);
  Py_XDECREF(partition);
  Py_XDECREF(indices);

  return 0;
}

例 2: 浮動小数点配列の分割

この例では、浮動小数点配列を基準値と比較して分割します。

#include <numpy/arrayobject.h>

int main() {
  // 初期化
  npy_intp dims[] = {5};
  PyArrayObject *arr = PyArray_SimpleNew(1, dims, NPY_FLOAT64);
  PyArrayObject *partition = PyArray_SimpleNew(1, dims, NPY_FLOAT64);
  PyArrayObject *indices = PyArray_SimpleNew(1, dims, NPY_INT32);

  // データのセット
  double *arr_data = (double *)PyArray_DATA(arr);
  double *partition_data = (double *)PyArray_DATA(partition);
  for (int i = 0; i < 5; i++) {
    arr_data[i] = i + 0.5;
    partition_data[i] = 3.0;
  }

  // 配列の分割
  int status = PyArray_Partition(arr, partition, indices, NPY_CORDER, PARTITION_LT);
  if (status < 0) {
    PyErr_PrintEx(0);
    return 1;
  }

  // 分割結果の表示
  printf("分割された配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%f ", arr_data[i]);
  }
  printf("\n");

  printf("分割基準配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%f ", partition_data[i]);
  }
  printf("\n");

  printf("インデックス配列:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", indices[i]);
  }
  printf("\n");

  // メモリ解放
  Py_XDECREF(arr);
  Py_XDECREF(partition);
  Py_XDECREF(indices);

  return 0;
}


np.partition() 関数

np.partition() 関数は、PyArray_Partition() 関数とほぼ同じ機能を提供する NumPy の組み込み関数です。

  • PyArray_Partition() 関数は C 言語で記述されており、NumPy 配列オブジェクトだけでなく、C 言語のデータ構造も操作できます。
  • 主な違いは、np.partition() 関数は NumPy 配列オブジェクトのみを操作できる点です。

import numpy as np

arr = np.array([5, 2, 4, 1, 3])
partition = np.array([3])
indices = np.array([0])

new_arr, pivot, new_indices = np.partition(arr, partition, axis=0)

print("分割された配列:", new_arr)  # 出力: [1 2 3 4 5]
print("分割基準配列:", pivot)    # 出力: [3]
print("インデックス配列:", new_indices)  # 出力: [0 1 2 3 4]

カスタムソートアルゴリズム

より柔軟な制御が必要な場合は、カスタムのソートアルゴリズムを実装することができます。

  • カスタムアルゴリズムは、特定のニーズに合わせたカスタマイズが可能ですが、np.partition() 関数よりも複雑になる可能性があります。
  • 例えば、クイックソート以外にも、マージソートやヒープソートなどのアルゴリズムを使用できます。

import numpy as np

def custom_partition(arr, partition):
  pivot = partition[0]
  left = []
  right = []
  for element in arr:
    if element < pivot:
      left.append(element)
    else:
      right.append(element)
  return np.array(left + [pivot] + right), pivot

arr = np.array([5, 2, 4, 1, 3])
partition = np.array([3])

new_arr, pivot = custom_partition(arr, partition)

print("分割された配列:", new_arr)  # 出力: [1 2 3 4 5]
print("分割基準配列:", pivot)    # 出力: [3]

np.where() 関数と np.concatenate() 関数

シンプルな分割が必要な場合は、np.where() 関数と np.concatenate() 関数を使用して実現することができます。

  • np.concatenate() 関数は、複数の配列を連結します。
  • np.where() 関数は、条件に基づいて配列の要素を抽出します。

import numpy as np

arr = np.array([5, 2, 4, 1, 3])
partition = 3

less_than_partition = arr[arr < partition]
greater_than_partition = arr[arr >= partition]

new_arr = np.concatenate((less_than_partition, [partition], greater_than_partition))

print("分割された配列:", new_arr)  # 出力: [1 2 3 4 5]

PyArray_Partition() 関数は、NumPy 配列を分割する強力なツールですが、状況によっては代替方法の方が適切な場合があります。

  • NumPy 配列オブジェクトのみを操作する場合は、np.partition() 関数を使用します。
  • シンプルな分割が必要な場合は、np.where() 関数と np.concatenate() 関数を使用します。
  • 柔軟な制御が必要な場合は、カスタムのソートアルゴリズムを使用します。