Quel est le moyen le plus rapide de transformer les éléments d’une liste de listes de nombres en caractères ? Ce défi est une extension de la question que l'on retrouve ici dans la Communauté. J'ai décidé de tester huit approches : trois méthodes itératives ( For , For Each et Transform Each ), quatre méthodes non itératives (opérations sur table, expressions, Words + expressions et Regex ) et une hybride ( Words + Transform Each ). Cinq de ces méthodes, celles n'impliquant pas de Regex ou d'expressions, ont été prises en compte dans l'article lié. Les méthodes utilisant Words et Regex sont un peu trompeuses dans la mesure où elles exploitent un effet secondaire des opérations sur les chaînes : en particulier, toute structure de données peut être transformée en chaîne de caractères et analysée en conséquence. Je laisse au lecteur le soin de décider s’il estime que cette approche est légitime. Je connais quelques personnes qui désapprouveraient cela !
En utilisant une conception de remplissage d'espace, j'ai examiné quatre facteurs : le nombre de listes, le nombre d'éléments de la liste, si les listes avaient un nombre d'éléments égal ou inégal et le type de nombre : entier ou flottant. La conception se trouve dans List of lists – Experimental Runs.JMP . Les valeurs fractionnaires de la génération de conception originale ont été arrondies. Dix réplications ont été effectuées pour chaque analyse. Le code que j'ai utilisé pour exécuter l'expérience se trouve dans Run list of lists experimental.JSL et les résultats dans List of lists results.JMP. Les exécutions préliminaires utilisant Regex étaient extrêmement lentes et elles ont été retirées de l'étude.
Pour comparer les méthodes, je calcule l'ordre de classement de chaque méthode par exécution et réplication ainsi que la différence entre la durée d'exécution et la durée d'exécution médiane pour toutes les réplications au sein d'une exécution expérimentale. Plus petit est mieux. Les graphiques ci-dessous suggèrent que Transform Each , les opérations de table et les méthodes Words étaient supérieures. Bien que la différence de temps réelle soit faible (les valeurs sont en microsecondes), ces méthodes sont systématiquement mieux classées et présentent moins de variabilité.


Pour voir s'il y avait des différences entre les quatre méthodes, j'ai réexécuté l'expérience avec des listes plus grandes ( List of lists big – Experimental Runs.JMP et List of lists big results.JMP ). En examinant les différences de temps par rapport à la durée médiane d'une exécution, il apparaît qu'à mesure que le nombre total d'éléments augmente, les opérations de transformation de chaque et de table fonctionnent mieux. C'est un résultat heureux, puisque Transform Each vient tout juste d'être introduit dans JMP 16 et que les opérations sur table constituent une bonne solution pour ceux qui utilisent encore JMP 15.

