[PYTHON] Toutes les méthodes destructrices que les data scientists devraient connaître

J'étais en train de réviser le code du data scientist de mon collègue et j'ai dit: "C'est dangereux de faire des méthodes destructrices sur des arguments de fonction", mais je ne pouvais pas l'expliquer correctement faute de vocabulaire, j'ai donc écrit un article. Je l'ai fait.

Certes, il n'y a pas de traitement destructeur en langage R (n'est-ce pas?), Donc je sens que je ne peux pas apprendre simplement en utilisant numpy ou pandas avec ce sentiment. J'ai cherché de la bonne documentation et des articles, mais je n'ai trouvé que Ruby, ce qui semblait difficile à expliquer, alors je l'ai écrit.

Qu'est-ce qu'une méthode destructive?

Voici un exemple du processus destructif / non destructif de "Ajout d'une liste à la fin d'une liste".

#Exemple destructeur
x = [1, 2, 3]
x.extend([4, 5, 6])
print(x)
# => [1, 2, 3, 4, 5, 6]
#Exemple non destructif
x = [1, 2, 3]
y = x + [4, 5, 6]
print(y)
# => [1, 2, 3, 4, 5, 6]

print(x)
# => [1, 2, 3]

En Python, «list.sort ()» et «list.reverse ()» sont fournis sous forme de méthodes de liste, et «trié» et «inversé» sont fournis en tant que traitement non destructif sous forme d'opérations de «tri de liste». Ça a été. Voir ce document officiel pour plus d'informations.

Aussi, pour des raisons de commodité, le terme «méthode destructive» est utilisé, mais il peut s'agir d'une fonction, bien que cela soit rare dans la fonction Python standard. Par exemple, random.shuffle qui trie la liste de manière aléatoire.

Points à surveiller lors de l'utilisation de méthodes destructrices

L'inconvénient est que si vous effectuez une opération destructive sur l'argument d'une fonction, l'argument lui-même sera modifié et le code sera difficile à suivre.

Considérez la "fonction qui traite les données pour assembler json".

import json
from typing import List

def convert_to_json(values: List[int]) -> str:
    """Fonction pour enregistrer le résultat au format json
    """
    #En cours de traitement des données`append`Effectuera un traitement destructif
    new_value = 3
    values.append(new_value)
    result = {"values": values, "size": len(values)}
    return json.dumps(result)

x = [1, 2, 3]
log = convert_to_json(x)

#Le résultat du calcul a été obtenu!
print(log)
# => {"values": [1, 2, 3, 3], "size": 4}

# convert_to_x lui-même sera changé dans json!
print(x)
# => [1, 2, 3, 3]

Avez-vous vu que la variable «x» a été mise à jour? Si vous faites cela involontairement, vous finirez par pleurer et imprimer sans cesse le débogage après avoir remarqué "Quoi? Une valeur a été ajoutée à la liste à ** quelque part ** dans le programme?"

Afin de ne pas le faire involontairement, vous pouvez effectuer un traitement non destructif comme suit.

def convert_to_json(values: List[int]) -> str:
    # Note:Contre la liste`values += [3]`Si vous le faites, ce sera une opération destructrice, alors soyez prudent!
    # https://stackoverflow.com/questions/2347265/why-does-behave-unexpectedly-on-lists
    #C'est un premier meurtre, mais il semble qu'il soit mis en œuvre en mettant l'accent sur l'efficacité de l'exécution
    values = values + [3]
    result = {"values": values, "size": len(values)}
    return json.dumps(result)

Vous pouvez également le copier au début de la fonction comme indiqué ci-dessous. Il y a quelques commentaires inquiétants ici et là, mais je vous le dirai quand je serai bloqué car il n'y a pas de netteté w

def convert_to_json(values: List[int]) -> str:
    # Note: `copy`Notez que la méthode ne copie pas jusqu'à la hiérarchie imbriquée des listes et des dictionnaires!
    # https://qiita.com/ninomiyt/items/2923aa3ac9bc06e6a9db
    #Pour plus de détails, copie complète/Vérifier avec copie superficielle
    copied = valus.copy()
    copied.append(3)
    result = {"values": values, "size": len(values)}
    return json.dumps(result)

De plus, si vous n'utilisez pas x après cela, vous n'aurez peut-être pas à vous inquiéter trop. Cependant, sachez que cela causera le même problème lors de la réutilisation de la fonction ailleurs. Il en va de même pour le traitement destructif des variables globales (ou des variables en dehors de la fonction).

