[PYTHON] Je voulais aussi vérifier les indices de type avec numpy

Aperçu

Indices de type ajoutés dans PEP484 depuis Python 3.5. Je me suis demandé s'il pouvait être appliqué au ndarray de numpy, il s'agit donc d'un outil de vérification statique de type hint mypy ) Et les résultats des enquêtes ww telles que le traitement des modules tiers sont résumés.

Conclusion

En conclusion, la vérification des indices de type de numpy.ndarray à l'aide de mypy est possible en utilisant numpy-stubs. Cependant, à partir de maintenant (janvier 2020), mypy check en spécifiant dtype et shape of ndarray n'est pas possible. D'un autre côté, si vous voulez ajouter des indices de type incluant dtype et shape comme annotations pour la lisibilité, l'option d'utiliser nptyping semble être bonne.

Préparation

Au minimum, installez ce qui suit avec pip. (L'environnement entre parenthèses est l'environnement au moment de ma vérification)

Vérification de type par mypy

Tout d'abord, le contrôle de type était dans un état incertain, alors j'ai expérimenté.

# ex1.py

from typing import List, Tuple

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

points_invalid = [[1, 1], [4, 2], [3, 6], [-1, 3]]

print(calc_center(points_invalid))  # TypeHint Error

Le code ci-dessus, bien sûr, se termine normalement, mais je vais vérifier l'indice de type avec mypy. Si vous avez installé mypy avec pip etc., vous devriez pouvoir exécuter mypy dans le terminal.

>mypy ex1.py
ex1.py:16: error: Argument 1 to "calc_center" has incompatible type "List[List[int]]"; expected "List[Tuple[int, int]]"
Found 1 error in 1 file (checked 1 source file)
>python ex1.py
(1.75, 3.0)

De cette façon, il indique où l'indication de type enfreint. Si vous le modifiez comme suit, le contrôle par mypy passera.

# ex2.py

from typing import List, Tuple

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

points = [(1, 1), (4, 2), (3, 6), (-1, 3)]

print(calc_center(points))  # Success
>mypy ex2.py
Success: no issues found in 1 source file

Vérification de type pour les modules tiers

Puisque le ndarray de numpy est pratique pour calculer les points de coordonnées, j'aimerais le changer en conséquence. Cependant, lorsque j'ajoute ʻimport numpy` au code précédent et que j'exécute mypy, l'erreur suivante se produit.

# ex3.py

from typing import List, Tuple
import numpy as np

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

points = [(1, 1), (4, 2), (3, 6), (-1, 3)]

print(calc_center(points))  # Success
>mypy ex3.py
ex3.py:4: error: No library stub file for module 'numpy'
ex3.py:4: note: (Stub files are from https://github.com/python/typeshed)
Found 1 error in 1 file (checked 1 source file)

La cause de l'erreur est que le package numpy lui-même ne prend pas en charge les indices de type. Ensuite, divisons les contre-mesures dans les trois cas suivants.

Méthode 1. Ignorer les indications de type tiers

Cela semble être le plus simple et le plus courant. La méthode consiste à créer un fichier avec le nom mypy.ini, à écrire comme suit, puis à le placer dans le répertoire en cours.

[mypy]

[mypy-numpy]
ignore_missing_imports = True

Les 3e et 4e lignes sont définies pour ignorer l'erreur de vérification de l'indice de type pour numpy. Si vous souhaitez l'appliquer à d'autres modules tiers, copiez les 3ème et 4ème lignes et modifiez la partie numpy. Pour les autres spécifications relatives à mypy.ini, veuillez vous référer à la page officielle ici.

Vous pouvez maintenant exécuter mypy check normalement. Cependant, notez que la vérification d'indication de type de ndarray lui-même est également ignorée (dernière ligne).

# ex4.py (ignore_missing_imports)

from typing import List, Tuple
import numpy as np

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

def calc_center_np(points: np.ndarray) -> np.ndarray:
    '''Trouvez le centre de gravité dans la liste des points(version ndarray)'''
    return np.average(points, axis=0)

points = [(1, 1), (4, 2), (3, 6), (-1, 3)]

print(calc_center(points))  # Success

np_points = np.array(points, dtype=np.int)

print(calc_center_np(np_points))  # Success
print(calc_center_np(points))  # Success ?
>mypy ex4.py
Success: no issues found in 1 source file

Méthode 2. Créez un stub pour les indications de type

Créez une fonction vide (stub) pour l'indice de type du module que vous souhaitez utiliser et mypy les examinera à la place. Les fichiers stub sont gérés avec l'extension .pyi.

Un stub pour numpy numpy-stubs est disponible sur github.

Tout d'abord, apportez le dossier "numpy-stubs" avec git clone https: // github.com / numpy / numpy-stubs.git etc. Remplacez le dossier "numpy-stubs" par "numpy".

La structure des dossiers est la suivante.

numpy-stubs/
└── numpy
    ├── __init__.pyi
    └── core
        ├── numeric.pyi
        ├── numerictypes.pyi
        ├── _internal.pyi
        └── __init__.pyi

De plus, ajoutez le chemin du dossier racine où le stub est placé à la variable d'environnement MYPYPATH et exécutez-le.

# ex5.py (numpy-stubs)

from typing import List, Tuple
import numpy as np

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

def calc_center_np(points: np.ndarray) -> np.ndarray:
    '''Trouvez le centre de gravité dans la liste des points(version ndarray)'''
    return np.average(points, axis=0)

points = [(1, 1), (4, 2), (3, 6), (-1, 3)]

print(calc_center(points))  # Success

np_points = np.array(points, dtype=np.int)
np_points_float = np.array(points, dtype=np.float)

print(calc_center_np(np_points))  # Success
print(calc_center_np(np_points_float))  # Success
print(calc_center_np(points))  # TypeHint Error
>set "MYPYPATH=numpy-stubs"
>mypy ex5.py
ex5.py:28: error: Argument 1 to "calc_center_np" has incompatible type "List[Tuple[int, int]]"; expected "ndarray"
Found 1 error in 1 file (checked 1 source file)

Maintenant, la vérification des indices de type de ndarray fonctionne. Cependant, il n'est pas possible de vérifier après avoir spécifié dtype et shape, et c'est un léger goulot d'étranglement que les variables d'environnement doivent être définies une par une.

stubgen

mypy est livré avec un script appelé stubgen qui génère automatiquement un fichier pour les indices de type (extension .pyi). ..

>stubgen -p numpy

-p est une option pour générer récursivement des stubs pour les packages. Lorsqu'il est exécuté, un dossier ʻout` est créé dans le répertoire courant et le fichier stub numpy y est compressé.

