Нарязването на масив Numpy транспонира вътрешни данни

Пиша C разширение, което ще се занимава с масиви numpy. Написах функция за четене и извеждане на масива numpy. Използвайки го, забелязах странно поведение, което се появява, когато използвам нарязване във входния масив.

Функцията C за четене на (булан) масив:

char **pymatrix_to_CarrayptrsChar(PyArrayObject *arrayin) {
    char **result, *array;
    int i, n, m, j;

    n = arrayin->dimensions[0];
    m = arrayin->dimensions[1];
    result = ptrvectorChar(n, m);

    array = (char *) arrayin->data; /* pointer to arrayin data as int */
    for (i = 0; i < n; i++) {
      result[i] = &array[i * m];
    }
    printArrChar(result, n, m);
    return result;
}

ptrvectorChar е функция за разпределение на паметта:

char **ptrvectorChar(long dim1) {
    char **v;
    if (!(v = malloc(dim1 * sizeof(char*)))) {
        PyErr_SetString(PyExc_MemoryError,
              "In **ptrvectorChar. Allocation of memory for character array failed.");
        exit(0);
    }
    return v;
}

И печатът се извършва с:

void printArrChar(char **arr, int dim1, int dim2) {
    int i, j;
    for (i = 0; i < dim1; i++) {
        for (j = 0; j < dim2; j++) {
            printf("%i ", arr[i][j]);
        }
        printf("\n");
    }
}

Моят python скрипт за възпроизвеждане на грешката е:

import numpy as np
import MyExtension
np.random.seed(1)

x = np.array((1,1,1,1,1,1)).astype(bool)
a = np.round(np.random.rand(trialNr, lakeNr)).astype(bool)
aSlicing = a[:, x]

print("a:")
print(a + 0)

print("aSlicing:")
print(aSlicing + 0)

print("C output for a:")
MyExtension.MyFunction(a)

print("C output for aSlicing:")
MyExtension.MyFunction(aSlicing)

Изходът е:

a:
[[0 1 0 0 0 0]
 [0 0 0 1 0 1]
 [0 1 0 1 0 1]
 [0 0 1 1 0 1]
 [1 1 0 0 0 1]
 [0 0 1 1 1 0]
 [1 1 0 1 1 1]
 [0 1 0 0 1 0]
 [0 0 0 1 0 0]
 [0 0 1 0 1 1]]

aSlicing:
[[0 1 0 0 0 0]
 [0 0 0 1 0 1]
 [0 1 0 1 0 1]
 [0 0 1 1 0 1]
 [1 1 0 0 0 1]
 [0 0 1 1 1 0]
 [1 1 0 1 1 1]
 [0 1 0 0 1 0]
 [0 0 0 1 0 0]
 [0 0 1 0 1 1]]

C output for a:
0 1 0 0 0 0 
0 0 0 1 0 1 
0 1 0 1 0 1 
0 0 1 1 0 1 
1 1 0 0 0 1 
0 0 1 1 1 0 
1 1 0 1 1 1 
0 1 0 0 1 0 
0 0 0 1 0 0 
0 0 1 0 1 1 

C output for aSlicing:
0 0 0 0 1 0 
1 0 0 0 1 0 
1 0 1 0 1 1 
0 0 0 0 0 1 
0 1 0 0 0 1 
0 1 1 1 0 1 
1 0 1 0 0 0 
0 0 0 1 1 1 
0 1 0 1 1 1 
1 0 1 0 0 1 

Както може да се види лесно, a и aSlicing са едни и същи масиви за python. Въпреки това функцията C, която чете данните в, вижда данните като един вид транспонирани. C разглежда aSliced ​​като че ли е

a.T.reshape((10,6))

Някой знае ли защо се появява тази грешка и как правилно да я заобиколя? Разбира се, транспонирането в C кода е лесно. Искам обаче моята програма да може да работи и с двата вида масиви.

Бих предпочел решение в рамките на моето C разширение, т.е. потребителите на моето разширение няма да се интересуват дали техният вход е „нарязан“ или не. Въпреки това се опитах да поставя дълбоко копие на aSliced в моето разширение - което имаше същия грешен резултат като aSliced.

Работя с python 3.4 64bit, numpy 1.9.1, Win8 64bit и Visual Studio 10 64bit C компилатор.


person Samufi    schedule 29.05.2015    source източник
comment
погледнете .flags или .__array_interface__. Булевото индексиране създава копие и в този случай то е F_CONTIGUOUS.   -  person hpaulj    schedule 29.05.2015
comment
Благодаря за коментара, @hpaulj! Би ли било най-добре да прочетете този флаг и в зависимост от резултата да използвате различен начин за четене на паметта на масива? Или има друг, по-елегантен начин? Търсих известно време, но не го намерих никъде: Как мога да получа стойността на флаг от C?   -  person Samufi    schedule 29.05.2015
comment
Документи за флаговете на масива: docs.scipy. org/doc/numpy/reference/c-api.array.html#array-flags   -  person hpaulj    schedule 30.05.2015


Отговори (1)


Както посочи hpaulj, възможно е да се разбере структурата на паметта с помощта на флага F_CONTIGUOUS. Прекарах много време в опити да намеря начин да прочета този флаг от C. Доколкото разбрах, това може да се направи с оценка на стойността на trials_array->flags % 2. Въпреки това не намерих препратка с ясно обяснение за този проблем.

trials_array->flags е цяло число. Константите на флага numpy NPY_C_CONTIGUOUS, NPY_F_CONTIGUOUS и т.н. са цели числа, които са степени на две. Изглежда вярно, че флагът е зададен, ако съответната позиция в двоичното представяне на trials_array->flags е 1.

Дори и да познавам структурата на паметта, не е толкова тривиално, колкото си мислех, да прочета масива. Намерих много по-лесен начин за конвертиране на numpy масиви в C масиви: Използвайте

char *myArray;
PyArrayObject *myArray_Numpy;

PyArray_AsCArray(&myArray_Numpy, (void *) &myArray, myArray_Numpy->dimensions, 2, PyArray_DescrFromType(NPY_BOOL));

//Do something with the array

PyArray_Free(myArray_Numpy, myArray);

Намерих пример за използване на тези функции тук.

person Samufi    schedule 30.05.2015