Quand faut-il utiliser des méthodes destructives?

Vous vous demandez peut-être: "S'il y a un tel risque, n'est-ce pas juste un traitement non destructif?", Mais la méthode destructive ne crée pas de nouvelles valeurs, donc l'efficacité du programme est bonne. La [documentation officielle comparant list.sort et sorted] de Python (https://docs.python.org/ja/3/howto/sorting.html) dit également:

Un peu plus efficace si vous n'avez pas besoin de la liste d'origine.

Par exemple, «le processus de manipulation d'une liste avec un nombre énorme de valeurs» peut utiliser de la mémoire chaque fois que vous effectuez une copie. Si vous êtes un data scientist, vous rencontrerez probablement ces problèmes d'efficacité de la mémoire lorsque vous effectuez un traitement avec un grand nombre de lignes avec pandas.DataFrame.

Dans ce cas, utilisons correctement la méthode destructive. Cependant, dans les pandas, les opérations destructives / non destructives sont difficiles à deviner à partir du nom de la méthode, et j'ai vu des cas où d'énormes données sont copiées à l'intérieur de la méthode. / items / f8c562e0938271695576) vous devrez peut-être vous y habituer.

En passant, puisqu'il n'y a pas de traitement destructif en langage R (une copie est faite en interne à chaque fois), il n'y a aucun moyen d'améliorer l'efficacité d'un tel programme, et c'est un langage de programmation polyvalent qui peut être utilisé correctement selon les cas. Je pense que c'est l'un des avantages d'utiliser un certain Python.

Pour le dire un peu ésotérique, il est nécessaire de considérer le compromis avec l'efficacité du programme, en gardant à l'esprit "** localiser l'effet secondaire **". Je viens de citer un bon chiffre dans l'article "Unit Testing in R".

image.png

Les effets autres que l'entrée (argument) et la sortie (valeur renvoyée) de la fonction sont appelés effets secondaires. Données autres que les arguments (variables globales, bases de données externes), etc.

L'utilisation d'une fonction sans effets secondaires (fonction pure) présente des avantages tels que «facile à tester» et «facile à réutiliser». Par exemple, si vous écrivez du code dans pytest, le test se terminera facilement,

def test_f():
    x = [1, 2, 3]
    y = f(x)
    assert y == [1, 2, 3, 4, 5, 6]

Si vous lancez SQL dans la base de données dans la fonction f ou si vous acceptez l'entrée du terminal, vous ne pouvez pas le tester tel quel (vous devez écrire du code pour préparer un objet fictif), et l'argument x est destiné S'il est détruit sans lui, le chèque est souvent manqué.

En d'autres termes, si les effets des opérations destructives sont contenus uniquement à l'intérieur de la fonction, je ne pense pas qu'il soit nécessaire de forcer le code dans un code non destructif. Par exemple, le code suivant effectue une opération destructive appelée ʻappend, mais comme il n'a aucun effet en dehors de la fonction f` (pas d'effets secondaires), il n'y a aucun problème de test et de réutilisation.

def f(x):
    matrix = []
    for i in range(x):
        row = []
        for j in range(x):
            if i == j:
                row.append(1)
            else:
                row.append(0)
        matrix.append(row)
    return matrix

Vous devez également être conscient des effets secondaires autres que les opérations destructives. Je vous le dirai à chaque fois car cela semble long, mais si vous me donnez un conseil

  1. Lisez les données nécessaires (avec effets secondaires)
  2. Recevez les données de ① et effectuez un calcul numérique compliqué (pas d'effets secondaires)
  3. Sortez le résultat de ② (avec effets secondaires)

Je pense qu'il est bon de le mettre en œuvre sous la forme de, car cela permet aux scientifiques des données de tester et de réutiliser plus facilement la partie calcul numérique qui devrait vraiment ajouter de la valeur.

(Bien que ce soit un aparté complet, certains langages sont obligés de séparer strictement les fonctions pures et les fonctions avec des effets secondaires, et Land of Lisp Cartoon of side effects /archives/51854832.html) rit tellement alors s'il vous plaît lisez-le)

Résumé

Je l'ai résumé. Veuillez commenter si vous faites une erreur.

Recommended Posts

Toutes les méthodes destructrices que les data scientists devraient connaître
Conseils (structure de données) à connaître lors de la programmation de compétitions avec Python2
[Analyse des données] Dois-je acheter le drapeau Harumi?