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
3 Réponses :
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 ) .
Merci pour l'explication, @Martijn Pieters. Cependant, je ne peux pas penser à relier cela à mon problème. Si vous % debug après avoir exécuté le bloc-notes Google Colab a > J'ai uploadé pour reproduire ce problème, vous pouvez voir que X est de float64 dtype et il n'y a pas de conversion dtype,
@ J.Doe: hors du temps maintenant, mais la forme du tableau est également importante, .shape [0] vaut 6, et 6 fois 8 octets ne rentrent pas dans 32 octets par élément dans la vue.
IIUC, n'est-ce pas .shape [1] (4) la longueur qui devrait être intéressante? Aussi, pourquoi ne puis-je pas reproduire cela avec 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') et a.view ( [('', a.dtype)] * a.shape [1]) ?
J'ai reproduit votre erreur - en attribuant order = 'F' à votre a .
@ J.Doe: de retour maintenant, et je le vois maintenant. Le tableau avec lequel vous travaillez est une vue, avec une vue strided à travers le tableau. Je trouverai les références doc et j'écrirai ce qui se passe.
@ J.Doe: Je pense que je l'ai entièrement couvert maintenant, et vérifié par rapport à votre ordinateur portable. Vous avez là un tableau contigu d'ordre Fortran, donc c'est bien shape [0] qui compte ici, pas shape [-1] .
Merci beaucoup @MartijnPieters Ce n'est pas quelque chose qu'un programmeur python profane comme moi résoudrait!
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.
Merci @hpaulj, c'est une découverte intéressante. Une raison pour laquelle vous pouvez penser à pourquoi les pandas font cela?
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 ")
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. ndarrayet[150,4].@ J.Doe:
ndarrayest le type de tableau, pas ledtypedes valeurs.ressemble à ce code tente de changer un tableau 2d en un tableau structuré 1d, en remplaçant, par exemple, un tableau à 4 colonnes
int64par un tableau structuré à 4 champs.np.uniquele 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
dtypeet / ou uneshapequi reproduit ce problème. Une question plus fondamentale, est-ce que ledtypeest simple, commeint64, ou est-il déjà composé?Lorsque cette réponse liée a été écrite,
np.uniquene 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/… a>,
dtypeestfloat64.X_rowsest (150,1) avecdtype ([('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èmeLe tableau ne provient donc pas directement de l'ensemble de données, mais fait plutôt partie de l'opération
pandasdataframeapply? 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.