Cependant, j'obtiens une autre erreur lorsque j'exécute mypy check, probablement parce que stubgen n'est pas capable d'extraire correctement la structure de numpy. Il y a des cas où les stubs sont ouverts au public comme numpy-stubs, il est donc plus sûr de les utiliser si possible.

Méthode 3. Utilisez également nptyping

Si vous souhaitez créer un indice de type comprenant dtype et la forme de ndarray après avoir utilisé la méthode 1 ou la méthode 2, [nptyping](numpy-type-hints-in-python-pep- 484) doit être utilisé.

Il peut être installé depuis PyPi avec pip install nptyping.

Bien que nptyping ne supporte pas la vérification des indices de type statique par mypy, les indices de type qui spécifient dtype et la forme de ndarray peuvent être spécifiés en utilisant l'alias ʻArray`.

Voici un échantillon officiel. Les tableaux avec des types mixtes tels que DataFrame de pandas sont également OK.

from nptyping import Array
Array[str, 3, 2]    # 3 rows and 2 columns
Array[str, 3]       # 3 rows and an undefined number of columns
Array[str, 3, ...]  # 3 rows and an undefined number of columns
Array[str, ..., 2]  # an undefined number of rows and 2 columns
Array[int, float, str]       # int, float and str on columns 1, 2 and 3 resp.
Array[int, float, str, ...]  # int, float and str on columns 1, 2 and 3 resp.
Array[int, float, str, 3]    # int, float and str on columns 1, 2 and 3 resp. and with 3 rows

La vérification d'instance à l'aide de «cette instance» est également possible.

# ex6.py (nptyping)

from typing import List, Tuple
import numpy as np
from nptyping import Array

def calc_center(points: List[Tuple[int, int]]) -> Tuple[float, float]:
    '''Trouvez le centre de gravité dans la liste des points'''
    n = len(points)
    x, y = 0, 0
    for p in points:
        x += p[0]
        y += p[1]
    return x/n, y/n

def calc_center_np(points: Array[int, ..., 2]) -> Array[float, 2]:
    '''Trouvez le centre de gravité dans la liste des points(version ndarray)'''
    print(isinstance(points, Array[int, ..., 2]))
    return np.average(points, axis=0)

points = [(1, 1), (4, 2), (3, 6), (-1, 3)]

np_points = np.array(points, dtype=np.int)
np_points_float = np.array(points, dtype=np.float)

print(isinstance(calc_center_np(np_points), Array[float, 2]))  #argument: True,Valeur de retour: True
print(isinstance(calc_center_np(np_points_float), Array[float, 2]))  #argument: False,Valeur de retour: True
print(isinstance(calc_center_np(points), Array[float, 2]))  #argument: False,Valeur de retour: True

