Ceux qui travaillent avec R savent très bien que le langage a été initialement conçu comme un outil de travail interactif. Naturellement, les méthodes pratiques pour une application étape par étape sur console par une personne qui est au fond du sujet se révèlent inadaptées à la création d'une application pour l'utilisateur final. La possibilité d'obtenir des diagnostics détaillés immédiatement en cas d'erreur, de parcourir toutes les variables et traces, d'exécuter manuellement des éléments de code (éventuellement en modifiant partiellement les variables) - tout cela ne sera pas disponible lorsque l'application R est hors ligne dans un environnement d'entreprise. (nous disons R, nous voulons dire essentiellement des applications Web Shiny).
Cependant, tout n'est pas si mauvais. L'environnement R (packages et approches) a tellement évolué qu'un certain nombre de trucs très simples peuvent résoudre avec élégance le problème de la stabilité et de la fiabilité des applications utilisateur. Un certain nombre d'entre eux seront décrits ci-dessous.
Il s'agit d'une continuation des publications précédentes .
Quelle est la difficulté de la tâche?
Le principal éventail de tâches pour lesquelles R est souvent utilisé est le traitement diversifié des données. Et même un algorithme entièrement débogué, présenté de tous côtés par des tests et entièrement documenté, peut facilement se décomposer et donner un non-sens si des données courbes sont glissées dans son entrée.
Les données peuvent être entrées à partir d'autres systèmes d'information ainsi que des utilisateurs. Et, si dans le premier cas il est possible d'exiger le respect de l'API et d'imposer des restrictions très strictes sur la stabilité du flux d'informations, alors dans le second cas il n'y a pas d'échappatoire aux surprises. Une personne peut faire une erreur et glisser le mauvais fichier, l'écrire mal. 99% des utilisateurs utilisent Excel dans leur travail et préfèrent abandonner le système avec lui, beaucoup de pages, avec un formatage astucieux. Dans ce cas, la tâche devient encore plus compliquée. Même un document visuellement valide peut sembler complètement insensé du point de vue de la machine. Les dates sont dispersées (la très célèbre histoire "Le designer d'Excel pensait que 1900 était une année bissextile, mais ce n'était pas le cas" ). Les valeurs numériques sont stockées sous forme de texte et de composition. Cellules invisibles et formules cachées ... Et bien plus encore. En principe, il est impossible de prévoir tous les râteaux possibles - il n'y a pas assez d'imagination. Qu'est-ce que cela vaut que de doubler les enregistrements dans diverses jointures avec des sources courbes.
Comme considération supplémentaire, nous prendrons ce qui suit:
L'excellent document «Une introduction au nettoyage des données avec R» décrit le processus de préparation préliminaire des données. Pour les étapes suivantes, nous distinguons la présence de deux phases de validation: technique et logique.
- La validation technique consiste à vérifier l'exactitude de la source de données. Structure, types, indicateurs quantitatifs.
- La validation logique peut être en plusieurs étapes, réalisée au cours des calculs, et consiste à vérifier la conformité de certains éléments de données ou de leurs combinaisons à différentes exigences logiques.
- L'une des règles de base dans le développement des interfaces utilisateur est la formation des diagnostics les plus complets en cas d'erreur utilisateur. Autrement dit, si l'utilisateur a déjà téléchargé le fichier, il est nécessaire de vérifier l'exactitude autant que possible et de donner un résumé complet de toutes les erreurs (il est également conseillé d'expliquer ce qui ne va pas), et de ne pas planter au tout premier problème avec un message comme «Entrée incorrecte valeur @ ligne 528493, pos 17 ”et nécessite le téléchargement d'un nouveau fichier avec cette erreur corrigée. Cette approche vous permet de réduire considérablement le nombre d'itérations pour former la source correcte et d'améliorer la qualité du résultat final.
Technologies et méthodes de validation
Allons de la fin. Il existe un certain nombre de packages pour la validation logique. Dans notre pratique, nous avons opté pour les approches suivantes.
- Déjà un
dplyr
classique. Dans les cas simples, il est pratique de dessiner simplement un tuyau avec une série de vérifications et d'analyse du résultat final. - Le package de
validate
pour vérifier la conformité des objets techniquement corrects avec les règles données.
Pour la validation technique, nous nous sommes concentrés sur les approches suivantes:
- Pack
checkmate
avec une large gamme de fonctions rapides pour effectuer une variété de contrôles techniques. - Travail explicite avec exceptions «Advanced R. Debugging, condition handling, and defensive programming» , «Advanced R. Beyond Exception Handling: Conditions and Restarts» à la fois pour effectuer la validation complète en une seule étape et pour garantir la stabilité de l'application.
- Utilisez des emballages de
purr
pour les exceptions. Très utile lorsqu'il est utilisé à l'intérieur d'un tuyau.
Dans le code décomposé en fonctions, un élément important de la programmation défensive est de vérifier les paramètres d'entrée et de sortie des fonctions. Dans le cas des langues avec typage dynamique, la vérification de type doit être effectuée indépendamment. Pour les types de base, le package qtest
est idéal, en particulier ses fonctions qtest
\ qassert
. Pour vérifier data.frame
arrêtés sur la construction suivante (vérification des noms et des types). L'astuce avec la fusion du nom et du type réduit le nombre de lignes dans le chèque.
ff <- function(dataframe1, dataframe2){ # calledFun <- deparse(as.list(sys.call())[[1]]) tic("Calculating XYZ") # (class, typeof, Date ) list(dataframe1=c("name :: character", "val :: numeric", "ship_date :: Date"), dataframe2=c("out :: character", "label :: character")) %>% purrr::iwalk(~{ flog.info(glue::glue("Function {calledFun}: checking '{.y}' parameter with expected structure '{collapse(.x, sep=', ')}'")) rlang::eval_bare(rlang::sym(.y)) %>% assertDataFrame(min.rows=1, min.cols=length(.x)) %>% {assertSetEqual(.x, stri_join(names(.), map_chr(., class), sep=" :: "), .var.name=.y)} # {assertSubset(.x, stri_join(names(.), map_chr(., typeof), sep=" :: "))} }) … }
Dans le cadre de la fonction de vérification de type, vous pouvez choisir une méthode à votre goût, en fonction des données attendues. class
été choisie car c'est elle qui donne la date comme Date
, et non comme un nombre (représentation interne). La question de la détermination des types de données est discutée en détail dans le dialogue "Une étude complète des types de choses dans R. 'mode' et 'class' et 'typeof' est insuffisante . "
assertSetEqual
ou assertSubset
sont sélectionnés pour des raisons de colonnes de correspondance claires ou du minimum suffisant.
Pour les tâches pratiques, un si petit ensemble couvre complètement la plupart des besoins.
Article précédent - R comme bouée de sauvetage pour un administrateur système .