5
votes

ValueError: lors du passage à un dtype plus grand, sa taille doit être un diviseur de la taille totale en octets du dernier axe du tableau

Je rencontre l'exception suivante lorsque je travaille sur un ensemble de données numpy fourni par un tiers:

def f(X):
    X_rows = X.view([('', X.dtype)] * X.shape[1])

def g(X):
    f(X)

def h(X):
    f(X)

# call the functions
g(X) # this runs without a problem
f(X) # this returns the error

Dans quelles circonstances numpy augmentera-t-il ce problème? Mon code applique une vue sur le tableau numpy, où j'essaie d'appliquer un dtype structuré qui correspond au nombre d'éléments dans une ligne.

Je vois ceci erreur lorsque l'instruction X.view ([('', X.dtype)] * X.shape [1]) est appelée dans une fonction f - mais pas dans chaque appel à cette fonction f:

from sklearn.datasets import load_iris
X = load_iris().data

X est toujours un tableau à deux axes ( len ( X.shape) est toujours 2), donc vous vous attendriez à un dtype structuré qui est long de X.shape [1] pour s'adapter au dernier axe ( X. autres? Je ne peux même pas voir quel code source .py numpy génère cette erreur.

J'ai du mal à produire un MCVE pour cela, mais je l'ai réduit à un cahier colab qui est encore un peu volumineux à publier ici.

Ici, X est censé être un sous-ensemble du iris ensemble de données, que j'ai obtenu de scikit learn.

ipdb> X.view([('', X.dtype)] * X.shape[1])
*** ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array.

Mon code ressemble à ceci:

ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array


13 commentaires

Quelle est la forme et le type de X?


@hpaulj C'est l'ensemble de données d'iris de scikitlearn comme indiqué dans le code. Je n'ai pas accès à mon ordinateur mais je pense qu'ils sont np. ndarray et [150,4] .


@ J.Doe: ndarray est le type de tableau, pas le dtype des valeurs.


ressemble à ce code tente de changer un tableau 2d en un tableau structuré 1d, en remplaçant, par exemple, un tableau à 4 colonnes int64 par un tableau structuré à 4 champs. np.unique le fait, mais avec une syntaxe légèrement différente (il nomme les champs)


@hpaulj J'essaie d'obtenir la différence définie entre deux tableaux numpy en utilisant cette réponse qui utilisait np.setdiff1d .


La question est, il existe un dtype et / ou une shape qui reproduit ce problème. Une question plus fondamentale, est-ce que le dtype est simple, comme int64 , ou est-il déjà composé?


Lorsque cette réponse liée a été écrite, np.unique ne fonctionnait qu'avec des tableaux 1d. Depuis lors, il a ajouté le paramètre axis, mais l'action sous-jacente consiste d'abord à transformer le tableau en un 1d en utilisant ce type de dtype structuré.


