Chaque semaine, découvrez de nouvelles astuces et bonnes pratiques pour devenir un expert !
Voir l'indexle 11-05-2018 08:10 PM
En tant qu'associé d'Alteryx qui a grandi avec l'entreprise du côté de l'avant-vente technique, vous vous attendriez à ce que je sois très utile pour créer un flux de travail ou deux. Je me souviens encore d'avoir construit ma première macro et d'avoir un peu buzz quand j'ai réalisé que je pouvais la réutiliser et la partager avec d'autres, et ça a juste marché.
Avance rapide de quelques mois, et un autre buzz quand j'ai découvert l'API Gallery - construire une application analytique dans Alteryx Designer et - hé presto! - un service analytique instantané qui pourrait s'appeler comme n'importe quel service Web. Nous avons eu du plaisir à jouer avec ces extrémités API-les mettre à l'intérieur des dispositifs intelligents comme le BTTN et Amazon Alexa!
Aller à 2017, et comme Alteryx a acquis la technologie et les compétences de YHat, nous avons développé Alteryx Promote - me donnant accès à une API en temps quasi réel que je pourrais utiliser pour répondre aux réponses prédictives du modèle: du score rapide au meilleur l'action, à l'analyse de texte - tout au long d'un format REST / JSON simple à mettre en œuvre.
Mais il y a toujours eu une partie du produit dont je me suis éloigné: Alteryx Engine et son kit de développement logiciel (SDK). Traditionnellement, cela a été le domaine de la meilleure et la plus brillante dans Alteryx et au-delà (James Dunkerley-je vous regarde ici!) et a exigé un certain niveau de maîtrise en C++ pour pouvoir parler la langue Alteryx.
J'ai regardé le développement du SDK Python avec un grand intérêt parce que, en tant que codeur amateur auto-avoué, Python, R et SQL sont probablement les limites de mes aspirations de codage. (je suis en train d'apprendre «juste assez de JavaScript pour être dangereux»-parce que JavaScript/node semble être partout ces jours-ci, et qui ne veut pas être un peu dangereux....?)
Connaissant juste assez de Python pour commencer, le SDK Python est ma passerelle vers cette dernière frontière - parler efficacement et directement à Alteryx Engine et construire de nouveaux outils partageables haute performance qui peuvent utiliser autant de grandes bibliothèques Python open-source comme un véritable complément aux outils R existants d'Alteryx.
Donc, j'ai sauté dans la documentation du SDK Python avec une grande excitation... et presque immédiatement obtenu coincé. Quelle déception! J'ai trouvé la terminologie du moteur déroutante et je ne pouvais pas progresser au-delà des bases du «bonjour monde» des échantillons initiaux.
Puis, il ya une semaine Neil Ryan a publié un guide très puissant dans le cadre de la communauté de science des données blog-ce rallumé mon désir de crack le SDK Python et ainsi (avec le code de Neil et de l'expertise personnelle), j'ai maintenant développé mon premier outil de SDK simple basé sur Python , et je voudrais partager avec vous dans ce blog. Je vais couvrir toutes les étapes que j'ai suivies pour que, espérons-le, vous puissiez reproduire ou améliorer le code, ou le prendre dans n'importe quelle direction!
Un nouveau projet exige un défi! J'ai vraiment aimé l'exemple de Neil de l'analyse de texte dans le billet de blog, et je veux prendre cette fois un pas de plus (en termes de contenu) et un pas en arrière (en termes de simplicité)!
Je voudrais utiliser un module Python appelé 'newspaper3k' pour exécuter la synthèse d'article sur une URL fournie, c'est-à-dire que je vous donne une URL et que vous analysez le texte derrière le lien, et renvoyez-moi les cinq phrases les plus importantes. article. J'adore les résumés d'articles - je peux obtenir l'essentiel d'une page sans avoir à lire le document entier, et j'aimerais avoir un outil de résumé d'article dans Alteryx pour que je puisse automatiser ce processus!
Un bon point de départ est de télécharger les exemples SDK à partir de https://github.com/alteryx/python-sdk-samples - se concentrer d'abord sur l'exemple Python - Single Input Output: il contient tout ce dont nous avons besoin pour notre premier outil de base.
Copiez ce répertoire sur votre machine locale et créez une structure de dossier comme suit:
Va chercher une image pour ton nouvel outil. Il ya beaucoup de sites qui offrent des jeux d'icônes libres (personnellement, j'utilise http://iconapp.IO/et https://iconmonstr.com/)-Enregistrez votre icône choisie au format png dans le dossier de l'article avec le nom Article_Icon. png.
Ensuite, créez un tout nouveau fichier vide dans un éditeur de votre choix (puisque nous écrivons du code Python, vous pouvez choisir un éditeur qui gère automatiquement le formatage du code python - Python choisit particulièrement l'indentation) et l'enregistre comme Article_Engine .py.
C'est la première partie du processus terminée - nous sommes prêts à commencer à personnaliser notre outil!
Lançons-nous dans notre fichier ArticleConfig.xml - cela indique à Alteryx le but de tous les fichiers que nous venons de copier ou de créer.
Nous allons apporter des modifications aux sections suivantes en jaune: changez simplement les noms de fichiers en ceux que nous avons créés dans la section précédente, et mettez à jour le MetaInfo pour qu'il contienne une bonne description de l'outil que nous construisons!
Pour ceux d'entre vous qui veulent un copier-coller, voici le code XML ci-dessous:
<metainfo> Python - Article Summary (Newspaper3k) Returns the most relevant sentences from a supplied URL. 1.1 Laboratory python, sdk, text analytics, text, nlp, python sdk Nick Jewell Alteryx, Inc. 2018
Certains outils auront des interfaces utilisateur plus complexes que d'autres. Ce blog couvre une interface assez basique où l'utilisateur sélectionne un seul champ pour l'analyse de texte, de sorte que notre fichier GUI va être vraiment simple. (ce qui est bon pour nous que nous sommes en train d'apprendre!)
Ouvrez le fichier Article_GUI.html et réduisez le code comme suit:
<meta charset="utf-8" />Article Summary <script type="text/javascript"> document.write(''); </script><script type="text/javascript"> Alteryx.Gui.BeforeLoad = (manager, AlteryxDataItems, json) => { } Alteryx.Gui.AfterLoad = (manager) => { } </script>
Tout ce que nous devons nous soucier dans ce code, c'est qu'il utilise le Alteryx JavaScript SDK pour créer une liste déroulante qui hérite des noms de champs à partir du flux de données et permet à l'utilisateur de choisir l'un de ces champs. Voir ci-dessous pour une capture d'écran d'action:
Enregistrez ce fichier HTML et c'est notre configuration complète. Nous sommes maintenant prêts à commencer à aborder la partie python de notre projet!
J'ai généralement constaté qu'un navigateur basé sur un environnement python tels que Jupyter Notebook Anaconda est le plus propice à l'itération rapide et le test de code-votre kilométrage peut varier, mais choisissez un environnement où vous pouvez tester votre code personnalisé avant de l'insérer dans le SDK Alteryx. Cette approche peut vous faire gagner beaucoup d'heures à résoudre des erreurs python!
Par exemple, dans un cahier de Jupyter, j'ai esquissé les fonctionnalités suivantes en quelques lignes:
La toute première commande! Pip install newspaper3k s'assure que la bibliothèque newspaper3k est installée dans mon environnement Python (ce n'est pas une partie standard des distributions Anaconda ou Alteryx).
J'importe ensuite la fonctionnalité Article du module de journal (ligne 2), fournissez une URL (ligne 22) et procédez au téléchargement, à l'analyse et à l'analyse du texte derrière l'URL conformément à la documentation du module (lignes 23-26).
Enfin, à la ligne 27, je produis un résumé de l'article en 5 phrases, délimité par le caractère de retour à la ligne (/ n). Ce sont les informations que j'aimerais apporter à Alteryx Designer pour une analyse plus poussée et un mélange.
Une fois que vous êtes satisfait d'avoir du code de travail, entrons dans un éditeur de texte / éditeur python et commençons à apporter des modifications à notre fichier Article_Engine.py - le cœur de notre travail Python SDK.
Dans cette section, je décompose le code section par section, en expliquant où je fais des ajouts (et pourquoi). Tout le code dans cette section réside dans le fichier Article_Engine.py.
""" AyxPlugin (required) has-a IncomingInterface (optional). Although defining IncomingInterface is optional, the interface methods are needed if an upstream tool exists. """ import AlteryxPythonSDK as Sdk import xml.etree.ElementTree as Et import nltk nltk.download('punkt') from newspaper import Article
Dans ces premières lignes, nous nous assurons que nous avons accès au Natural Language Toolkit de Python (NLTK), à un corpus de documents spécifique («punkt») et au module de journal que nous avons testé dans la section précédente.
class AyxPlugin: """ Implements the plugin interface methods, to be utilized by the Alteryx engine to communicate with a plugin. Prefixed with "pi", the Alteryx engine will expect the below five interface methods to be defined. """ def __init__(self, n_tool_id: int, alteryx_engine: object, output_anchor_mgr: object): """ Constructor is called whenever the Alteryx engine wants to instantiate an instance of this plugin. :param n_tool_id: The assigned unique identification for a tool instance. :param alteryx_engine: Provides an interface into the Alteryx engine. :param output_anchor_mgr: A helper that wraps the outgoing connections for a plugin. """ # Default properties self.n_tool_id = n_tool_id self.alteryx_engine = alteryx_engine self.output_anchor_mgr = output_anchor_mgr # Custom properties self.summary = "article_summary" self.summary_type = Sdk.FieldType.string self.summary_size = 1000
Nos propriétés personnalisées incluent une référence à la propriété 'summary' qui représentera notre sortie de l'outil. Dans ces trois lignes, nous avons défini un champ de sortie appelé 'résumé de l'article' qui est une chaîne et qui a une taille maximale de 1000 caractères.
def pi_init(self, str_xml: str): """ Handles building out the sort info, to pass into pre_sort() later on, from the user configuration. Called when the Alteryx engine is ready to provide the tool configuration from the GUI. :param str_xml: The raw XML from the GUI. """ if Et.fromstring(str_xml).find('FieldSelect') is not None: self.field_selection = Et.fromstring(str_xml).find('FieldSelect').text else: self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.error, 'Please select field to analyze') self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.info, self.field_selection) self.output_anchor = self.output_anchor_mgr.get_output_anchor('Output') # Getting the output anchor from the XML file.
Dans cette section, nous demandons à l'interface du plugin ('pi') d'analyser le champ et de stocker la valeur dans la propriété field_selection pour une utilisation ultérieure.
def pi_add_incoming_connection(self, str_type: str, str_name: str) -> object: """ The IncomingInterface objects are instantiated here, one object per incoming connection, also pre_sort() is called here. Called when the Alteryx engine is attempting to add an incoming data connection. :param str_type: The name of the input connection anchor, defined in the Config.xml file. :param str_name: The name of the wire, defined by the workflow author. :return: The IncomingInterface object(s). """ self.single_input = IncomingInterface(self) return self.single_input def pi_add_outgoing_connection(self, str_name: str) -> bool: """ Called when the Alteryx engine is attempting to add an outgoing data connection. :param str_name: The name of the output connection anchor, defined in the Config.xml file. :return: True signifies that the connection is accepted. """ return True def pi_push_all_records(self, n_record_limit: int) -> bool: """ Called when a tool has no incoming data connection. :param n_record_limit: Set it to <0 for no limit, 0 for no records, and >0 to specify the number of records. :return: True for success, False for failure. """ self.alteryx_engine.output_message(self.n_tool_id, Sdk.EngineMessageType.error, self.xmsg('Missing Incoming Connection')) return False def pi_close(self, b_has_errors: bool): """ Called after all records have been processed.. :param b_has_errors: Set to true to not do the final processing. """ self.output_anchor.assert_close() # Checks whether connections were properly closed.
Cette section a été laissée selon le code GitHub par défaut pour les connexions entrantes/sortantes vers le plugin, et la gestion des erreurs/fermeture de la connexion au plugin.
class IncomingInterface: """ This optional class is returned by pi_add_incoming_connection, and it implements the incoming interface methods, to be utilized by the Alteryx engine to communicate with a plugin when processing an incoming connection. Prefixed with "ii", the Alteryx engine will expect the below four interface methods to be defined. """ def __init__(self, parent: object): """ Constructor for IncomingInterface. :param parent: AyxPlugin """ # Default properties self.parent = parent # Custom properties self.record_copier = None self.record_creator = None
La classe d'interface entrante gère les interactions du moteur Alteryx avec le plugin, et c'est là que la plupart de notre code sera placé. Nous devons apporter des modifications au code GitHub afin de spécifier les champs qui sont traités sur une base ligne par ligne. Nous définissons ces définitions dans la fonction ii_init (), ci-dessous:
def ii_init(self, record_info_in: object) -> bool: """ Called to report changes of the incoming connection's record metadata to the Alteryx engine. :param record_info_in: A RecordInfo object for the incoming connection's fields. :return: True for success, otherwise False. """ # Returns a new, empty RecordCreator object that is identical to record_info_in. record_info_out = record_info_in.clone() # Adds field to record with specified name and output type. #record_info_out.add_field(self.parent.out_name, self.parent.out_type, self.parent.out_size) record_info_out.add_field(self.parent.summary, self.parent.summary_type, self.parent.summary_size) # Lets the downstream tools know what the outgoing record metadata will look like, based on record_info_out. self.parent.output_anchor.init(record_info_out) # Creating a new, empty record creator based on record_info_out's record layout. self.record_creator = record_info_out.construct_record_creator() # Instantiate a new instance of the RecordCopier class. self.record_copier = Sdk.RecordCopier(record_info_out, record_info_in) # Map each column of the input to where we want in the output. for index in range(record_info_in.num_fields): # Adding a field index mapping. self.record_copier.add(index, index) # Let record copier know that all field mappings have been added. self.record_copier.done_adding() # Grab the index of our new field in the record, so we don't have to do a string lookup on every push_record. #self.parent.out_field = record_info_out[record_info_out.get_field_num(self.parent.out_name)] self.parent.summary = record_info_out[record_info_out.get_field_num(self.parent.summary)] # Grab the index of our input field in the record, so we don't have to do a string lookup on every push_record. self.parent.input_field = record_info_out[record_info_out.get_field_num(self.parent.field_selection)] return True
Dans les lignes surlignées en gras, nous créons un enregistrement basé sur un «clone» (copie) des champs entrants, puis nous ajoutons notre nouveau champ de résumé aux métadonnées à la fin de l'enregistrement. En termes d'Alteryx, c'est comme utiliser un outil de formule pour créer un nouveau champ dans un ensemble de données.
Vers la fin de ce bloc de code, nous nous assurons que nos champs sont stockés efficacement afin que nous n'ayons pas à faire des recherches inutiles dans le cadre du traitement.
def ii_push_record(self, in_record: object) -> bool: """ Responsible for pushing records out Called when an input record is being sent to the plugin. :param in_record: The data for the incoming record. :return: False if method calling limit (record_cnt) is hit. """ # Copy the data from the incoming record into the outgoing record. self.record_creator.reset() self.record_copier.copy(self.record_creator, in_record) if self.parent.input_field.get_as_string(in_record) is not None: url = self.parent.input_field.get_as_string(in_record) article = Article(url) article.download() article.parse() article.nlp() result = article.summary self.parent.summary.set_from_string(self.record_creator, result) out_record = self.record_creator.finalize_record() # Push the record downstream and quit if there's a downstream error. if not self.parent.output_anchor.push_record(out_record): return False return True
La fonction ii_push_record () est où la majorité de notre codage personnalisé est placé. Notre code d'analyse de texte est situé dans une instruction if () qui vérifie une ligne de données. Nous exécutons ensuite la synthèse d'article et plaçons le résultat dans le champ de résumé que nous créons au début du code.
Nous appelons la fonction finalize_record () pour renvoyer l'enregistrement à l'utilisateur dans Alteryx Designer.
def ii_update_progress(self, d_percent: float): """ Called by the upstream tool to report what percentage of records have been pushed. :param d_percent: Value between 0.0 and 1.0. """ self.parent.alteryx_engine.output_tool_progress(self.parent.n_tool_id, d_percent) # Inform the Alteryx engine of the tool's progress. self.parent.output_anchor.update_progress(d_percent) # Inform the downstream tool of this tool's progress. def ii_close(self): """ Called when the incoming connection has finished passing all of its records. """ self.parent.output_anchor.output_record_count(True) # True: Let Alteryx engine know that all records have been sent downstream. self.parent.output_anchor.close() # Close outgoing connections.
Ces deux dernières fonctions (ii_update_progress () et ii_close ()) sont des fonctions d'entretien qui n'ont pas été modifiées depuis le modèle github.
Depuis la version 2018.1.4 + de Alteryx, il ya eu un petit changement quant à la façon dont le code python peut être distribué entre les utilisateurs qui veulent partager ces types d'outils, et c'est un processus en deux étapes.
Tout d'abord, créez un environnement virtuel pour Python en utilisant la commande suivante (ceci peut nécessiter un accès Admin pour écrire dans le dossier ProgramData):
C:/Program Files/Alteryx/bin/Miniconda3>python -m venv C:/ProgramData/Alteryx/Tools/Article
Ensuite, nous installons les modules nécessaires dans cet environnement virtuel:
C:/ProgramData/Alteryx/Tools/Article/Scripts>pip install nltk C:/ProgramData/Alteryx/Tools/Article/Scripts>pip install newspaper3k
(La seconde de ces commandes installera également un grand nombre de bibliothèques de support)
Ensuite, nous listons tous les modules dans cet environnement virtuel et les capturons dans un fichier requirements.txt (qui sera utilisé par le SDK Python pour répliquer cette configuration pour tous les utilisateurs supplémentaires.
C:/ProgramData/Alteryx/Tools/Article/Scripts>pip freeze > ../requirements.txt
Copiez ce fichier requiremnts.txt dans votre dossier Article et il devrait ressembler à ceci:
beautifulsoup4==4.6.0 certifi==2018.4.16 chardet==3.0.4 cssselect==1.0.3 feedfinder2==0.0.4 feedparser==5.2.1 idna==2.6 jieba3k==0.35.1 lxml==4.2.1 newspaper3k==0.2.6 nltk==3.2.5 Pillow==5.1.0 python-dateutil==2.7.2 PyYAML==3.12 requests==2.18.4 requests-file==1.4.3 six==1.11.0 tldextract==2.2.0 urllib3==1.22
Dans l'Explorateur Windows, naviguez dans un dossier au-dessus de votre dossier Article et créez un fichier appelé Config.xml. Ce sera le fichier de configuration principal pour votre outil d'installation. Utilisez le code suivant pour ce fichier:
<metainfo> Article Summary Return a new-line delimted article summary from a supplied URL. 1.1 Laboratory Nick Jewell Article/Article_Icon.png
Changez les éléments en gras si nécessaire, et enregistrez. Vous devriez maintenant avoir une structure de dossier qui ressemble à ceci:
La seule contrainte entourant le nommage que j'ai trouvé est que le fichier ArticleConfig.xml doit être nommé de manière cohérente avec le répertoire parent et doit inclure le mot Config sans aucun espace. Ainsi, un répertoire parent appelé 'foo' devrait avoir un fichier de configuration nommé FooConfig.xml à l'intérieur.
Zip le répertoire d'article et les fichiers Config. xml dans un fichier zip appelé'article. zip', puis utilisez la ligne de commande pour renommer l'extension. zip en. yxi (type de fichier Alteryx installer) comme suit:
move Article.zip Article.yxi
Vous devriez voir l'icône changer dans l'Explorateur Windows à partir d'un dossier compressé vers un installateur Alteryx.
Si vous double-cliquez sur le programme d'installation, vous devrez effectuer une action dans Alteryx Designer. La boîte de dialogue ressemblera à ceci:
Cliquez sur Installer, accédez à l'onglet Laboratoire et déposez l'outil dans un flux de travail pour commencer les tests.
Dans le cadre du test de l'outil, créez un workflow simple qui utilise une URL de test et vérifiez qu'il retourne les résultats correctement – déposez un outil texte-à-colonnes après que l'outil personnalisé a été divisé en fonction du délimiteur de saut de ligne (/n) en lignes pour faciliter l'affichage:
Avec ces données d'entrée, je reçois la sortie suivante de mon nouvel outil:
(c'est à dire exactement le même que je reçois de mon portable Jupyter.) Toutefois, s'il y a des erreurs, vous devez recevoir des messages d'erreur raisonnablement bons du SDK Python, y compris la ligne de code qui lance l'erreur.
Dans ce tour de montagnes russes du SDK Python, nous avons relevé le défi d'améliorer notre outil d'analyse textuelle de la manière la plus simple possible - nous avons simplifié toutes les étapes pour produire un nouvel outil Alteryx au strict minimum, mais introduit:
S'il vous plaît laissez-moi savoir via la section commentaires si toutes les étapes de ce processus ne sont pas claires, ou si vous trouvez des erreurs. Sinon, je vous souhaite à tous des sentiers heureux avec cette grande nouvelle fonctionnalité!
Un grand Merci à Neil Ryan et à l'ensemble de la communauté des développeurs Alteryx (Developers.alteryx.com) pour m'avoir donné le soutien dont j'avais besoin pour réussir!
Jamais demandé comment construire un nouvel outil analytique à partir de zéro en utilisant le SDK Python Alteryx, mais ne savait pas par où commencer? Ce blog vous emmène à travers les bases absolue pour vous mettre en place et en cours d'exécution-vous serez la création de nouveaux outils de marque, connecteurs et des analyses avancées en un rien de temps avec ce guide étape par étape débutants!