Pour le deuxième défi, je vous ai demandé de transposer une liste de listes. Une liste de listes transposée rassemble tous les premiers éléments des listes d'entrée dans la première liste de sortie, les 2 èmes éléments d'entrée dans la 2 ème liste de sortie, etc. Si l'entrée avait n listes alors chaque liste de sortie aurait n éléments. Le nombre de listes de sortie serait égal au nombre maximum d'éléments dans chaque liste. Bien que le nombre de listes d'entrée soit facile à déterminer, le nombre de listes de sortie nécessite de compter les éléments de chaque liste d'entrée. Est-ce nécessaire ? Cela conduit-il à un processus plus rapide ?
Pour répondre à cette question et tester des méthodes spécifiques, j'ai exécuté une autre conception remplissant l'espace. Dans ce document, j'ai examiné trois méthodes itératives ( For , For Each et Transform Each ), chacune utilisant deux approches : l'une calculant le nombre de listes de sortie avant de générer la sortie, ce qui nécessite de boucler deux fois l'entrée, et l'autre construisant la sortie en utilisant une seule boucle sur l’entrée. De plus, j'ai envisagé d'utiliser les opérations sur les tables. Six types d'éléments de liste différents ont été pris en compte (entier, flottant, 1 caractère, 8 caractères, 15 caractères et caractères à largeur variable (longueur maximale 15), ainsi que des longueurs de liste fixes et variables (intérieures).
Lors de mon codage initial, j'ai découvert qu'il était plus rapide de créer la liste de sortie en commençant par une liste vide et en utilisant Insert Into plutôt que de pré-remplir la sortie avec les valeurs manquantes à l'aide de Repeat ou d'expressions. Vous pouvez utiliser le code dans Prebuild ou Insert.JSL pour vérifier cela par vous-même. Une deuxième découverte concernait Transform Each et For Each . Si vous n'êtes pas familier avec ces fonctions, elles se comportent un peu différemment de votre boucle For ou While standard dans la mesure où elles vous permettent de parcourir des listes, des matrices ou des tableaux associatifs, sans avoir à spécifier leur taille. Cela peut rendre le code un peu plus compact et plus propre. La structure d'un appel de base à l'une ou l'autre de ces fonctions est
fnName ({ indValue , ind }, conteneur , corps )
où fnName est For Each ou Transform Each , indValue est la valeur du conteneur à l'index ind , le conteneur est une matrice, une liste ou un tableau associatif et body est le corps du code. Des précautions doivent être prises lorsque vous travaillez avec de grands conteneurs. L'utilisation de grandes structures de données comme conteneur ou indValue ralentira considérablement l'exécution. Un exemple de ceci est donné dans Using For Each et Transform Each.JSL . Dans ce document, nous comptons le nombre d'éléments dans chaque liste d'une liste de listes. Les deux premiers blocs de code correspondent à la façon dont vous pourriez vous attendre à coder le problème, le premier utilisant chaque sous-liste comme valeur d'entrée, le second indexant les sous-listes. Comparez cela au troisième bloc où les indices de aList sont explicitement créés et utilisés dans la boucle. Selon la taille de aList , les améliorations de la vitesse de traitement peuvent être de 100 fois ou mieux. Transform Each est encore plus rapide, lui permettant de renvoyer les valeurs résultantes. Les blocs 4 et 5 montrent que la structure des données de sortie sera la même que la structure de données utilisée pour le conteneur d'entrée. Il existe un argument Output() facultatif pour contrôler le type de structure de données de sortie.
Le code pour tester les différentes méthodes de transposition se trouve dans Transpose lists.JSL . Comme ci-dessus, une conception remplissant l’espace a été utilisée pour comparer les résultats dans différentes conditions. Contrairement au code ci-dessus, une nouvelle structure de données est créée à chaque répétition. Les trois premières méthodes lisent l’entrée et construisent la sortie en un seul bloc. Les trois suivants prédéfinissent d'abord la taille des listes de sortie, avant de traiter l'entrée. L'avant-dernière méthode utilisait des opérations de table et la dernière Transform Each, structurée pour utiliser ses valeurs de sortie.

La prédétermination de la taille des listes de sortie s'est avérée plus rapide que d'essayer de le faire en un seul bloc. Les différentes implémentations de Transform Each montrent les améliorations de vitesse obtenues en travaillant avec ses valeurs de sortie. Les résultats sont dans Transpose results.JMP . Une expérience avec des listes plus grandes comparant For Each , Transform Each (2 ème implémentation) et les opérations de table peut être trouvée dans Transpose results big.JMP . Les résultats suggèrent que les opérations sur table, en général, surpassent les deux autres approches.

Pour récapituler ce que nous avons découvert :
- Dans le temps : transformer chaque < pour chaque < pour
- Les opérations sur les tables peuvent être ultra-rapides si les formules de colonnes ne sont pas impliquées. Dans des articles précédents, nous avons vu qu'en fonction de leur complexité, les formules de colonnes peuvent ralentir les opérations au point où les itérations ou les expressions sont favorables.
Je n'ai pas reçu les retours habituels sur ce défi que j'ai eu dans le passé, donc je suis sûr qu'il existe une meilleure approche. J'aimerais entendre vos commentaires. Cherchez un nouveau défi le mois prochain.