Chargement comme dans scikit-learn.org/stable/auto_examples/datasets/…, dtype est float64 . X_rows est (150,1) avec dtype ([('f0', '


@hpaulj Je suis désolé pour mon commentaire sur X . En fait, il s'agit d'un sous-ensemble de l'ensemble de données d'iris ( (6, 4) , ('float64') ). Je ne pouvais pas penser à réduire le problème; veuillez exécuter ce bloc-notes Google Colab pour reproduire ce problème


Le tableau ne provient donc pas directement de l'ensemble de données, mais fait plutôt partie de l'opération pandas dataframe apply ? Je ne vais pas essayer de deviner ce que les pandas ont fait avec la forme et le type à ce stade. exemple reproductible minimal


@MartijnPieters Merci pour l'expérience accueillante de SO! Dans un univers parallèle, cette question aurait été close au motif de ne pas fournir de MCVE - que je n'arrive toujours pas à penser comment produire. Je pense que ce fil de discussion servirait beaucoup mieux à l'avenir si un MCVE est fourni, veuillez donc modifier complètement la question si vous pensez qu'une telle formulation est possible. Notez également que Google n'a pas eu de résultats de recherche sur SO pour cette erreur également.


@hpaulj merci beaucoup pour les efforts et bien sûr pour accepter cette question bien que ce ne soit pas vraiment une question de type SO. En ce qui concerne le MCVE, veuillez consulter ma suggestion à Martijn ci-dessus - n'hésitez pas à modifier complètement la question.


@ J.Doe: Il s'agit davantage d'une question «sous quelles conditions soulèvera cette exception *», qui pour ce cas précis est une question à portée raisonnable. Le fait qu'il ait une base dans un cas de code réel difficile à réduire n'en fait pas nécessairement une question de débogage. Je vais éditer l'article pour le recentrer un peu.


3 Réponses :


2
votes

Vous essayez de créer une vue sur un tableau avec une disposition de mémoire incompatible, où la sortie dtype a un itemsize qui ne correspond pas parfaitement au nombre d'octets nécessaires en mémoire pour couvrir toute la longueur du 'dernier' axe de le tableau source. L'exception s'appliquerait également si vous définissiez simplement l'attribut .dtype sur le tableau directement , pas seulement sur ndarray.view () (qui crée un nouveau ndarray avec dtype défini sur ce nouvel objet).

Le «dernier» axe ici est la dimension «la plus interne» en termes de mémoire mise en page ; pour les tableaux d'ordre C qui sont shape [-1] , pour les tableaux d'ordre Fortran qui sont shape [0] . Cette taille de dimension multipliée par le dtype.itemsize d'origine doit être divisible par le nouveau dtype.itemsize , sinon vous ne pouvez pas `` parcourir '' proprement la structure de la mémoire interne.

Par exemple, pour un tableau d'ordre C (ordre principal de la ligne) avec une forme (4, 3, 5) et un dtype.itemsize de 8, le «dernier» axe occupe 5 * 8 == 40 octets de mémoire, et vous pouvez donc créer une vue sur celui-ci avec des dtypes plus grands de tailles 10, 20 et 40. Le même tableau mais en Fortran em> order (ordre majeur de la colonne), cependant, utilise 4 * 8 == 32 octets de mémoire, limitant vos options à des dtypes plus grands de tailles 16 et 32 ​​uniquement.

Si X.view ([('', X.dtype)] * X.shape [1]) échoue, alors soit X.shape a plus de dimensions que juste 2, ou em> c'est un tableau utilisant l'ordre Fortran. Vous pouvez corriger le premier en utilisant X.shape [-1] et vous pouvez vérifier le lattr en regardant ndarray.flags ['F_CONTIGUOUS'] . Les combiner en une seule expression comme celle-ci devrait fonctionner:

if X.flags['F_CONTIGUOUS']:
    X_rows = X.T.view([('', X.dtype)] * X.shape[0]).T

Cependant , comme ndarray.view () documentation avertit:

Les vues qui modifient la taille de dtype (octets par entrée) doivent normalement être évitées sur les tableaux définis par des tranches, des transpositions, un ordre de transposition, etc. [.]

Lorsque vous essayez de changer le type d'un tableau d'ordre Fortran, un avertissement est émis:

DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead

donc il vaudrait mieux transposer le tableau, créez votre vue, puis transposez à nouveau la vue résultante:

X_rows = X.view([('', X.dtype)] * X.shape[0 if X.flags['F_CONTIGUOUS'] else -1])

Vous devez toujours vous en tenir à X.shape [0] ici, c'est shape [-1] du tableau transposé.

Le fait que la prise en charge de la modification du dtype sur les tableaux d'ordre Fortran soit obsolète peut également expliquer le la référence de l'exception au «dernier axe», ce qui est parfaitement naturel en termes de tableaux d'ordre C mais semble contre-intuitif lorsqu'il est appliqué à des tableaux d'ordre Fortran.

Je ne vois même pas quel code source .py numpy génère cette erreur.

Numpy est principalement écrit en C (avec un tiret de Fortran 77), et vous devez donc fouiller dans le code source des composants compilés. L'erreur est renvoyée dans le dtype fonction de définition de descripteur , qui est appelée ici lorsque La fonction PyArray_View () appelle la fonction PyObject_SetAttrString () pour définir le l'attribut dtype lorsqu'il est appelé depuis ndarray.view () method .

Selon le code source, non seulement la modification du dtype des tableaux d'ordre Fortran est obsolète , mais les vues sur des tableaux non contigus ne sont pas du tout prises en charge (ce qui signifie que si les deux X.flags ['C_CO NTIGUOUS '] et X.flags [' F_CONTIGUOUS '] sont False alors vous ne pouvez pas du tout changer le dtype ) .



1
votes

Donc, en essayant de reproduire votre situation:

In [153]: a = np.array([[4.7, 3.2, 1.3, 0.2],[4.6, 3.1, 1.5, 0.2],[4.6, 3.4, 1.4
     ...: , 0.3],[4.4, 3. , 1.3, 0.2],[4.4, 3.2, 1.3, 0.2],[4.6, 3.2, 1.4, 0.2]]
     ...: , dtype='float64', order='F')                                         

In [154]: a.view([('', a.dtype)] * a.shape[1])                                  
/usr/local/bin/ipython3:1: DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead
  #!/usr/bin/python3
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-154-b804730eb70b> in <module>
----> 1 a.view([('', a.dtype)] * a.shape[1])

ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array.

Pour l'instant ça a l'air bien.


J'étais sur le point de donner, car je ne voulais pas déboguer carnet. Mais j'ai peut-être trouvé une cause possible.

Au début de votre course, il y a un avertissement:

In [148]: X1 = X.copy(order='F')                                                
In [149]: X_rows = X1[:0].view([('',X1.dtype)] * X1.shape[1])                   
In [150]: X_rows                                                                
Out[150]: 
array([], shape=(0, 1),
      dtype=[('f0', '<f8'), ('f1', '<f8'), ('f2', '<f8'), ('f3', '<f8')])
In [151]: X_rows = X1[:6].view([('',X1.dtype)] * X1.shape[1])                   
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-151-f3272035dc14> in <module>
----> 1 X_rows = X1[:6].view([('',X1.dtype)] * X1.shape[1])

ValueError: To change to a dtype of a different size, the array must be C-contiguous

Vous exécutez cette fonction avec un pandas apply , que je n'ai pas beaucoup utilisé. Mais je suis conscient que les pandas préfèrent un ordre «F», car il est orienté série. Alors que se passe-t-il si je passe X à cette commande?

/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:14: DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead

OK, ce n'est pas la même erreur, mais cela montre que l'ordre peut affecter cela type de view.


Mais prenons le tableau de votre commentaire - et donnons-lui un ordre F :

In [129]: from sklearn import datasets                                          
In [131]: iris = datasets.load_iris()                                           

In [132]: X = iris.data                                                         
In [133]: X.shape                                                               
Out[133]: (150, 4)
In [134]: X.dtype                                                               
Out[134]: dtype('float64')

In [135]: X_rows = X.view([('',X.dtype)] * X.shape[1])                          
In [136]: X_rows.shape                                                          
Out[136]: (150, 1)
In [137]: X_rows.dtype                                                          
Out[137]: dtype([('f0', '<f8'), ('f1', '<f8'), ('f2', '<f8'), ('f3', '<f8')])

Ça y est - l'avertissement et l'erreur comme indiqué dans le notebook.


1 commentaires

Merci @hpaulj, c'est une découverte intéressante. Une raison pour laquelle vous pouvez penser à pourquoi les pandas font cela?



0
votes

Pas une réponse mais juste un commentaire sur la réponse @martijn. Pour un grand ensemble de données, j'obtenais cette erreur dans une opération similaire de conversion en vue. Il semble que chaque fois que numpy décide de créer un tableau d'ordre F, l'erreur pos up. L'ensemble de données avec lequel je travaille est assez uniforme, je ne sais pas pourquoi numpy décide parfois de créer un ordre F et un tableau d'ordre C autre. J'espère que quelqu'un pourra vous expliquer.
Quoi qu'il en soit, ma solution consistait à convertir la commande F en commande C.

    if arr2.flags['F_CONTIGUOUS']:  # ‘C_CONTIGUOUS’ (‘C’) - ensure a C-contiguous array
        arr2 = np.require(arr1, requirements= ["C"])
        logging.critical("Switching from Fortran to Cstyle ")


0 commentaires