N'oubliez pas de définir nptyping sur ʻignore_missing_imports = True` dans mypy.ini. Le résultat de l'exécution est le suivant.

>mypy ex6.py
Success: no issues found in 1 source file
>python ex6.py
True
True
False
True
False
True

Résumé

J'ai résumé les indices de type autour de numpy. Je pense qu'il est courant de traiter les informations telles que les coordonnées et les données de table comme un ndarray et de mettre en œuvre des opérations géométriques et statistiques. À ce moment-là, je m'inquiète souvent du code que j'ai écrit, comme "Combien de dimensions de ndarray pétrissez-vous?" J'ai trouvé des conseils de type comme nptyping utiles en termes de lisibilité et de maintenabilité. Je pense que ce sera plus utile s'il peut prendre en charge la vérification de type par mypy à l'avenir.

Article de référence

https://stackoverflow.com/questions/52839427/ https://www.sambaiz.net/article/188/ https://masahito.hatenablog.com/entry/2017/01/08/113343

Recommended Posts

Je voulais aussi vérifier les indices de type avec numpy
Je voulais résoudre ABC160 avec Python
Je voulais résoudre ABC172 avec Python
Je voulais vraiment copier avec du sélénium
Je voulais résoudre NOMURA Contest 2020 avec Python
Je voulais jouer avec la courbe de Bézier
Je voulais installer Python 3.4.3 avec Homebrew + pyenv
Je veux écrire un élément dans un fichier avec numpy et le vérifier.
J'ai écrit GP avec numpy
Je voulais résoudre le concours de programmation Panasonic 2020 avec Python
J'ai essayé de mettre en œuvre une évasion (type d'évitement de tromperie) avec Quantx
Je veux changer le drapeau japonais en drapeau des Palaos avec Numpy
J'ai capturé le projet Toho avec Deep Learning ... je le voulais.
Je voulais calculer un tableau avec la méthode des subs de Sympy
Je voulais supprimer plusieurs objets en s3 avec boto3
Entraine toi! !! Introduction au type Python (conseils de type)
Je voulais créer une présentation intelligente avec Jupyter Notebook + nb present
Chaîne de hachage que je voulais éviter (2)
Je voulais faire évoluer cGAN vers ACGAN
Augmentez la visibilité de la source avec des conseils de type
Je voulais résoudre le problème ABC164 A ~ D avec Python
Je veux faire ○○ avec les Pandas
Je veux vérifier la position de mon visage avec OpenCV!
Je veux déboguer avec Python
Chaîne de hachage que je voulais éviter (1)
C'est pourquoi j'ai quitté pandas [Trois façons de groupby.mean () avec juste NumPy]
J'ai essayé de mettre en œuvre un apprentissage en profondeur qui n'est pas profond avec uniquement NumPy
Je voulais utiliser le notebook jupyter avec docker dans l'environnement pip (opticspy)
Utilisez Python de Java avec Jython. J'étais aussi accro.
C'est plus récent, mais je voulais essayer le calcul de l'IMC avec python.
J'ai commencé l'apprentissage automatique avec Python (j'ai également commencé à publier sur Qiita) Préparation des données
Je veux détecter des objets avec OpenCV
[Python] Vérification simple du type d'argument avec la classe de données
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé de visualiser AutoEncoder avec TensorFlow
J'ai essayé de commencer avec Hy
Je veux écrire un blog avec Jupyter Notebook
Je veux installer Python avec PythonAnywhere
Je veux analyser les journaux avec Python
Je veux jouer avec aws avec python
[Introduction à Pytorch] J'ai joué avec sinGAN ♬
Je voulais résoudre ABC159 avec Python
J'ai essayé d'implémenter CVAE avec PyTorch
J'ai fait un jeu de vie avec Numpy
J'ai essayé de résoudre TSP avec QAOA
Implémentation de DQN avec TensorFlow (je voulais ...)
Je voulais visualiser la simulation de particules 3D avec la bibliothèque de visualisation Python Matplotlib.
Je veux utiliser mkl avec numpy et scipy sous l'environnement pyenv + poetry
J'ai essayé de créer un environnement à vérifier régulièrement en utilisant Selenium avec AWS Fargate
Vous voulez ajouter des indices de type aux décorateurs Python?
J'ai essayé d'implémenter la lecture de Dataset avec PyTorch
J'ai essayé d'utiliser lightGBM, xg boost avec Boruta
Je veux utiliser MATLAB feval avec python
Convertissez les données avec la forme (nombre de données, 1) en (nombre de données,) avec numpy.
J'ai essayé d'apprendre le fonctionnement logique avec TF Learn
J'ai essayé de déplacer GAN (mnist) avec keras
Scraping de pages i-Town: je voulais prendre la place de Wise-kun
Ajouter des lignes à un tableau vide avec numpy