Les rabais sur Shopify en 2023

Les réductions sur Shopify ont toujours été un peu maladroites. Faciles à utiliser et à comprendre, mais pas assez flexibles pour faire quoi que ce soit d’extraordinaire. Le lancement de Shopify Functions pour les Réductions plus tôt cette année promettait de résoudre ce problème une fois pour toutes. Cependant, son utilisation implique la création d’une application personnalisée, ce qui est bien plus complexe que mes bien-aimés, mais obsolètes Scripts Shopify. Et dans les deux cas, Shopify Plus était nécessaire, ce qui n’est pas accessible à tout le monde.

Il y a quelques semaines, j’ai découvert une application publique qui utilise les Fonctions Shopify pour les Réductions en interne, et qui ouvre toute la logique des réductions complexes à tout le monde. Étant une application publique, elle peut aussi être utilisée sur tous les plans Shopify! Voici Discount Kit.

En plus d’utiliser les fonctionnalités de réduction natives et de permettre aux marchands de réaliser des réductions en volume de produits, des cadeaux avec achat et des objectifs de commande (ajoutés automatiquement au panier sans intervention du client!), elle ouvre les fonctionnalités des Fonctions Shopify avec la fonction “Réduction Personnalisée”. J’ai eu la chance de participer à la bêta et, d’après mes tests, cela fonctionne à merveille!

Un exemple de réduction qui était toujours impossible ou extrêmement compliqué pour le client était d’avoir différentes réductions sur différents produits dans le même panier. C’était possible depuis l’année dernière, lorsque la superposition de réductions a été introduite, mais c’était un enfer pour le client, qui devait entrer différents codes les uns après les autres à la caisse.

Les réductions personnalisées vous permettent de le faire en 2 minutes. Point bonus : cela se fait dans l’interface de réduction réelle, donc vous n’avez pas à jongler entre les réductions natives et les réductions d’applications! Voici à quoi cela ressemble:

Maintenant, la stratégie d’application “Tout” est toujours en bêta et a quelques bizarreries, comme n’afficher que le premier message. Je comprends que c’est une limitation de l’API de Réductions Shopify pour le moment, donc cela devrait être corrigé prochainement. La répartition réelle de la réduction fonctionne bien, c’est ce qui compte!

Avec cela, je peux dire avec confiance que les réductions ne sont plus un point faible sur Shopify. Cette application a toutes les fonctions de l’API de Fonctions de Réduction avec une interface utilisateur élégante, et leur support est excellent. Elle peut également être utilisée sur tous les plans Shopify, ce qui est génial!

Une petite parenthèse pour les développeurs (les non-développeurs peuvent sauter cela), ils incluent également une API JS côté client pour construire dessus. C’est encore une construction préliminaire et elle ne prend pas en charge les Réductions Personnalisées (pour le moment), mais elle donne accès à toutes les réductions actives lors d’un événement nommé ‘discount_kit:engine_result’. Voici à quoi cela ressemble:

Avec cela, vous pouvez construire tout ce dont vous avez besoin en rapport avec l’affichage des réductions dans le thème, que ce soit pour afficher les réductions disponibles dans le panier, les tableaux de réductions en volume sur la page produit, ou n’importe quoi d’autre, vraiment. Les données sont là!

Allez l’essayer pendant la période d’essai gratuite! Je parie que vous la garderez une fois celle-ci terminée.

Black Friday – Tactiques Classiques et alternatives pour 2021

Le Black Friday 2021 s’en vient. Le 26 novembre, pour être exact. Dans moins d’un mois. Populaire depuis 2012 au Canada, vos clients s’attendent à une communication de votre part durant cette période.  

Moment crucial de l’année pour tous les marchands en ligne, il est source d’angoisse profonde pour ceux mal préparés, et d’excitation pour ceux prenant de l’avance. Essayons de vous faire tomber dans le 2e camps avec ce petit article, où je vous présenterai les tactiques « classiques » de cette promotion, ainsi que certaines tactiques alternatives pour se démarquer de la cohue.

Tout d’abord, le calendrier « officiel » de cette promotion :

  • Thanksgiving – Jeudi, 25 novembre
  • Black Friday – Vendredi, 26 novembre
  • La fin de semaine du Black Friday – Samedi et Dimanche, 27-28 novembre
  • Cyber Monday – Lundi, 29 novembre

Au Canada, le jeudi est moins important qu’aux États-Unis, puisque les Canadiens ne sont pas en congé comme leurs voisins du Sud. Je l’inclus dans le calendrier pour ceux qui auraient des ventes importantes aux É-U, mais la tactique classique ne sera pas axée sur les ventes lors de cette journée.

Voici donc la tactique, le minimum à réaliser pour votre Black Friday 2021! Celle-ci se concentre sur quatre canaux : email, social, direct et paid. Il s’agit ici du minimum, ce qui ne veux pas dire que vous ne pouvez pas faire plus! Nous discuterons de plusieurs idées plus loin.

La recette minimale pour un Black Friday réussi

Thanksgiving – Jeudi, 25 novembre

C’est le moment de créer de l’anticipation pour votre offre. Ne vendez pas le punch! Restez mystérieux, mais donnez assez d’indices pour que vos fans les plus fidèles aient une bonne idée de ce qui s’en vient.

Canal Email : Envoyez un teaser de votre offre du lendemain par courriel à votre liste complète. Si vous avez un segment VIP, vous pouvez ouvrir votre offre d’avance à ce segment seulement.

Canal Social : Publiez le même teaser sur tous vos canaux sociaux.

Canal Direct : Changez votre « Hero Image » de page d’accueil pour le teaser de votre offre.

Canal Paid : Amplifiez votre publication du canal social à une audience de reciblage.

Black Friday & Fin de semaine – Vendredi, 26 novembre au dimanche 28 novembre

Canal Email : Envoyez votre offre à votre liste complète le vendredi, puis un rappel le samedi et le dimanche. Segmentez votre liste pour les rappels du samedi et du dimanche pour exclure ceux qui ont déjà interagi avec vous (soit via une ouverture de courriel, un clic, ou un achat, dépendant si vous voulez être très agressifs ou non)

Canal Social : Publiez votre offre sur tous vos canaux sociaux, ainsi qu’un rappel le samedi et le dimanche. Si vous pouvez, ajoutez un sentiment d’urgence par une possibilité de rupture de stocks, de délais de livraison, ou encore simplement en mentionnant que l’offre prends fin bientôt.

Canal Direct : Changez votre « Hero Image » de page d’accueil pour celle de votre offre. N’oubliez pas de clairement indiquer la date de fin pour augmenter le sentiment d’urgence.

Canal Paid : Amplifiez votre publication du canal social à une audience de reciblage. Si vous faites des publicités d’acquisition Google Ads durant cette période, ajoutez votre code promo et date de fin comme Ad Extension sur vos publicités texte, et mettez à jour votre flux de données (si pas connecté directement) dans le cas où vous faites aussi du Google Shopping.

Cyber Monday – Lundi, 29 novembre

C’est le moment de faire une nouvelle offre, différente de celle que vous aviez durant la fin de semaine, et qui dure seulement durant la journée, une « flash sale ».

Il faut faire attention, toutefois, de ne pas faire quelque chose de meilleur que ce que vous aviez durant la fin de semaine. En effet, cela créerait de la frustration chez votre clientèle. L’idéal est de créer une offre qui est complémentaire et/ou distincte. Par exemple, si vous avez offert 25% de rabais sur tout en magasin (pas idéal comme offre – voir plus loin) durant la fin de semaine, vous pourriez faire une offre d’un cadeau avec un achat minimal pour le lundi. Ainsi, quelqu’un qui a profité du 25% durant la fin de semaine pourrait acheter à nouveau le lundi afin de profiter de la nouvelle offre.

Canal Email : Envoyez votre offre à votre liste complète. Si c’est pertinent, vous pouvez personnaliser le message entre les acheteurs de la fin de semaine et les non-acheteurs.

Par exemple, les acheteurs de la fin de semaine reçoivent un courriel avec le titre « Merci pour votre achat chez XYZ! Voici une nouvelle offre juste pour vous » alors que les non-acheteurs reçoivent un courriel avec le titre « Notre offre du Cyber Monday à l’intérieur, valide aujourd’hui seulement! ».

Canal Social : Publiez votre offre sur tous vos canaux sociaux. N’oubliez pas de mentionner que l’offre est valide aujourd’hui seulement.

Canal Direct : Changez votre « Hero Image » de page d’accueil pour celle de votre offre. N’oubliez pas de mentionner que l’offre est valide aujourd’hui seulement.

Canal Paid : Amplifiez votre publication du canal social à une audience de reciblage. Si vous faites des publicités d’acquisition Google Ads durant cette période, ajoutez votre code promo et date de fin comme Ad Extension sur vos publicités texte, et mettez à jour votre flux de données (si pas connecté directement) dans le cas où vous faites aussi du Google Shopping.

Qu’est-ce qu’une bonne offre?

Maintenant, avant d’aller dans les tactiques alternatives la grande question est, qu’est-ce qu’une bonne offre? Il est important d’y réfléchir car vous serez en compétition avec toutes les autres offres sur le marché. Généralement, je conseille de faire une ou des offres de 20-40% sur des catégories spécifiques, et des offres jusqu’à 50% sur des produits spécifiques. Par exemple, si vous êtes un marchand de vêtements, vous pourriez offrir 20% sur une collection que vous vendez déjà très bien, question de mousser ses ventes sans la dévaluer, un 30% sur une collection que vous aimeriez voir bouger un peu plus, et un 40% sur la collection d’été qui, évidemment à l’automne, ne bouge pas du tout. Par-dessus tout ça, vous pouvez offrir du 50% sur des produits particuliers « Dernière chance » qui ne reviendront jamais en stock.

Une chose à ne pas faire, jamais au grand jamais, est d’offrir un rabais majeur « store-wide » – par exemple, offrir 50% de rabais sur tout. Je le spécifie car je le vois, mais cette tactique, bien qu’elle va évidemment ramener des ventes, a deux faiblesses majeures. La première, et la plus grave, est qu’elle envoie le message que votre marge de profit sur tous vos produits est assez grande pour vous permettre de les vendre à moitié prix. Les consommateurs comprennent alors qu’ils surpaient pour votre produit, et leur perception de qualité en est grandement diminuée. Ensuite, offrir un « rabais sur tout », en termes de communication, ne dis pas c’est un rabais sur quoi, exactement. La réalité est que vos clients ne connaissent pas si bien vos produits. Toutefois, si vous faites un rabais sur une catégorie (Les pantalons de sport de marque XYZ à 20% de rabais!), vous venez de créer une association que la marque XYZ vends des pantalons de sport. Une occasion ratée si vous avez fait un rabais sur tout.

Maintenant que ce faux-pas est derrière nous, il est toujours préférable d’être créatif dans ses promotions, plutôt que de toujours se retourner vers un pourcentage de rabais. Les « Buy X, Get Y » sont une bonne façon de se démarquer, en offrant un produit complémentaire. Par exemple :

  • Achetez une paire de lunettes, obtenez un traitement anti-rayures gratuit
  • Achetez des chaussures de sport, obtenez une séance d’entrainement personnalisée de 30 minutes
  • Achetez un système de son, obtenez le câblage gratuitement

Remarquez que ces offres ont l’avantage d’informer le client sur vos produits ou services complémentaires. Peut-être que le client du magasin de chaussures de sport n’avait aucune idée de l’existence d’un service d’entrainement personnalisé. Même s’il ne profite pas de l’offre, vous aurez informé vos clients de l’existence de ce service.

Si votre marque est axée sur le « social good » avec des valeurs environnementales, et que votre audience s’identifie bien à cette valeur, vous avez probablement peur de les aliéner avec votre participation au Black Friday, qui est perçu (avec raison) comme la fête de la surconsommation. Une offre durant laquelle vous augmentez votre impact positif en même temps que vous donnez un petit quelque chose au client pourrait alors être très efficace. Par exemple, une marque qui plante 10 arbres par chandail planté (tousse-tentree-tousse) pourrais offrir 10% de rabais sur sa collection de t-shirt, mais planter 50 arbres par t-shirt au lieu des 10 habituels. Une pierre deux coups : augmentation des ventes, augmentation des arbres, tout le monde y gagne!  

Enfin, lorsque vous élaborez vos offres, ayez en tête l’objectif qu’il soit facile d’atteindre votre seuil de livraison gratuite[1]. Si votre seuil est assez élevé, vous pouvez aussi le baisser durant la période du Black Friday – n’oubliez pas de le communiquer!

Tactiques créatives pour se démarquer

Le problème principal à surmonter durant la fin de semaine du Black Friday est le bruit ambiant. Tout le monde crie sur les toits qu’ils ont la meilleure offre, et la compétition pour l’attention du consommateur est féroce. Durant cette fin de semaine (et même la semaine avant), le prix du paid media ainsi que le nombre de courriels promotionnels envoyés augmentent de façon significative, et la portée organique sociale diminue. L’attention et le budget du consommateur, eux, restent toutefois fixe.

Voici certaines idées à essayer cette année, afin de tirer son épingle du jeu. Un bémol, toutefois – ces tactiques prennent toutes « de l’avance » sur l’événement, et pourraient affecter vos ventes actuelles. En effet, les consommateurs vont reporter leur consommation s’ils s’attendent à un rabais à court terme.

Créer un événement Facebook pour le Black Friday

Créez un événement Facebook 2-3 semaines d’avance avec un teaser de votre offre, en spécifiant que les détails seront révélés le matin du Black Friday. Puis, mettez un budget de paid media en reciblage (et en acquisition si nécessaire) à l’avance pour que les consommateurs répondent qu’ils sont intéressés.

Cette tactique permet de créer de l’excitation pour l’offre à venir, tout en profitant d’un paid media à prix normal. De plus, Facebook va envoyer un push notification (gratis!) le jour de l’événement à tout ceux ayant répondu qu’ils seraient présents. Une bonne manière de sortir du lot à plus petit prix!

Envoyer l’offre 24h d’avance aux abonnés SMS

Deux ou trois semaines à l’avance, faites une campagne d’inscription aux SMS via des Lead Ads Facebook, un pop-up sur votre site, ainsi qu’un envoi à votre liste de courriels, informant les clients qu’ils recevront vos offres du Black Friday 24h à l’avance. Le taux d’ouverture des SMS frise les 100%, et sont encore peu utilisés de manière promotionnelle. Il s’agit du canal le plus direct pour traverser la cohue de communication du Black Friday.

Attention, toutefois – votre offre doit être communiquable de manière courte et précise. Ce n’est pas le moment du roman-fleuve (comme ce billet!)

Faire une campagne « Sponsored Messages » sur Facebook Messenger

Deux ou trois semaines d’avance, créez une publicité d’interaction avec vous sur Messenger, avec la promesse de connaitre votre offre Black Friday le jour même et un teaser de cette offre. Vous pouvez faire une expérience sympathique via un chatbot, mais ce n’est pas requis. Vouis pouvez gérer les messages « à la mitaine », aussi.

Puis, activez une campagne « Sponsored Messages » durant la fin de semaine du Black Friday pour informer tous les clients ayant interagi avec vous dans la dernière année (!!!), incluant ceux de la campagne mentionnée au paragraphe précédent, de votre offre.

Ce placement est nouveau depuis cette année, donc je n’ai pas encore eu l’occasion de l’essayer, mais il semble extrêmement prometteur. J’ai hâte de l’utiliser!

Conclusion

Ce petit guide ne se veut ni exhaustif, ni universel. Toutefois, il donne une direction et des idées afin de faire de votre Black Friday 2021 un succès! Avez-vous d’autres idées à proposer pour cette année? Laissez-les ci-dessous en commentaire!  


[1] Si vous n’offrez pas la livraison gratuite à partir d’un certain montant, ignorez ce guide et allez d’abord offrir ce must.

Créer un catalogue de produits multilangues sur Facebook via Shopify, grâce aux nouveaux Localized Language Feeds

Il n’y a pas si longtemps, il fallait créer un catalogue par langue pour utiliser toute la puissance des publicités de produits dynamique (Dynamic Product Ads – DPA) dans un marché bilingue comme le Québec.

Plus maintenant! Une nouvelle fonctionnalité lancée cette année permet maintenant d’ajouter un feed supplémentaire au feed principal, qui vient remplaçer les valeurs affectées par la langue (ex. le titre, le lien, la description) tout en conservant les valeurs qui ne le sont pas (ex. le prix, les photos). En gros, c’est un simple « Join » sur le ID du produit, qui va ajouter l’information de langue au feed principal.

Malheureusement, la documentation de Facebook est déficiente et ne donne des instructions que pour le téléversement d’un fichier csv statique. Pour ceux qui me connaissent, j’ai horreur des tâches manuelles, et je me suis donc lancé dans la création d’un feed RSS via un template Shopify, qui afficherait les bonnes informations et qui serait mis-à-jour à toutes les heures.

Le résultat? Enfin, la possibilité de faire UN SEUL CRÉATIF dans UN SEUL AD SET dans UNE SEULE CAMPAGNE pour une publicité DPA qui va cibler un marché bilingue comme le Québec, au lieu de le faire avec des catalogues différents, ce qui impliquait une duplication des campagnes pour être en mesure de cibler par langue. Vous n’aurez qu’à créer vos variations de langue dans l’option « Add Languages » du gestionnaire de publicité, et le tour sera joué!

Prêts à commençer? Attachez votre tuque, on se lance!

Pour suivre ce tutoriel, vous aurez besoin de :

  1. Shopify
  2. Langify V2 (Ne fonctionne pas sur V1, désolé!)
  3. Un catalogue de produit mis-à-jour via data feed (L’app Facebook fonctionne bien pour ça)

La première étape est de créer votre feed de langue. Celui-ci va être créé à l’aide d’un template et d’une collection personnalisée.

Allez dans l’éditeur de code Shopify, et créez un nouveau template de collection. Dans celui-ci, copiez collez le code ci-dessous, compatible pour Anglais et Français seulement, mais c’est assez simple de rajouter des langues si nécessaires via la section <override>. Ce template est fortement inspiré par l’article de blog « DIY Facebook Product Feed for Shopify »

{% layout none %}<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
{%- paginate collection.products by 1000 -%}
{%- if shop.currency == 'CAD' -%}{%- assign CountryCode = 'CA' -%}{%- endif -%}
{%- assign PriceAdjustment = 1.0 -%}
{%- assign PriceAdjustmentEffectiveDate =  '20181226T080000-0500/20190102T235900-0800' -%}

<channel>
<title>{{ shop.name }} {{ collection.title | replace: '&', '&amp;' }}</title>
<link>{{ shop.url }}</link>
<description>{{ collection.description | strip_html }}</description>
{%- for product in collection.products -%} 
  {%- for variant in product.variants -%}
    {%- for option in product.options -%}
  	  {%- if option == 'Color' -%}{% capture Color %}{{ variant.options[forloop.index0] }}{% endcapture %}
  	  {%- elsif option == 'Size' -%}{% capture Size %}{{ variant.options[forloop.index0] }}{% endcapture %}
  	  {%- endif -%}
    {%- endfor -%}

<item>    
<override>{% if shop.locale contains "fr" %}fr_XX{% elsif shop.locale contains "en" %}en_XX{% endif %}</override>
<title>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }}{% unless product.title contains Color %} {{ Color | replace: '&', '&amp;' }}{% endunless %}</title>
<link>{{ shop.url }}{{ variant.url }}</link>
<description>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }} {{ variant.title | strip_html | strip_newlines | replace: '&', '&amp;' }} {{ product.description | replace: '</', ' </' | strip_html | strip_newlines | replace: '&', '&amp;' }}</description>
<g:item_group_id>{{ product.id }}</g:item_group_id>
<g:id>{{ variant.id }}</g:id>
{% unless Color == "" %}<g:color>{{ Color | strip_html | strip_newlines | replace: '&', '&amp;' }}</g:color>{% endunless %}
{% unless Size == "" %}<g:size>{{ Size | strip_html | strip_newlines | replace: '&', '&amp;' }}</g:size>{% endunless %}
<g:custom_label_0>{{ product.metafields.mm-google-shopping.custom_label_0 }}</g:custom_label_0>
<g:custom_label_1>{{ product.metafields.mm-google-shopping.custom_label_1 }}</g:custom_label_1>
<g:custom_label_2>{{ product.metafields.mm-google-shopping.custom_label_2 }}</g:custom_label_2>
<g:custom_label_3>{{ product.metafields.mm-google-shopping.custom_label_3 }}</g:custom_label_3>
<g:custom_label_4>{{ product.metafields.mm-google-shopping.custom_label_4 }}</g:custom_label_4>
</item>

  {% endfor %}
{% endfor %}
</channel>
</rss>
{% endpaginate %}

Sauvegardez votre nouveau template, puis allez ensuite créer une nouvelle collection. Ajoutez tous les produits de votre boutique à la collection, puis choisissez le template que vous venez de créer :

Une fois que c’est fait, vous pouvez cliquer sur « View » pour être redirigé vers l’url de votre nouveau data feed! La structure d’url ressemblera à https://<DOMAIN>.<TLD>/collections/<NOM COLLECTION>. Vous pouvez faire un clic-droit et choisir « View Source » si vous voulez que votre fichier XML soit affiché avec un formatage pour les humains.

Vous devez ensuite changer votre url en rajoutant le préfixe ou suffixe du choix de langue, ex.

https://fr.<DOMAIN>.<TLD>/collections/<NOM COLLECTION>

https://<DOMAIN>.<TLD>/fr/collections/<NOM COLLECTION>

Je rappelle que cette méthode ne fonctionne qu’avec Langify V2, qui va chercher les traductions avec l’API GraphQL. Possiblement que ça peut marcher avec d’autres apps qui utilisent GraphQL, mais je n’ai pas testé.

Votre feed va ressembler à ça :

Maintenant que vous êtes rendus la, vous devez aller ajouter votre feed dans votre gestionnaire de catalogue, sur le feed déjà installé. Vous allez dans vos Data Sources, puis cliquez sur Add New Feed, puis vous suivez les étapes après « Add Language Information » où les étapes seront les mêmes qu’un feed standard. Vous verrez finalement apparaitre cet écran magique qui vous confirme que vous avez ajouté la localisation de la langue à votre data feed :

Et bang! Juste comme ça, vous allez pouvoir simplifier vos campagnes publicitaires. Pour tous ceux qui ont de plus petits budgets, et qui sont souvent pris dans le « Learning Phase » de leurs Ad Sets, ça peut énormément aider à la performance de vos publicités, car tous votre budget ira dans un seul Ad Set, ce qui permettra à Facebook de mieux optimiser selon votre objectif.

J’espère que ce mini tutoriel vous viendra en aide! N’hésitez pas à répondre en commentaire si vous avez des questions.

Références:
Create a Country or Language Feed for a Catalog
https://www.facebook.com/business/help/2144286692311411?id=725943027795860
Localized Catalog Setup
https://developers.facebook.com/docs/marketing-api/catalog/localized-catalog/localized-catalog-setup/#feed-format-lang
Reference – Catalog – Supported Fields — Localized Catalogs
https://developers.facebook.com/docs/marketing-api/catalog/reference#loc-cat-fields
Reference – Catalog – Supported Feed Formats
https://developers.facebook.com/docs/marketing-api/catalog/reference#loc-cat-fields

Stratégie de marketing numérique pour PME

Qu’est-ce que le point de départ d’une stratégie de marketing numérique ? C’est d’abord de déterminer les étapes qu’on doit prendre afin d’atteindre un objectif précis. En effet, la stratégie offre une direction à l’implémentation de tactiques de marketing efficaces. Il faut se dire qu’il n’y a pas de mauvaise action de marketing (bon, peut-être que faire du keyword spamming avec du texte blanc sur fond blanc, ou du follow-unfollow sur Instagram n’est pas la meilleure idée), mais il y a toute la question du coût d’opportunité. Si on fait une chose, on est pas en train d’en faire une autre. Tiens, dans mon cas (même si je ne suis pas une PME), je fais présentement du marketing de contenu, mais je ne peux pas faire autre chose en même temps (oups la comptabilité qui traine !).

Il faut donc prendre les actions qui auront le plus grand retour pour le plus faible effort en premier. On ne va pas implanter un CRM avant d’avoir fait de simples campagnes de marketing par courriel. On ne va pas essayer de vendre notre produit en France si on n’a pas saturé notre marché au Québec. Ainsi de suite ! Ca peut sembler évident, mais j’ai souvent vu des entreprises mettre « la charrue avant les bœufs » et s’embourber dans des projets impossibles alors que la base n’était pas encore atteinte.

Sans plus tarder, je vous offre mon plan stratégique de base pour une entreprise avec une image de marque bien définie et reconnue dans son domaine (je n’entre pas dans le brand buidling ici !) qui souhaite vendre un produit aux attributs peu différenciés, et qui véhicule son avantage concurrentiel à travers sa marque.

Le framework utilisé pour la réflexion

Pour créer une stratégie, j’utilise le modèle classique AIDA que tout le monde connait, intuitivement ou formellement, faisant partie de la famille des nombreux modèles de hiérarchie des effets. Celui-ci postule qu’un individu doit passer par quatres étapes avant de faire un achat : Awareness, Interest, Desire, Action – en français : Connaissance, Intérêt, Désir, Action.

Le classique entonnoir de conversion selon le modèle AIDA – CC BronHiggs : Wikipedia

Ce modèle étant tellement véhiculé partout, plusieurs personnes l’appliquent de manière inconsciente en réfléchissant au chemin complet du consommateur. Une réflexion commune serait la suivante : je dois faire circuler ma marque devant un consommateur afin qu’il me connaisse. Une fois que le consommateur me connait, je peux intéresser celui-ci de manière cognitive à mon produit en lui présentant ses avantages concurrentiels durables. Une fois qu’il est intéressé, je peux m’adresser à lui de manière affective de manière à créer le désir envers ma marque. Enfin, je peux l’amener à l’action par des activités promotionnelles, ou par des contacts réguliers dans l’espoir de l’atteindre au moment où il est prêt à passer à l’action.

Cette manière de penser semble tout à fait logique et efficace. Il faut bien que le client connaisse notre entreprise avant d’être en mesure d’acheter ! Toutefois, pour une PME, ceci est fondamentalement faux pour une seule raison : il y a déjà des clients sur le marché maintenant qui recherchent un produit comme le vôtre pour répondre à leur besoin. Ils n’ont pas besoin de connaitre votre marque pour assouvir leur besoin, ils veulent répondre à celui-ci simplement, rapidement et à bon prix. Ainsi, lorsqu’on décide quoi faire dans notre stratégie marketing, il faut considérer l’effort et l’argent à investir afin de rejoindre chacune des strates :

Un petit ajout qui change tout

En ajoutant cette dimension, on voit pourquoi une campagne massive d’image de marque devrait arriver en dernier. Si on n’a pas déjà maitrisé les autres étapes, ce sera un (dispendieux) coup d’épée dans l’eau. Ainsi, il faut travailler à l’envers, en commençant par les consommateurs les plus propices à acheter. Une fois qu’on a maitrisé une strate, on passe à la suivante. Mais avant tout, on doit mettre sa maison en ordre.

.Étape 0 : On pose le premier bloc formant la fondation de notre stratégie

Étape 0 : Analyser son site pour l’améliorer, implémenter les analytiques de base, et mettre en place une campagne de remarketing

Avant toute chose, avant la moindre campagne de marketing, il faut s’assurer que notre pot de miel est efficace. Ça ne donne rien de rejoindre des clients potentiels si notre panier d’achat est brisé ou difficile d’utilisation. On ne parle pas de Conversion Rate Optimization ici (CRO) – on assume qu’il n’y a pas assez de données pour le faire. Le A-B testing, c’est pour plus tard. On utilise plutôt les principes de base de UX: diminuer la friction, organiser les éléments de navigation de manière logique, ou organiser les éléments visuels selon les principes de la Gestalt comme la proximité et la similarité. Demandez à votre mère de faire un achat sur votre site en disant tout ce qu’elle se dis elle-même dans son monologue intérieur. Utilisez GTMetrix pour obtenir des suggestions afin d’augmenter la rapidité de votre site. Organisez votre navigation à l’aide de la méthode des tris de carte. Présentez vos produits simplement et professionnellement, autant au niveau du texte que des images.

Une fois que cela est fait, mettez en place Google Analytics. Difficile de juger du retour sur investissement de vos campagnes si celles-ci ne sont pas mesurées. Configurez le Enhanced E-Commerce correctement afin de savoir le taux de conversion de vos campagnes. Mettez en place des Events personalisés selon vos objectifs, et liez des Goals à ceux-ci. Par exemple, vous pouvez avoir un Goal de cliquer sur le numéro de téléphone afin d’appeler votre entreprise, et assigner un valeur monétaire à celui-ci. Activez les Search Terms si votre site contient un moteur de recherche, afin de connaitre les désirs de consommateurs qui visitent votre site. Installez le pixel Facebook afin d’avoir la possibilité de créer des audiences en fonction du comportement des visiteurs sur votre site.

Enfin, ne laissez pas les visiteurs actuels de votre site partir dans l’oubli. Si ces derniers visitent votre site, c’est qu’ils sont intéressés par votre produit à court ou moyen terme. Mettez en place une campagne de remarketing via Facebook Ads (les dynamic product ads fonctionnent très bien) ainsi qu’une campagne de remarketing Google Display. La première servira à inciter à l’action, alors que la deuxième servira à rester top of mind et familier par le concept de mere exposure. Le ROI sur Facebook est très important et devrait être élevé. Le ROI sur Google Display l’est moins car son objectif est différent.

Une fois ces trois tâches accomplies, vous êtes prêts à passer à la deuxième étape.

Étape 1 : On part à la guerre

Étape 1 : Rejoindre les consommateurs prêts à acheter maintenant (Action)

Votre site est en ordre, les analytiques sont mise en place et vous ne laissez pas partir vos visiteurs sans un petit rappel de votre existence sur les autres sites qu’ils visitent. Bien ! Afin d’obtenir des résultats à court terme, vous êtes prêts à cibler les gens qui veulent répondre à leur besoin immédiat par votre extraordinaire produit. Ici, c’est principalement une campagne de Search Ads et/ou Shopping sur Google (ou sur Bing, sur Amazon, sur Etsy, sur eBay ! Ca dépends de la cible 😉) qui sera efficace.

L’idée est de convaincre les gens à l’étape Action que vous êtes le meilleur fournisseur afin de répondre à leur besoin : vous avez le meilleur produit et le plus faible risque de déception. Vous démontrez que vous avez le meilleur produit par une excellente description et des photos professionnelles, des commentaires d’acheteurs précédents, ou l’aval d’experts dans le domaine. Vous diminuez le risque perçu en offrant la livraison gratuite ou à faible coût, des retours faciles et une livraison rapide.

Étape 2 : On explore notre terrain de jeu

Étape 2 : Rejoindre les consommateurs à l’étape de la recherche d’informations dans votre catégorie de produits (Desire)

C’est le moment de se démarquer des compétiteurs comme étant la marque connaissant le mieux son sujet. Une stratégie de marketing de contenu et de SEO/SEM vous permettra de répondre aux questions des clients qui ne sont pas encore prêts à acheter, mais qu’y s’y préparent en recherchant des informations. On ne s’attends pas nécessairement à un grand ROI à cette étape, mais plutôt à élargir notre bassin de consommateurs à l’étape Action. En effet, une fois qu’ils sont venus sur votre site, vous serez en mesure de les recibler jusqu’à ce qu’ils passent à l’étape de l’achat. Comme le reciblage est toujours limité en fonction du nombre de visiteurs sur votre site, il faut donc trouver le moyen d’augmenter le nombre de visiteurs qualifiés.

Pensez aux questions que les clients en étape de recherche d’information se posent et, si ces questions semblent avoir de bons volumes de recherche, répondez-y sur votre blogue. Faites des comparatifs des attributs de votre produit à celui des concurrents, et faites une campagne SEM ciblant les mots-clés de la marque des concurrents. Créez des tutoriels expliquant comment améliorer son expérience avec votre catégorie de produit. Faites tout ce qu’il faut pour démontrer votre expertise !

Étape 3: On construit notre village

Étape 3 : Rejoindre les consommateurs qui évaluent différentes catégories de produits afin de répondre à leur besoin (Interest)

C’est l’étape de l’évaluation des alternatives et de la valeur attendue. Un consommateur à cette étape se demande quel moyen il va utiliser afin de répondre à son besoin. Un exemple simple est une personne désirant se déplacer de Montréal à Québec. À cette étape, il se demande s’il devrait prendre sa voiture, l’autobus, l’avion ou le train. Si vous êtes un transporteur aérien, il faut donc convaincre les gens que votre alternative est la meilleure parmi les options possibles pour répondre au besoin de voyager de Montréal vers Québec. Pour une campagne vraiment géniale sur cette étape, jetez un œil à la campagne de Via Rail réalisée par Touché ! En comparaison, s’il avait été à l’étape 2, le choix de prendre l’autobus étant déjà été pris, l’objectif du consommateur est plutôt de trouver la meilleure compagnie d’autobus, la moins chère, la plus confortable, la plus fiable, parmi la catégorie des transports par autobus.

C’est une étape tricky à cibler, qui demande souvent plus de moyens qu’une seule entreprise possède. C’est pourquoi certains se regroupent afin d’augmenter leur puissance de frappe dans leur catégorie : Le Lait est un excellent exemple. L’objectif ici est de favoriser la consommation de lait par rapport aux autres alternatives de boissons. Allez voir leur site : on y vante les vertus du lait pour des dents saines, pour une peau en santé, pour de bon os, etc. Tout cela dans l’espoir que, lorsque présenté devant l’option de boire un verre de lait ou un verre de Coke, les gens feront le choix du lait.

Pensez aux joueurs dans votre catégorie. Est-ce que ceux-ci seraient ouverts à joindre leurs forces aux vôtres afin de créer un regroupement pour favoriser la consommation de votre catégorie par rapport à une autre ? Il n’est pas inaccessible de créer un microsite comme celui proposé par Le Lait. Il est aussi pertinent de réfléchir à la manière d’intégrer des activités de marketing traditionnels à votre stratégie de marketing numérique, comme une campagne de relations publiques, des événements, un festival, etc. Je peux vous garantir qu’il n’y a rien de mieux pour le SEO que d’être l’organisateur d’un événement à grand déploiement, et de mettre les efforts en relations publiques pour que les grands médias en parlent. Amenez-en, des backlinks avec des DA de 90+ !

Étape 4 : On étend notre empire

Étape 4 : Devenez la marque la plus reconnue et la plus familière dans votre catégorie (Awareness)

Ici, on commence à sortir pas mal des stratégies purement numériques. Demandez-vous quelle serait la meilleure manière de faire en sorte que, parmi les noms/logos de tous vos compétiteurs, le vôtre soit instantanément reliée à votre catégorie de produit. Encore plus difficile, imaginez les actions que vous auriez à prendre afin qu’une personne, lorsqu’on lui demande un exemple de marque dans une catégorie, la vôtre soit prépondérante. C’est la notion de rappel assisté ou non assisté, et probablement la meilleure métrique à utiliser à ce niveau. Au niveau numérique, une métrique comme le nombre de recherches organiques branded (contenant votre marque) additionnée au traffic direct peut être un bon indicateur d’une campagne d’Awareness qui fonctionne.

C’est ici qu’on ouvre les gros canons : on veut rejoindre le plus de gens possible, au plus faible CPM possible, le plus souvent possible sans déranger. Sur ce dernier point, le sujet de la fréquence optimale est vaste et, s’il vous intéresse, je vous conseille cet article très accessible de Gerard J. Tellis dans le Journal of Advertising Research[i].  Les canaux numériques peuvent être utiles ici : on peut penser à des campagnes de reach au CPM sur Facebook, des takeover de pages d’accueil de grands médias, des pre-roll sur les sites de streaming comme tou.tv, ou encore l’achat programmatique de publicités. On peut même penser à des campagnes… oserai-je le dire… virales ! J’adore la campagne de Québec Original Un voyage jamais vu qui était 100% numérique. On est clairement dans le branding touristique, avec comme objectif de faire connaitre le Québec comme une destination possible dans le panier d’alternatives des chercheurs de voyages.

Toutefois, les canaux traditionnels sont presque toujours utilisés à cette étape : tout le monde n’est pas sur Facebook (hé non !). Considérant les coûts énormes engendrés par ces stratégies, il est super important d’être créatif dans notre approche. SVP il faut que quelqu’un dise à Corbeil Électroménagers d’arrêter de dépenser des centaines de milliers de dollars dans des billboards poches. Soyez créatifs, fous, sortez des idées éclatées ou engagez des gens qui en ont. Un bon exemple de campagne traditionnelle qui a misé sur la créativité et le punch est la campagne de Valérie Plante « L’homme de la situation » réalisé par upperkut. Avec moins de moyens que son rival, elle a réussi à passer de pure inconnue à mairesse de Montréal en quelques mois. Tout un accomplissement pour la créativité !

Est-ce que je dois faire toutes les étapes ?

Évidemment que non. Il y a plein de PME qui bénéficieraient énormément de faire uniquement l’étape 0 et 1, sans jamais aller plus loin. En fonction de la grosseur du marché, du type d’entreprise, de sa catégorie de produit, il est peut-être plus efficace de faire seulement ces étapes, et de se concentrer sur d’autre chose par la suite comme le développement de nouveaux produits, l’ouverture de magasins physiques, l’optimisation de ses opérations, la recherche d’investisseurs, etc. Il y a toujours une infinité de choses à faire, et aucune n’est mauvaise ! Il faut seulement réfléchir au coût d’opportunité de chacune de nos actions : si je fais une chose, je n’en fais pas une autre. Il faut parfois prendre un pas en arrière et se poser la question : est-ce que je suis en train de faire la bonne chose pour maximiser le retour sur mon investissement d’effort et d’argent ? Si la réponse est oui, vous êtes sur la bonne voie !

Est-ce que cela veut aussi dire qu’une stratégie ciblant directement l’étape 4 sans faire les autres étapes sera nécéssairement infructueuse? Évidemment que non. Toutefois, à performance égale, une campagne de masse sera plus efficace si les étapes précédentes ont été implantées. Prenons l’exemple d’une entreprise qui a la chance de passer aux Dragons (1M de téléspectateurs) et qui réussi à convaincre un dixième de pourcent de ces téléspectateurs à visiter son site (10 000 personnes). Si son taux de conversion est de 3%, il peut espérer faire 300 ventes à peu près sur le champ. S’il n’a pas mis les autres étapes en place, c’est tout ce qu’il obtiendra. Toutefois, s’il est passé à travers les autres étapes, il viendra de créer une audience de 10 000 personnes intéressées à son produit qu’il pourra recibler par la suite à l’étape 0. Pour ceux ne visitant pas le site sur le champ, il est probable qu’ils chercheront le produit (étape 1) ou de l’information sur le produit (étape 2) dans les jours suivants, ce qui augmentera les possibilités de vente à ces étapes. Enfin, il aura aussi montré la pertinence de sa catégorie par rapport à d’autres afin de répondre à un besoin, ce qui augmentera les ventes provenant de l’étape 3. C’est pourquoi c’est souvent le branle-bas-de-combat lorsqu’une entreprise se prépare à passer à cette émission, il ne faut pas manquer sa shot!

Et vous, quelle est l’ordre dans lequel vous priorisez vos actions ? Avez-vous un autre framework que le miens ? Je suis curieux de connaitre votre opinion !


[i] Tellis, Gerard J., Effective Frequency: One Exposure or Three Factors?. Journal of Advertising Research, pp. 75-80, July-August 1997. Available at SSRN: https://ssrn.com/abstract=906019

Mailchimp

Les alternatives à Mailchimp – Mai 2019

Maintenant que Mailchimp a annoncé la fin du lien avec Shopify, ainsi que son pivot stratégique vers une plate-forme complète de CRM, plusieurs PME se demandent s’il est pertinent de trouver un nouveau logiciel de newsletter, après avoir passé tout ce temps à apprendre comment utiliser Mailchimp. Évidemment, ces derniers comptent sur l’inertie de leurs utilisateurs actuels. Toutefois, malgré mon amour profond pour Mailchimp après l’avoir utilisé des années, il est pertinent de réévaluer s’il est toujours le bon outil pour nous, considérant les changements fondamentaux de la plate-forme.

Je commencerai d’abord par expliquer en détails les changements sur la plateforme Mailchimp. Puis, je proposerai différentes options pour vous aider à prendre la meilleure décision pour votre situation. Puis, je terminerai par mon choix chouchou. Afin de faire des comparaisons de prix, je prendrai l’exemple d’une entreprise qui possède une liste de 10 000 abonnés, et qui envoie un courriel par semaine à sa liste. Les conversions $USDà$CAD sont en date du 27 mai 2019.

Les changements à la plateforme Mailchimp

Est-ce la réaction des gens chez Mailchimp?

Le premier changement a été la fin de la connexion native entre Mailchimp et Shopify afin de synchroniser les informations d’achats des clients pour analyser la performance, d’envoyer des courriels aux clients qui abandonnent leur panier, d’envoyer des notifications de suivi de commande par courriel, et d’avoir accès aux produits de notre boutique dans l’éditeur de courriels. La raison de cette séparation provient du refus de Mailchimp à la demande de Shopify de partager les informations de sa plateforme vers la leur. Difficile de dire qui est fautif dans cette histoire : je vous laisse juger par vous-même avec la déclaration de Mailchimp et la déclaration de Shopify. Malgré cette guéguerre, il y a heureusement une solution gratuite à ce problème avec ShopSync, qui permet de conserver l’intégration. Problème réglé !

Le deuxième changement, plus majeur, est le pivot de Mailchimp d’une plateforme d’envoi de courriels à une plateforme de CRM complète. La première étape de ce pivot est la restructuration de leurs échelles de prix : un contact est maintenant compté, qu’il soit abonné ou non à l’envoi de courriels. Cela implique qu’il faut additionner votre liste de clients non abonnés, à votre liste de courriels abonnés, à votre liste de courriels désinscrits. Une liste de 10 000 abonnés peut ainsi gonfler de façon importante (j’en ai vu quadrupler !), surtout si vous avez beaucoup d’activité d’acquisition de courriels, ce qui implique un plus gros churn qu’une acquisition organique. La logique derrière ce changement est qu’il est possible de communiquer avec les membres d’une audience par d’autres moyens via Mailchimp, comme les audiences de retargeting Google et Facebook, par exemple. Toutefois, il s’agissait de fonctionnalités qui existaient déjà auparavant. Il est donc un peu étrange, à mon avis, de changer complètement sa structure de prix sans créer de nouvelles fonctionnalités. 

Heureusement, pour les clients existants, la structure de prix reste la même selon un système de « Legacy Pricing ». La nouvelle structure de prix, beaucoup plus complexe qu’avant, s’applique à tous les nouveaux comptes et à tous les comptes inactifs depuis plus de 90 jours. En plus du gonflement dû à la nouvelle manière de compter un contact, le nombre de courriels envoyés par mois n’est plus illimitée non plus – il est maintenant ajusté en fonction du plan choisi. La limite est de 10 courriels par mois par contact pour le plan « Essentials », 12 courriels par mois par contact pour le plan « Standard » et 15 courriels par mois par contact pour le plan « Premium ». Quelqu’un qui roule beaucoup d’automatisations pourrait ainsi avoir à payer encore plus que le montant mensuel prévu, car le dépassement de la limite implique des frais supplémentaires. Les fonctionnalités sont aussi maintenant restreintes en fonction du niveau de service choisi : il n’est même plus possible de créer un template personnalisé ou des automatisations avec le plan « Essentials ».

À mon avis, considérant tous ces changements, Mailchimp n’est plus une plateforme attrayante pour un nouveau compte. Heureusement, le « Legacy Pricing » fait en sorte que les clients existants n’ont pas besoin de fuir au plus vite, mais qui sait si ce plan sera changé ou retiré dans le futur ? Je vous présente donc maintenant les différentes options disponibles, en fonction de différentes situations.

Option 1 : Statu quo en ajoutant ShopSync pour Mailchimp

ShopSync et Shopify

Quelqu’un ayant investi beaucoup de temps dans son workflow Mailchimp, qui n’a pas les resources humaines ou monétaires pour changer de plateforme, et qui est satisfait des fonctionnalités existantes, pourrait décider de rester sur Mailchimp. L’intégration avec ShopSync fonctionne bien, et le prix reste le même. Parfois, la réalité est qu’on a bien d’autres chats à fouetter : don’t fix what’s not broken. Toutefois, je conseillerais à une entreprise qui fait ce choix de rester à l’affût des « vraies » solutions de CRM comme Hubspot, et de planifier une intégration complète de ses activités de marketing dans ce genre de plateforme dans le futur, pour améliorer ses capacités et son efficacité.

Avantages

  • Nécessite peu d’efforts (15 minutes de réintégration)
  • Pas besoin d’apprendre un nouveau logiciel
  • La configuration existante est conservée

Inconvénients

  • Rester à la merci d’un futur changement aux tarifs
  • Pas d’amélioration des capacités de marketing
  • Pas de diminution des coûts

Prix

Pour une liste de 10 000 abonnés et un envoi par semaine

  • Legacy Pricing : 101$CAD/mois
  • Standard Plan : 133$CAD/mois

Option 2 : Diminuer les coûts avec Sendy et BeeFree

Interface purement utilitaire! Le composeur visuel est très peu sophistiqué, mais BeeFree est à la rescousse.

Sendy et son complément BeeFree sont les outils parfaits pour une entreprise qui désire avoir des coûts minuscules, qui a la capacité technique d’installer une application auto-hébergée, et qui a seulement besoin d’une application d’envoi de courriels plutôt qu’un CRM complet. À la place d’un modèle SaaS (Software-as-a-service), Sendy vends une licence d’utilisation pour une installation sur un serveur au coût unique de 59$USD, et agit ensuite comme back-end vers le service Amazon SES (Simple Email Service) qui a une structure tarifaire par courriel envoyé : 1$USD par 10 000 courriels envoyés. La faiblesse de Sendy est que son éditeur visuel n’est pas très performant, ce qui est réglé avec BeeFree, la meilleure plateforme de création de courriels sur le marché. En plus, il existe une intégration gratuite afin de lier les deux logiciels. Enfin, il faut lier Sendy et Shopify, ce qu’il est possible de faire avec Zapier (payant si gros volume) ou avec un script gratuit qu’on héberge soit-même – assez simple à faire sur Google App Engine par exemple.

Avantages

  • Prix très, très bas
  • Possibilité de personnaliser le back-end selon ses besoins
  • Prix très bas (est-ce que j’ai dis que c’était pas cher ?)

Inconvénients

  • Interface un peu vieillotte
  • Pas de A/B testing
  • Automatisation limitée (Courriels de bienvenue seulement)
  • Il faut gérer notre hébergement pour le back-end

Prix

Pour une liste de 10 000 abonnés et un envoi par semaine

  • Frais unique de 79$CAD
  • 6$CAD/mois (No joke!!!)

Option 3 : Augmenter la sophistication de nos campagnes courriel avec Omnisend

L’interface d’Omnisend pour choisir l’objectif de notre automatisation

Évidemment, l’autre solution est de migrer vers une nouvelle plateforme hébergée dans le nuage. Il y en a des dizaines, en voici une liste très incomplète :

Toutefois, mon choix personnel pour un marchand qui serait sur Shopify et qui cherche à migrer de Mailchimp vers un autre service serait Omnisend. C’est clairement la plateforme qui s’intègre le plus facilement avec Shopify : on aime les one-click install qui importe le opt-in de courriel et de SMS! Dans sa version « Lite », à un prix similaire à Mailchimp, elle offre plus de fonctionnalités que Mailchimp comme l’envoi de SMS et un constructeur d’automatisations visuel. Elle offre aussi des plans plus avancés qui permettent de faire du marketing par SMS, Messenger, WhatsApp ou push notifications, et de créer des audiences personalisées Facebook et Google Ads. En plus, plusieurs template d’automatisations sont déjà batis en fonction de l’objectif qu’on désire atteindre, par exemple, créer une série de courriels de bienvenue, une série de courriels de paniers abandonnés, faire du cross-selling, ou réactiver des abonnés. Encore mieux, elle offre un service de migration gratuit pour les marchands et un rabais de 50% pendant 3 mois et 20% pendant un an. Très agressif comme offre ! Sinon, si vous voulez le faire vous-même, c’est très simple de mettre votre clé API Mailchimp et d’importer vos listes. Maintenant, si vous décidez de prendre cette option, vous devriez quand même jeter un œil aux autres logiciels avant de faire votre choix, mais le miens s’est arrêté sur Omnisend. J’ai été particulièrement impressionné par son deliverability : j’utilise Zoho qui est très très agressif dans le classement automatique des infolettres dans des dossiers, et les courriels provenant d’Omnisend atterrissent dans mon Inbox !

Avantages

  • Sophistiqué mais simple d’utilisation
  • Intégration parfaite avec Shopify, autant au niveau des clients que des produits
  • Permet d’ajouter des fonctionnalités en fonction de nos besoins
  • Les triggers dans les automatisations peuvent être en fonction du comportement des contacts sur notre site, par pages ou produits vus, et même par custom event !

Inconvénients

  • Les fonctionnalités supplémentaires comme les push notifications, l’intégration à Messenger et les audiences Facebook et Google Ads sont à frais fixe, ce qui les rends un peu chères pour une petite liste.
  • Pas de workflow conditionnel (Si c’est un must, regardez du côté de Klaviyo ou ActiveCampaign)

Prix

Pour une liste de 10 000 abonnés, un envoi par semaine, un paiement annuel et le forfait « Lite »

  • 55$CAD/mois les trois premiers mois
  • 88$CAD/mois les neuf mois suivants
  • 110$CAD/mois par la suite

Voilà, j’espère que ce petit tour d’horizon vous sera utile ! Et vous, quelle est votre solution d’automatisation de marketing par courriel ?

TL :DR

  Mailchimp Sendy + BeeFree Omnisend
Sophistication 6/10 4/10 9/10
Facilité d’utilisation 8/10 5/10 10/10
Prix (10k user) 101-133$CAD 6$/mois (!!!) 110$
Intégrations 200 et + via API Via API seulement 30 et + via API
Qualité de l’éditeur visuel 6/10 10/10 (BeeFree) 0/10 (Sendy) 10/10
Deliverability 9/10 9/10 10/10
Automatisations 7/10 4/10 9/10
Intégration native avec Shopify Oui avec ShopSync (Contacts et product views) Oui (Contacts seulement) Oui (Contacts et comportement des visiteurs)

Web à Québec 2019 – Jour 2 et 3

Une deuxième journée où j’ai assisté à moins de conférences, car je devais présenter la mienne! Mais un jour 3 fantastique avec des conférenciers de calibre mondial vraiment inspirants.

Martin Boucher et Marie-Anne Courchesne

Quelles sont les retombées concrètes de la stratégie de communication numérique du Québec?

On entre dans la modernité

Une conférence qui début avec une grande question: comment passer de 800 sites distincts à un seul site unifié? La réponse… Un à la fois! À l’aide d’une approche itérative, la petite équipe web de 20 personnes du gouvernement du Québec a développé la première version de quebec.ca, en six mois, entièrement à l’interne. Pour un site v1 avec 500 pages de contenu, c’est tout un accomplissement pour notre cher gouvernement qu’on croit souvent bien lent! L’arme secrète? Une organisation du travail selon les principes agile.

Prochaine étape? Les 798 autres sites qui sont encore indépendants!

Faites un tour sur le nouveau site du gouvernement: https://www.quebec.ca/

Hugues Foltz

L’arrivée massive des technologies de rupture à l’ère du 4.0

Qu’est-ce qu’une innovation de rupture? Il s’agit d’une innovation qui a le pouvoir de renverser complètement les plus grands joueurs d’une industrie. Les exemples amenés sont multiples avec les classiques Tesla, Amazon, Uber et AirBnB, mais aussi d’autres moins connus comme Doctolib, en France, qui casse les barrières entre un patient et son médecin, ou Lemonade qui chambarde la vitesse à laquelle on peut reçevoir un paiement d’assurance.

Outre ces innovations, cette conférence a regardé très loin dans le futur, imaginant une société ou les humains peuvent se recentrer sur ce qu’ils font de mieux: la créativité, l’empathie et la gestion du flou. Selon lui, les emplois débilitants et répétitifs seront automatisés sans aucun doute, d’ici 10 à 20 ans.

Visitez le site de Vooban: https://vooban.com/

Colin Megil

AI & the future of the political party

Une toute nouvelle façon de réfléchir à la manière d’engager les citoyens dans le processus politique: voici la solution offerte par la plate-forme pol.is. À l’aide d’une stratégie d’apprentissage machine, il est maintenant possible de compresser les opinions qualitatives de milliers de personnes en un résumé qui est possible à comprendre pour nos élus. Déjà implanté à Taiwan, pol.is fait maintenant l’objet d’essais avec le gouvernement du Canada!

Visitez le site de pol.is: https://pol.is/

Simo Ahava

There’s data everywhere!

Simo, véritable superstar du web analytique et de GTM en particulier, nous a présenté sa vision d’une équipe d’analytiques qui est en mesure d’offrir de hautes performances: une équipe avec des membres ayant une expertise dans l’utilisation des outils, d’autres membres avec une expertise du marché dans lequel évolue l’entreprise, et une grande maturité de gens et de processus. C’est uniquement lorsque ces trois éléments se rejoignent dans une équipe que celle-ci travaillera ensemble à atteindre leurs buts partagés. Et la meilleure manière d’organiser le travail? Avec la méthode Scrum, même si celle-ci peut être malaisante au début!

Points bonis accordés pour la montée de lait contre le concept un peu arrièré d’une session dans Google Analytics.

Visitez le site de Simo Ahava: https://www.simoahava.com/

Tim Wilson

Statistics Intuition for the Marketer (and Why It Matters)

Probablement ma conférence chouchou des trois journées, Tim a réussi en 45 minutes à expliquer les principes fondamentaux de l’analyse statistique, en plus de présenter l’ANOVA, les analyses post-hoc, la régression linéaire et les séries temporelles. À mon avis, il s’agit d’un tour de force d’être en mesure d’expliquer ces méthodes d’une manière aussi claire et concise. Mais, outre ses explications limpides, il a même proposé un exemple d’application réelle de ces trois méthodes d’analyse statistique à des données web.

À mon avis, les connaissances en statistiques sont une lacune généralisée en marketing numérique, que ce soit au niveau des pratiques ou des outils. Espérons que cette présentation amorçe un petit vent de changement dans l’industrie!

Le profil LinkedIn de Tim Wilson: https://www.linkedin.com/in/tgwilson/

Dina Zielinski

A future for digital data in nature’s oldest storage device

ÇA c’est du Big Data!

Saviez-vous qu’il était possible de stocker des données numériques dans de l’ADN? Moi non plus! Avec une toute première étude sur le sujet en 1988, ce n’est que depuis 2007 que des recherches sérieuses sont entreprises sur cette méthode originale d’entreposer des données. Loin d’être prête à une utilisation par les consommateurs à cause de son prix élevé, 9000$US pour encoder et décoder 2Mo de données, une utilisation future pourrait être l’archivage des données les plus primordiales de l’humanité en raison de sa durée de vie de plus de 1000 ans dans des conditions d’archivage simples, et de sa densité d’information inégalée.

Visitez le profil LinkedIn de Dina Zielinski: https://www.linkedin.com/in/dinazielinski/

Jean-François Renaud

Budget optimal en marketing numérique : utiliser les données

On revient sur terre après la conférence sur le stockage des données dans l’ADN avec un sujet tout ce qu’il y a de plus plate (le principal intéressé l’a qualifié comme tel) mais tellement important: combien de dollars dépenser en marketing numérique. À l’aide d’une nouvelle méthode dévelopée chez Adviso, le Delta Opportunity Index (DOI), il est maintenant possible d’ajuster les budgets publicitaires en fonction de la demande pour les produits et services offerts. Pourquoi vendre des motoneiges uniquement lors d’une seule campagne en novembre, alors qu’il y a de l’intérêt pour celles-ci à des niveaux différents toute l’année? On comprends à quel point Jean-François adore le always-on et n’aime pas vraiment les campagnes. Mais à voir le succès qu’il obtient pour ses clients, on comprends qu’il a un peu raison!

Visitez le profil LinkedIn de Jean-François: https://www.linkedin.com/in/jeanfrancoisrenaud/

Lisez l’article scientifique sur la méthodologie DOI:
https://lp.adviso.ca/doi-waq2019

Rémy Savard

Gatsby – Performances irréelles, sécurité accrue et meilleure expérience de développement Web

Je ne suis pas développeur web, mais cette conférence a piqué ma curiosité car elle présente une des nombreuses technologies permettant de déployer un site sans serveur, hébergé uniquement sur des CDN, ce qui les rends tellements rapides que ça en est effectivement un peu irréel. Ce que j’ai retenu de cette présentation est qu’il est présentement possible d’utiliser cette technologie tout en conservant le CMS auquel nous sommes habitués, comme WordPress, Magento ou Shopify. Considérant la performance hallucinante qu’il est possible d’atteindre, ça vaut la peine de faire un peu plus de recherches, particulièrement pour les sites e-commerce où chaque seconde de chargement diminue le taux de conversion. Pour voir un site e-commerce de plus de 50 000 pages utilisant cette technologie, jetez un oeil ici, et attachez votre tuque avec du duct tape avant:
https://www.danielwellington.com/

Pour le profil LinkedIn de Rémy Savard: https://www.linkedin.com/in/remysavard

Vitaly Friedman

Bringing Personality Back To The Web

En 2019, les sites web modernes n’ont plus de personalité. Enfermés dans les codes stylistiques créés par le responsive et les frameworks de type bootstrap, et la diminution de la friction à tout prix, nous avons perdu ce qui était original, surprenant et mémorable du web d’autrefois. Il nous dis même qu’il s’ennuie du Flash Player! Toutefois, tout n’est pas perdu, car plusieurs exemples de sites webs modernes mémorables sont mis de l’avant:

http://dada-data.net – On ne comprends pas tout à fait l’objectif du site, mais c’est une véritable oeuvre d’art numérique.

https://www.sbs.com.au/theboat/ – Une bande dessinée numérique absolument fabuleuse.

https://www.bloomberg.com/features/elon-musk-goals/ – Ce qui arrive quand on donne 100% de la liberté créative aux designers.

J’ai trouvé cette présentation absolument superbe pour conclure une congrès sur le numérique, car elle était complètement éclatée et donnait envie de faire les choses différement! Et ne s’agit-il pas la de la raison d’être d’un congrès? Apprendre de nouvelles choses, et faire les choses mieux, mais aussi différentes des autres!

Visitez le site de Vitaly Friedman, Smashing Magazine:https://www.smashingmagazine.com/

Web à Québec – Jour 1

Je vous offre un aperçu des conférences auxquelles j’ai eu la chance d’assister lors de la première journée du Web à Québec!

Aaron Draplin – Draplin Design Co

Une créativité complètement éclatée.

Le fondateur de Draplin Design Co, Aaron Draplin, nous as parlé de sa vie et de la manière qu’il s’y est pris afin d’atteindre le style de vie qu’il désire d’une manière très éloquente.

Son style de design éclaté, très pop art, a été utilisé dans des projets du gouvernement des États-Unis, par la campagne de Bernie Sanders et par la Nasa.

Jetez un oeil au site web de Draplin Design Co.

Ross Simmonds – Content isn’t King anymore

Quelques exemples de plate-formes de distribution.

Nous avons eu droit à une démonstration expliquant pourquoi le contenu n’est plus roi, la distribution l’est! Des changements d’algorithmes continuels aux featured snippets qui gardent les visiteurs dans l’écosystème de Google, Ross démontre qu’il est risqué de garder tous ses oeufs dans le même panier et qu’il est pertinent de diversifier sa distribution. Il dit ainsi que Content isn’t King anymore, distribution is. Il suggère de trouver la communauté intéressée par notre sujet, de créer un contenu pertinent pour elle, et de la diffuser dans cette communauté.

Visitez le site de Ross Simmods: https://rosssimmonds.com/

Tin Kadoic – AirBnB

Designing designers time

C’est occupé, un designer.

Le titre de “designer” prends un tout autre sens de nos jours, et est plus large que celui de designer graphique, web, ou UI/UX. Tin Kadoic nous explique qu’un designer doit prendre en main sa carrière comme il designerait un produit, et qu’il doit être intentionel dans les aptitudes qu’il apprends. Il faut apprendre les éléments qui sont pertinents pour se rendre la où l’on veut aller.

Il as aussi fait un détour par sa vision d’un bon gestionnaire, en expliquant la différence entre un “Talker-Talker”, un “Talker-Maker” et un “Maker-Maker”. Il dit que les meilleurs gestionnaires sont les meilleurs contributeurs qui sont forcés de devenir gestionnaires afin d’arriver à leur fins, donc des “Makers-Makers” qui deviennent “Talker-Makers”. Il n’avait pas beaucoup d’éloges pour les “Talkers-Talkers”, toutefois!

Visitez le site web de Tin Kadoic: https://blackduke.com/

Daria Hvizdalova

AI != Robotics: Backend Enablement

Une conférence fascinante par cette ingénieure venue tout droit de la République Tchèque, elle a fait la différence entre l’automatisation des processus humains (Robotic Process Automation – RPA), possible entièrement par software, et l’automatisation industrielle, alliant software et hardware afin de remplacer l’humain dans des tâches trop dangereuses ou dans lesquelles l’humain est inneficient. Surprenament, les processus d’automatisation industrielle sont généralement de niveau 1, c’est à dire que les robots sont uniquements programmés afin de définir une tâche très spécifique, sans capacité d’adaptation ou de capteurs leur permettant de changer leur comportement en fonction de l’environnement. Elle a fait un mention des cobots, ces robots cohabitants avec les humains dans un cadre industriel, mais que ceux-ci n’étaient pas encore très efficaces.

Elle a aussi présenté les différences entre un narrow AI, capable de faire une seule tâche (ex. jouer aux échecs), et un broad AI, capable de raisonner et d’apprendre par lui-même. Nous sommes encore loin du broad AI!

Visitez la page LinkedIn de Daria: https://www.linkedin.com/in/dariahvizdalova

Lea Pica

The PICA Protocol™: Your Prescription for Healthy, Actionable Data Storytelling

Il ne faut pas juste vomir des buzzwords

Lea Pica est une sommitée dans le monde du data storytelling. Elle nous as présenté son protocole de détox des visualisations en entreprise à travers son protocole PICA: Purpose, Insights, Context, Aesthetics. Pour avoir de l’impact, une visualisation doit avoir un but précis, répondre à une question d’affaires. Elle doit amener un insight, un angle qui n’est pas visible immédiatement à partir des données. Celle-ci doit être analysée dans son contexte, c’est-à-dire qu’il faut vérifier s’il existe d’autres sources de données qui pourraient confirmer ou infirmer ce qui est présenté. Enfin, elle doit être présenté selon des critères d’esthétismes bien précis, afin que le message ne soit pas perdu dans le medium.

Visitez le site web de Lea Pica:http://leapica.com/

Préparation des données de l’article sur le tableau de bord automatisé

Jean-Philippe Allard

6 avril, 2019

Provenance des données

Pour la boutique en ligne, j’ai utilisé les données de l’année 2017 provenant du Google Merchandise Store, que j’ai manuellement exportées en fichiers csv. Une meilleure méthode serait d’utiliser une des nombreuses librairies permettant d’accéder au Analytics Reporting API. Toutefois, le demo account de Google Analytics ne permet pas l’accès par API, donc tous les fichiers requis ont été exportés à la mitaine.

Pour la boutique physique, j’ai créé un fichier d’exemple en modifiant extensivement des données d’un marchand du Royaume-Uni. Les données originales sont disponibles sur Kaggle.

J’ai aussi créé une page Kaggle pour les fichiers finaux de cette page.

Début du projet

Nous avons tout d’abord besoin des librairies suivantes:

  • tidyverse, une librairie de librairies contenant tout ce qu’on a besoin pour le data wrangling.
  • readr, une librairie plus intuitive et rapide pour lire les fichiers csv.
#Loading Packages
library(tidyverse)
library(readr)

La première étape est d’importer les données. Ici à partir d’un fichier local, mais il est aussi possible de faire des requêtes http à un fichier sur un serveur, ou des GET requests à un API avec la librairie httr. Par défaut, read_csv ne va pas parser des colonnes contenant des lettres en tant que colonne de type numérique, afin d’éviter de perdre des données. Il faut ainsi le demander formellement pour enlever les signes de $ ou de %.

#Importer les données
Online_original <- read_csv("All_Transactions_SKU.csv", 
    skip = 6)
KEY_TransactionID_Date <- read_csv("KEY_TransactionID_Date.csv")
KEY_SKU_ProductName <- read_csv("KEY_SKU_ProductName_GOOGLE.csv", 
    skip = 6)
KEY_SKU_Category <- read_csv("KEY_SKU_Category_Google.csv", 
    skip = 6)
#Parser les colonnes avec des $ ou des %
Online_original$"Product Revenue" <- parse_number(Online_original$"Product Revenue")
Online_original$"Avg. Price" <- parse_number(Online_original$"Avg. Price")

KEY_SKU_Category$"Product Revenue" <- parse_number(KEY_SKU_Category$"Product Revenue")
KEY_SKU_Category$"Avg. Price" <- parse_number(KEY_SKU_Category$"Avg. Price")
KEY_SKU_Category$"Product Refund Amount" <- parse_number(KEY_SKU_Category$"Product Refund Amount")
KEY_SKU_Category$"Basket-to-Detail Rate" <- parse_number(KEY_SKU_Category$"Basket-to-Detail Rate")
KEY_SKU_Category$"Buy-to-Detail Rate" <- parse_number(KEY_SKU_Category$"Buy-to-Detail Rate")

KEY_SKU_ProductName$"Product Revenue" <- parse_number(KEY_SKU_ProductName$"Product Revenue")
KEY_SKU_ProductName$"Avg. Price" <- parse_number(KEY_SKU_ProductName$"Avg. Price")
KEY_SKU_ProductName$"Product Refund Amount" <- parse_number(KEY_SKU_ProductName$"Product Refund Amount")
KEY_SKU_ProductName$"Basket-to-Detail Rate" <- parse_number(KEY_SKU_ProductName$"Basket-to-Detail Rate")
KEY_SKU_ProductName$"Buy-to-Detail Rate" <- parse_number(KEY_SKU_ProductName$"Buy-to-Detail Rate")

KEY_TransactionID_Date$"Revenue" <- parse_number(KEY_TransactionID_Date$"Revenue")
KEY_TransactionID_Date$"Tax" <- parse_number(KEY_TransactionID_Date$"Tax")
KEY_TransactionID_Date$"Delivery" <- parse_number(KEY_TransactionID_Date$"Delivery")
KEY_TransactionID_Date$"Refund Amount" <- parse_number(KEY_TransactionID_Date$"Refund Amount")

Enfin, nous allons filtrer les données des différents fichiers importés afin de conserver un seul type de catégories (il y en avait trois qui se dupliquaients), des Transaction ID uniques pour les dates et des Product SKU uniques pour les informations sur les produits. Puis, nous allons joindre toutes les tables en une seule table contenant toutes les informations, et enfin, retirer les informations qui ne sont pas pertinentes.

#Filtrer les données de la table catégorie pour conserver uniquement un type de catégorie
KEY_SKU_Category <- filter(KEY_SKU_Category, 
                          `Product Category (Enhanced E-commerce)` == "Accessories"|
                          `Product Category (Enhanced E-commerce)` == "Android"|
                          `Product Category (Enhanced E-commerce)` == "Apparel"|
                          `Product Category (Enhanced E-commerce)` == "Backpacks"|
                          `Product Category (Enhanced E-commerce)` == "Bags"|
                          `Product Category (Enhanced E-commerce)` == "Bottles"|
                          `Product Category (Enhanced E-commerce)` == "Drinkware"|
                          `Product Category (Enhanced E-commerce)` == "Fun"|
                          `Product Category (Enhanced E-commerce)` == "Gift Cards"|
                          `Product Category (Enhanced E-commerce)` == "Google"|
                          `Product Category (Enhanced E-commerce)` == "Headgear"|
                          `Product Category (Enhanced E-commerce)` == "Housewares"|
                          `Product Category (Enhanced E-commerce)` == "Lifestyle"|
                          `Product Category (Enhanced E-commerce)` == "More Bags"|
                          `Product Category (Enhanced E-commerce)` == "Nest"|
                          `Product Category (Enhanced E-commerce)` == "Nest-Canada"|
                          `Product Category (Enhanced E-commerce)` == "Nest-USA"|
                          `Product Category (Enhanced E-commerce)` == "Notebooks & Journals"|
                          `Product Category (Enhanced E-commerce)` == "Office"|
                          `Product Category (Enhanced E-commerce)` == "Waze"
                          )

#Enlever les duplicatas de KEY_TransactionID_Date (Transaction ID 45426 et 33673, probablement des modifications manuelles à des commandes eronnées)
KEY_TransactionID_Date <- distinct(KEY_TransactionID_Date, `Transaction ID`, .keep_all = TRUE)

#Enlever les duplicatas de KEY_SKU_ProductName
KEY_SKU_ProductName <- distinct(KEY_SKU_ProductName, `Product SKU`, .keep_all = TRUE)

#Enlever les duplicatas de KEY_SKU_Category
KEY_SKU_Category <- distinct(KEY_SKU_Category, `Product SKU`, .keep_all = TRUE)

#Joindre les données de toutes les tables
Online_joined <- left_join(Online_original, KEY_TransactionID_Date, by = "Transaction ID")
Online_joined <- left_join(Online_joined, KEY_SKU_ProductName, by = "Product SKU")
Online_joined <- left_join(Online_joined, KEY_SKU_Category, by = "Product SKU")

#Conserver uniquement les colonnes pertinentes et les réorganiser
Online <- select(Online_joined, "Transaction ID", "Date", "Product SKU", "Product", "Product Category (Enhanced E-commerce)", "Quantity.x", "Avg. Price.x", "Revenue", "Tax", "Delivery" )

#Renommer les colonnes avec un indicateur de doublon maintenant qu'il n'est plus nécéssaire
colnames(Online)[colnames(Online)=="Quantity.x"] <- "Quantity"
colnames(Online)[colnames(Online)=="Avg. Price.x"] <- "Avg. Price"

Voila notre fichier d’exemple de transactions d’une boutique en ligne fin prêt!

Création du fichier de magasin physique

Considérant que je rends publiques les données de cette exemple, je ne peux utiliser les données privées sur lesquelles je fais normalement ce genre de travail. J’ai donc décidé d’utiliser un fichier provenant de Kaggle, et de l’ajuster à la situation présente.

Nous allons tout d’abord lire notre fichier original “UK Retailer” provenant de Kaggle: https://www.kaggle.com/carrie1/ecommerce-data

#Lire le fichier original
Retail_original <- read_csv("UK retailer data.csv")
#Retirer les retours
Retail_clean <- Retail_original %>% filter(Quantity > 0)

Maintenant, nous allons modifier les dates d’achat afin d’obtenir uniquement des dates du 1ier janvier 2018 au 31 décembre 2018, comme dans notre fichier e-commerce

#Nous chargeons la librairie lubridate qui permet de mieux jouer avec les dates
library(lubridate)
#Convertir la colone de type caractères en type date, en enlevant l'information d'heure
Retail_clean$InvoiceDate <- as.Date(Retail_clean$InvoiceDate, format = "%m/%d/%Y")

#Ajouter 1 mois puis 6 ans, et retirer 1 journée à toutes les dates
Retail_clean$InvoiceDate <- Retail_clean$InvoiceDate %m+% months(1)
Retail_clean$InvoiceDate <- Retail_clean$InvoiceDate %m+% years(6)
Retail_clean$InvoiceDate <- Retail_clean$InvoiceDate %m-% days(1)

#Retirer tout ce qui n'est pas en 2017
Retail_clean <- Retail_clean %>% filter(InvoiceDate < "2018-01-01" & InvoiceDate > "2016-12-31")

Maintenant, nous allons simuler les quantités achetées et les stocker en attendant de les appliquer sur notre fichier.

#Visualiser les quantités achetées sur la boutique en ligne en retirant les outliers
freqs <- Online %>% group_by(Quantity) %>% tally()

#Enlever les outliers
freqs <- freqs %>% filter(Quantity <= 10)

#Visualiser
ggplot(data = freqs, aes(x = Quantity, y = n)) + 
  geom_point() +
  xlim(0, 10)
#Nous allons maintenant créer des chiffres aléatoires de quantité selon la distribution de la boutique en ligne pour nos données de magasin physique 

den <- density(Online$Quantity, na.rm = TRUE)

den_qty <- round(sample(Online$Quantity, 505882, replace=TRUE) + rnorm(505882, 0, den$bw), digits = 0)

den_qty <- as.data.frame(den_qty)

#Il faut remplacer les NA créés par l'arrondissement par 1...
den_qty <- den_qty %>% mutate(den_qty = if_else(is.na(den_qty), 1, den_qty))

#Voyons voir si la distribution semble similaire à l'original...
freqs2 <- den_qty %>% group_by(den_qty) %>% tally()

#Enlever les outliers
freqs2 <- freqs2 %>% filter(den_qty <= 10)

ggplot(data = freqs2, aes(x = den_qty, y = n)) + 
  geom_point() +
  xlim(0, 10)
#Ça semble le cas! 

Nous allons maintenant joindre les données pour notre fichier fictif en conservant uniquement les colonnes Date – Stock Code et Invoice Number, et en utilisant nos données précédentes pour les quantités achetées. On simule un fichier avec très peu d’informations, que nous enrichirons dans l’exemple.

#Création du fichier
Retail <- data.frame(InvoiceNo = Retail_clean$InvoiceNo, InvoiceDate = Retail_clean$InvoiceDate, StockCode = Retail_clean$StockCode, Quantity = den_qty$den_qty, stringsAsFactors = FALSE)

Enfin, nous allons réduire le nombre de SKU dans le fichier Retail afin qu’il soit égal au nombre de SKUS du fichier Online, ce qui nous permettra de créer la clé entre les deux fichiers dans l’exemple qui suivra.

#Compter le nombre de SKUs dans le fichier Online
nrow(as.data.frame(unique(Online$`Product SKU`)))
## [1] 1178
#1178 SKUs uniques. 

#Compter le nombre de SKUs dans le fichier Retail
nrow(as.data.frame(unique(Retail$StockCode)))
## [1] 3931
#3931 SKUs uniques. C'est trop!

#Il faut diminuer de près du trois quart le nombre de SKUs du fichier Retail, mais on ne veut pas trop diminuer le nombre de transactions. Nous allons donc commencer par un clean-up des SKUs qui ont des variations à l'aide d'une lettre pour conserver uniquement des SKUs numérique.

Retail$StockCode <- str_replace(Retail$StockCode, "[a-zA-Z]\\b$", "")

#Combien de SKUs maintenant? 
nrow(as.data.frame(unique(Retail$StockCode)))
## [1] 3309
#3309 SKUs. Encore trop. 

#Enlevons les SKUs qui sont des erreurs (BANKCHARGES, POS, etc.) en retirant aussi la transaction au complet. 

Retail <- Retail %>% filter(str_detect(StockCode, "\\d\\d\\d\\d\\d"))

#On peut maintenant parser la colonne comme numérique car il n'y a plus de lettres

Retail$StockCode <- parse_number(Retail$StockCode)

#Combien de SKUs maintenant? 
nrow(as.data.frame(unique(Retail$StockCode)))
## [1] 3288
#3290 SKUs. Nous allons retirer des commandes de façon arbitraire pour atteindre notre objectif (il faut se rappeler que c'est uniquement un fichier d'exemple!)

#Créons un tableau avec nos SKUs uniques, afin de voir quels sont les 1178 premiers SKUS
unique_retail_SKUS <- as.data.frame(sort(unique(Retail$StockCode)))

#Les 1178 premiers SKUs sont situés de 10002 à 22314. Nous allons exclure toutes les lignes comportant des achats de SKUs à l'extérieur de cette plage. 

Retail <- Retail %>% filter(StockCode <= 22314)

#Combien de SKUs maintenant? 
nrow(as.data.frame(unique(Retail$StockCode)))
## [1] 1178
#1178 SKUs. Nous avons atteint notre objectif! 

Voila! Nos deux fichiers sont maintenants prêts pour être utilisés dans l’exemple. Visitez le ici: Exemple de tableau de bord automatisé avec R et Google Compute Engine

Exemple de tableau de bord automatisé

Jean-Philippe Allard

6 avril 2019

Bienvenue sur cet article avec les détails techniques de ma conférence du Web à Québec. Nous allons coder ensemble afin d’éviter à Bob de passer des heures à chaque mois à faire les mêmes opérations dans Excel, afin de le libérer à faire des activités plus productives.

Contexte de l’exemple de code

Cet exemple est basé sur les données réelles du Google Merchandise Store Demo Account de 2017 ainsi que d’un fichier lourdement modifié provenant de Kaggle. Toutes les manipulations pour créer ces fichiers sont détaillées dans mon autre article sur la préparation des données dans R. Malheureusement, confidentialité oblige, c’était la seule manière de créer un exemple accessible au public, car il y a très peu de données transactionnelles réelles disponibles au public.

Rappel du mandat

Bob est un analyste d’affaire très travaillant. Outre ses activités régulières, il passe une journée par mois à recueillir des données, les nettoyer, les analyser et les mettre en page, afin de faire une présentation de 15 minutes aux dirigeants de l’entreprise pour laquelle il travaille, sur la performance de leur boutique en ligne et de leur boutique physique.

Cela fait deux ans qu’il présente les mêmes données de la même façon, ce qui implique qu’il a passé 192 heures (24 jours de 8h) à refaire les mêmes tâches répétitives afin de présenter des données durant 6 heures (24 présentations de 15 minutes). Bob pense qu’il doit y avoir un meilleur moyen d’utiliser son temps. Il a raison!

Solution

Bob décide qu’il va apprendre à utiliser le langage de programmation R sur Datacamp. Il planifie apprendre ce langage 1h par jour en se réveillant 1h plus tôt chaque matin. Il se dit qu’en un an au maximum, avec 250h de pratique (1h/jour, 5 jours par semaine, 50 semaines), il sera capable d’utiliser ce langage pour libérer son temps. Et il a raison.

Voici ce qu’il peut faire après 250h de pratique. Nous sommes le 1ier janvier 2018 et Bob décide d’utiliser le temps qu’il mettait auparavant à produire son rapport à la main, afin de créer des visualisations selon les meilleurs principes de la communication des données.

Importation et nettoyage des données

#Bob commence par charger les librairies qu'il a besoin. 

#Librairie readr pour charger ses fichiers rapidement et facilement.
library(readr)

#Librairie tidyverse, une librairie de librairies, permettant de mieux travailler avec le tidy data. 
library(tidyverse)

#Librairie lubridate qui permet de facilement travailler avec les dates.
library(lubridate)

#Librairie ggthemes, qui offre des préconfigurations de style de tableaux
library(ggthemes)

#Librairie permettant de contrôler l'affichage des chiffres sur une visualisation
library(scales)

#Librairie pour gérer les fontes
library(extrafont)

#Bob va aussi déterminer la localisation des dates, pour affichage en français. 
Sys.setlocale("LC_TIME", "French")

Voici les cours Datacamp pour ces différentes librairies.

readr

tidyverse

lubridate

Maintenant, Bob lit ses fichiers et les transforme en objet dans R. Dans un contexte de production, ceux-ci seraient chargés via API ou par un connecteur de données. Mais pour garder cet exemple simple, Bob lit un fichier local sur son disque dur.

Si vous voulez avoir les fichiers de cet exemple, ils sont disponibles sur Kaggle.

#Lecture des fichiers
Online <- read_csv("Online.csv", col_types = cols(Date = col_date(format = "%Y%m%d")))
Retail <- read_csv("Retail.csv", col_types = cols(InvoiceDate = col_date(format = "%Y-%m-%d")))

#Bob vérifie la structure des fichiers afin de mieux orienter ses prochaines manipulations
glimpse(Online)
## Observations: 54,144
## Variables: 10
## $ `Transaction ID`                         <dbl> 48497, 48496, 48495, ...
## $ Date                                     <date> 2017-12-31, 2017-12-...
## $ `Product SKU`                            <chr> "GGOENEBQ079099", "GG...
## $ Product                                  <chr> "Nest® Protect Smoke ...
## $ `Product Category (Enhanced E-commerce)` <chr> "Nest-USA", "Nest-USA...
## $ Quantity                                 <dbl> 4, 5, 1, 1, 1, 3, 1, ...
## $ `Avg. Price`                             <dbl> 80.52, 80.52, 151.88,...
## $ Revenue                                  <dbl> 316.00, 395.00, 149.0...
## $ Tax                                      <dbl> 34.44, 33.14, 12.06, ...
## $ Delivery                                 <dbl> 19.99, 6.50, 6.50, 6....
glimpse(Retail)
## Observations: 181,247
## Variables: 4
## $ InvoiceNo   <dbl> 536598, 536598, 536598, 536599, 536599, 536600, 53...
## $ InvoiceDate <date> 2017-01-01, 2017-01-01, 2017-01-01, 2017-01-01, 2...
## $ StockCode   <dbl> 21421, 21422, 22178, 20749, 21056, 21730, 21871, 2...
## $ Quantity    <dbl> 1, 2, 26, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 60, 2, 1, ...

Bob remarque que son fichier Retail, montrant les ventes des magasins physiques, n’est pas très complet par rapport à son fichier Online. Il aimerait rajouter l’information sur les produits, leur prix de vente et leur catégorie à son fichier Retail. Mais les codes de produit sont aussi différents! Il va donc falloir créer une “clé” entre les “Product SKU” du fichier online et le “StockCode” du fichier retail.

Bob cherche donc à trouver une liste de StockCode – Nom de produit, mais malheureusement, son entreprise n’a jamais cru bon de créer cette liste (à se demander comment elle est toujours en affaires!). Comme Bob doit travailler avec ces contraintes, cela implique qu’il doit créer un fichier de “clé” faisant le lien entre le SKU et le StockCode. Bob demande donc à son stagiaire, Gérard, de créer la concordance entre les 1179 Product SKU et les 1179 StockCodes. Gérard, après avoir sombré dans l’alcoolisme, lui livre le fichier suivant:

KEY_SKU <- read_csv("KEY_SKU.csv")

glimpse(KEY_SKU)
## Observations: 1,178
## Variables: 2
## $ `Product SKU` <chr> "GGOENEBQ079099", "GGOENEBQ079199", "GGOENEBQ084...
## $ StockCode     <dbl> 21421, 21422, 22178, 20749, 21056, 21730, 21871,...

Il s’agit de la concordance entre chaque StockCode et son Product SKU. Avec cette concordance, il devient possible de prendre les informations du fichier Online et de les appliquer au fichier Retail.

Il faut d’abord créer un fichier avec la liste des informations voulues par SKU, c’est-à-dire le nom du produit, sa catégorie, et son prix.

#On commence par créer un vecteur logique des SKUS dupliqués, qu'on rajoute comme une nouvelle colonne dans notre fichier Online. 
Online$dups <- duplicated(Online$`Product SKU`)

#On conserve uniquement les lignes non dupliquées
Product_list <- Online %>% filter(dups == FALSE)

#On conserve seulement l'information pertinente
Product_list <- Product_list[c("Product SKU", "Product", "Product Category (Enhanced E-commerce)", "Avg. Price")]

#On rajoute le stock code à notre liste de produits
Product_list <- left_join(KEY_SKU, Product_list, by = "Product SKU")

#Notre liste de produits avec toutes les informations est prête! 
glimpse(Product_list)
## Observations: 1,178
## Variables: 5
## $ `Product SKU`                            <chr> "GGOENEBQ079099", "GG...
## $ StockCode                                <dbl> 21421, 21422, 22178, ...
## $ Product                                  <chr> "Nest® Protect Smoke ...
## $ `Product Category (Enhanced E-commerce)` <chr> "Nest-USA", "Nest-USA...
## $ `Avg. Price`                             <dbl> 80.52, 80.52, 151.88,...
#On peut enlever la colonne dups de notre fichier Online
Online$dups <- NULL

#Et ensuite, rajouter toute l'information à notre fichier Retail
Retail <- merge(Retail, Product_list, by = "StockCode", all.x = TRUE)
Retail <- arrange(Retail, InvoiceDate, InvoiceNo)

#Nous pouvons calculer le reste de l'information manquante: les revenus et les taxes. Nous calculons par ligne d'abord:
perLine <- Retail %>% mutate(RevenuePerLine = Quantity * `Avg. Price`, TaxPerLine = RevenuePerLine * 0.014975)

#Puis nous groupons les lignes par numéro de commande: 
perInvoiceNo <- perLine %>% group_by(InvoiceNo) %>% summarise(Revenue = sum(RevenuePerLine), Tax = sum(TaxPerLine))

#Puis nous joignons ces informations à nos données
Retail <- merge(Retail, perInvoiceNo, all.x = TRUE)

#Arrondissons le montant des taxes
Retail$Tax <- round(Retail$Tax, digits = 2)

#Et ajoutons des frais de livraison de 0
Retail <- Retail %>% mutate(Delivery = 0)

#On peut même enlever le StockCode et travailler uniquement avec le Product SKU
Retail$StockCode <- NULL

#Notre fichier Retail possède maintenant toutes les informations disponibles dans notre fichier Online! 
glimpse(Online)
## Observations: 54,144
## Variables: 10
## $ `Transaction ID`                         <dbl> 48497, 48496, 48495, ...
## $ Date                                     <date> 2017-12-31, 2017-12-...
## $ `Product SKU`                            <chr> "GGOENEBQ079099", "GG...
## $ Product                                  <chr> "Nest® Protect Smoke ...
## $ `Product Category (Enhanced E-commerce)` <chr> "Nest-USA", "Nest-USA...
## $ Quantity                                 <dbl> 4, 5, 1, 1, 1, 3, 1, ...
## $ `Avg. Price`                             <dbl> 80.52, 80.52, 151.88,...
## $ Revenue                                  <dbl> 316.00, 395.00, 149.0...
## $ Tax                                      <dbl> 34.44, 33.14, 12.06, ...
## $ Delivery                                 <dbl> 19.99, 6.50, 6.50, 6....
glimpse(Retail)
## Observations: 181,247
## Variables: 10
## $ InvoiceNo                                <dbl> 536598, 536598, 53659...
## $ InvoiceDate                              <date> 2017-01-01, 2017-01-...
## $ Quantity                                 <dbl> 1, 2, 26, 2, 2, 2, 1,...
## $ `Product SKU`                            <chr> "GGOENEBQ079099", "GG...
## $ Product                                  <chr> "Nest® Protect Smoke ...
## $ `Product Category (Enhanced E-commerce)` <chr> "Nest-USA", "Nest-USA...
## $ `Avg. Price`                             <dbl> 80.52, 80.52, 151.88,...
## $ Revenue                                  <dbl> 4190.44, 4190.44, 419...
## $ Tax                                      <dbl> 62.75, 62.75, 62.75, ...
## $ Delivery                                 <dbl> 0, 0, 0, 0, 0, 0, 0, ...

Dernière étape avant de faire nos analyses et nos visualisations: joindre les deux tableaux ensemble.

#Il faut d'abord renommer les colonnes pour qu'elles possèdent le même nom d'un côté comme de l'autre. Ça nous simplifiera la vie. 
Retail <- Retail %>% rename("Transaction ID" = "InvoiceNo", "Date" = "InvoiceDate")

#On rajoute un colonne catégorielle à chaque tableau
Retail$Channel <- "Retail"
Online$Channel <- "Online"

#On joint
Full <- bind_rows(Online, Retail)

#Notre tableau complet est prêt! 

Analyse des données

Maintenant que toutes nos données sont regroupées, il est très facile de faire des statistiques descriptives en utilisant les fonctions de base de R ainsi que les fonctions group_by(), filter() et summarize() de dplyr.

#Exemple: On peut calculer le revenu total annuel par Canal de vente (Online ou Retail)
Full_unique <- Full %>% 
  filter(duplicated(Full$`Transaction ID`) == FALSE)

Revenue_by_channel <- Full_unique %>% 
  group_by(Channel) %>% 
  summarize(sum(Revenue))

Revenue_by_channel
## # A tibble: 2 x 2
##   Channel `sum(Revenue)`
##   <chr>            <dbl>
## 1 Online        4743705.
## 2 Retail       19019265.

Rappelons les données que nous voulons obtenir et présenter pour notre rapport:

  • Revenus par catégorie par mois
  • Croissance par catégorie MoM
  • Estimation des ventes pour le mois suivant
  • Scénarios d’estimation des ventes en fonction du budget marketing

Nous allons produire et visualiser chacune de ces données. Débutons par créer un sommaire des revenus par canal, par catégorie, et par mois.

Revenus par mois par catégorie

#Créer la colonne "Mois-Année" qui va agir à titre de facteur de groupement
Full_unique$Mois_An <- format(as.Date(Full_unique$Date), "%Y-%m")

#Changer les vecteurs catégoriels en facteurs
Full_unique$`Product Category (Enhanced E-commerce)` <- as.factor(Full_unique$`Product Category (Enhanced E-commerce)`)
Full_unique$Mois_An <- as.factor(Full_unique$Mois_An)
Full_unique$Channel <- as.factor(Full_unique$Channel)

#Créer le sommaire des ventes par canal, mois et catégorie. 
Full_summary <- Full_unique %>%
  group_by(Channel, Mois_An, `Product Category (Enhanced E-commerce)`, .drop = FALSE) %>%
  summarize(sum(Revenue))

#On remplace les ventes non catégorisées par une catégorie "Autres"
Full_summary$`Product Category (Enhanced E-commerce)` <- as.character(Full_summary$`Product Category (Enhanced E-commerce)`)

Full_summary$`Product Category (Enhanced E-commerce)` <- Full_summary$`Product Category (Enhanced E-commerce)` %>% replace_na("Other")

Full_summary$`Product Category (Enhanced E-commerce)` <- as.factor(Full_summary$`Product Category (Enhanced E-commerce)`)

Maintenant que nous avons notre sommaire de ventes par canal, par catégorie et par mois, nous pouvons commençer à créer nos visualisations.

#Initialisation de la date. Dans un cadre réel, nous utiliserions Sys.Date(), mais ici, nous utilisons une date fixe pour notre exemple. 

sysdate <- as.Date(ymd_hms("2018-01-01 9:00:00"))

#Visualisation des ventes du dernier mois, par catégorie. 
ggplot(data = filter(Full_summary, Mois_An == format(as.Date(sysdate %m+% months(-1)), "%Y-%m")), aes(x = reorder(`Product Category (Enhanced E-commerce)`, `sum(Revenue)`), y = `sum(Revenue)`, fill = Channel)) +
  geom_col(position = "stack") +
  coord_flip() +
  theme_tufte() + 
  theme(legend.position = c(0.8, 0.15)) +
  scale_fill_manual(values = c("#2c7bb6", "#abd9e9")) +
  scale_y_continuous(labels = dollar) +
  theme(axis.title.x = element_text(family = "Arial", colour="#484848", size=12),
        axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
        axis.title.y = element_blank(),
        axis.text.y  = element_text(family = "Arial", colour="#484848", size=12), 
        legend.title = element_text(family = "Arial", colour="#484848", size=10), 
        legend.text  = element_text(family = "Arial", colour="#484848", size=9),
        plot.title   = element_text(family = "Arial", colour="#484848", size=16),
        plot.subtitle = element_text(family = "Arial", colour="#484848", size=10)) +
  labs(y = "Revenus (En $)",
       title = "Ventes par catégorie, par canal",
       subtitle = (paste("Mois de", format(as.Date(sysdate %m+% months(-1)), "%B %Y"), sep = " ")),
       fill = "Canal")

Maintenant, nous désirons obtenir la croissance des ventes par catégorie pour le dernier mois complet, comparé au mois précédent.

#Calcul de la croissance des ventes du dernier mois (Décembre) par rapport au mois précédent (Novembre)
Summary_current <- Full_summary %>% filter(Mois_An == format(as.Date(sysdate %m+% months(-1)), "%Y-%m"))
Summary_previous <- Full_summary %>% filter(Mois_An == format(as.Date(sysdate %m+% months(-2)), "%Y-%m"))
Summary_current$Growth <- (Summary_previous$`sum(Revenue)` - Summary_current$`sum(Revenue)`) / Summary_previous$`sum(Revenue)` * 100

#
Summary_current[is.na(Summary_current)] <- NA
Summary_current$Growth[is.infinite(Summary_current$Growth)] <- NA

#Visualisation de la croissance des ventes du dernier mois, en ligne, par catégorie. 
ggplot(data = filter(Summary_current, Channel == "Online" & !is.na(Growth)), aes(x = reorder(`Product Category (Enhanced E-commerce)`, Growth), y = Growth, fill = Growth > 0)) +
  geom_col() +
  coord_flip(ylim = c(-100, 100)) +
  theme_tufte() + 
  theme(axis.title.x = element_text(family = "Arial", colour="#484848", size=12),
        axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
        axis.title.y = element_blank(),
        axis.text.y  = element_text(family = "Arial", colour="#484848", size=12),
        plot.title   = element_text(family = "Arial", colour="#484848", size=16),
        plot.subtitle = element_text(family = "Arial", colour="#484848", size=10),
        legend.position = "none"
        ) +
  labs(y = "Variation MoM (en %)",
       title = "Variation des ventes mensuelles en ligne",
       subtitle = (paste("Mois de", format(as.Date(sysdate %m+% months(-1)), "%B %Y"), "par rapport à", format(as.Date(sysdate %m+% months(-2)), "%B %Y"), sep = " "))) +
       scale_fill_manual(values=c("FALSE"= "#fdae61", "TRUE" = "#abd9e9"))
#Visualisation de la croissance des ventes du dernier mois, en magasin, par catégorie. 
ggplot(data = filter(Summary_current, Channel == "Retail" & !is.na(Growth)), aes(x = reorder(`Product Category (Enhanced E-commerce)`, Growth), y = Growth, fill = Growth > 0)) +
  geom_col()+
  coord_flip(ylim = c(-100, 100)) +
  theme_tufte() + 
  theme(axis.title.x = element_text(family = "Arial", colour="#484848", size=12),
        axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
        axis.title.y = element_blank(),
        axis.text.y  = element_text(family = "Arial", colour="#484848", size=12),
        plot.title   = element_text(family = "Arial", colour="#484848", size=16),
        plot.subtitle = element_text(family = "Arial", colour="#484848", size=10),
        legend.position = "none"
        ) +
  labs(y = "Variation MoM (en %)",
       title = "Variation des ventes mensuelles en magasin",
       subtitle = (paste("Mois de", format(as.Date(sysdate %m+% months(-1)), "%B %Y"), "par rapport à", format(as.Date(sysdate %m+% months(-2)), "%B %Y"), sep = " "))) +
       scale_fill_manual(values=c("FALSE"= "#fdae61", "TRUE" = "#abd9e9"))

Maintenant que nous connaissons nos ventes et notre croissance par catégorie, nous aimerions faires des prévisions pour le mois prochain afin d’établir un objectif de ventes.

Si vous voulez apprendre à faire des modèles de séries temporelles, voici l’excellent cours sur Datacamp: Forecasting using R par Rob J. Hyndman, le créateur de la librairie forecast.

#Chargeons la librairie forecast qui permet de faire des modèles prédictifs de série temporelle, et la librairie xts qui permet de créer des objets de type séries de dates plus facilement
library(forecast)
library(xts)

#Créons les revenus journaliers totaux, incluant les ventes en ligne et en magasin
Revenue_daily <- Full_unique %>% group_by(Date) %>% summarize(sum(Revenue))

#Et une série de dates pour construire notre objet xts
dates <- seq(as.Date("2017-01-01"), length = 365, by = "days")

#Convertissons cet objet en format xts
Revenue_daily <- xts(Revenue_daily$`sum(Revenue)`, order.by = dates)

#Créons notre modèle prédictif de manière automatique
model <- auto.arima(Revenue_daily, stepwise = FALSE)

#Faisons une prédiction pour le nombre de jours dans le mois actuel
fcast <- forecast(model, h = days_in_month(sysdate))

#On jette un oeil rapide à la prédiction et aux valeurs prédites
plot(fcast)
#Et aux prévisions moyennes
fcast$mean
## Time Series:
## Start = 366 
## End = 396 
## Frequency = 1 
##  [1] 100409.10  95617.85  82661.92  95286.51  98492.79  85303.40  91042.45
##  [8]  98923.06  88758.51  88421.40  97584.02  91933.86  87538.74  95362.89
## [15]  94152.09  88053.42  93077.02  95182.81  89395.81  91298.83  95155.73
## [22]  90978.02  90295.77  94414.55  92341.67  90062.89  93365.82  93227.23
## [29]  90410.92  92360.72  93573.34
#On crée notre tableau de données à visualiser
df <- data.frame(Bas = sum(fcast$lower[,1]), Moyen = sum(fcast$mean), `Élevé` = sum(fcast$upper[,1]))
df_long <- gather(df, key = "Estimation", value = "value")

#On ajuste l'affichage des chiffres en $
dollar <- dollar_format(accuracy = 1, big.mark = " ", prefix = "", suffix = "$")

#On crée notre visualisation
ggplot(data = df_long, aes(x = Estimation, y = value, fill = Estimation, label = dollar(value))) +
  geom_bar(stat="identity", position = "dodge") +
  geom_text(hjust = 1.1, size = 5, color = "#ffffff") +
  coord_flip() +
  theme_tufte() +
  scale_y_continuous(labels = dollar) +
  scale_x_discrete(limits = c("Élevé", "Moyen", "Bas")) +
  scale_fill_manual(values = c("#abd9e9", "#abd9e9", "#2c7bb6")) +
  labs(title = (paste("Estimation des ventes pour le mois de", format(as.Date(sysdate), "%B %Y"), sep = " ")),
       subtitle = "Calculé à l'aide d'un modèle ARIMA automatique") +
  theme(
    axis.title.x = element_blank(),
    axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
    axis.title.y = element_blank(),
    axis.text.y  = element_text(family = c("Arial", "Arial Black", "Arial"), colour="#484848", size=12), 
    plot.title   = element_text(family = "Arial", colour="#484848", size=16),
    plot.subtitle = element_text(family = "Arial", colour="#484848", size=10),
    legend.position = "none"
) 

En utilisant uniquement un modèle de série temporelles, il y aura toujours une marge d’erreur assez importante. Toutefois, il est aussi possible de créer plutôt un modèle de régression linéaire avec des erreurs ARIMA. Comme nous désirons conaitre l’effet de la publicité sur nos ventes, nous allons créer ce type de modèle avec des données sur le budget publicitaire online et offline, et ensuite faire des prédictions par scénarios.

#Commençons par importer un fichier avec les dépenses marketing par jour pour l'année 2017. 
Marketing_spend <- read_csv("Marketing_Spend.csv", 
    col_types = cols(`Offline Spend` = col_double(), 
        `Online Spend` = col_double(), X1 = col_date(format = "%Y-%m-%d")))

#On crée trois modèles en ajoutant les budgets marketing comme régresseurs
model_spend_both <- auto.arima(Revenue_daily, stepwise = FALSE, xreg = cbind(Marketing_spend$`Online Spend`, Marketing_spend$`Offline Spend`))

model_spend_online <- auto.arima(Revenue_daily, stepwise = FALSE, xreg = Marketing_spend$`Online Spend`)

model_spend_offline <- auto.arima(Revenue_daily, stepwise = FALSE, xreg = Marketing_spend$`Offline Spend`)

#On observe le "fit" des modèles
summary(model_spend_both)
## Series: Revenue_daily 
## Regression with ARIMA(3,0,0) errors 
## 
## Coefficients:
##           ar1      ar2     ar3   intercept    xreg1   xreg2
##       -0.0879  -0.0553  0.0715  -24577.857  43.1001  2.6479
## s.e.   0.0523   0.0524  0.0529    2346.622   0.9302  0.7613
## 
## sigma^2 estimated as 190771248:  log likelihood=-3994.55
## AIC=8003.11   AICc=8003.42   BIC=8030.41
## 
## Training set error measures:
##                    ME  RMSE      MAE       MPE     MAPE      MASE
## Training set 8.370002 13698 11354.57 -12.31825 34.27963 0.3066989
##                      ACF1
## Training set -0.002404769
summary(model_spend_online)
## Series: Revenue_daily 
## Regression with ARIMA(3,0,0) errors 
## 
## Coefficients:
##           ar1      ar2     ar3   intercept     xreg
##       -0.0579  -0.0164  0.1133  -18797.216  44.0179
## s.e.   0.0527   0.0524  0.0531    1899.972   0.9144
## 
## sigma^2 estimated as 195942090:  log likelihood=-3999.95
## AIC=8011.9   AICc=8012.13   BIC=8035.3
## 
## Training set error measures:
##                   ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 8.96493 13901.72 11577.88 -13.89588 34.22336 0.3127307
##                      ACF1
## Training set -0.008030993
summary(model_spend_offline)
## Series: Revenue_daily 
## Regression with ARIMA(0,1,4) errors 
## 
## Coefficients:
##           ma1      ma2     ma3     ma4     xreg
##       -0.9442  -0.1772  0.0047  0.1510  14.0375
## s.e.   0.0517   0.0723  0.0749  0.0534   1.6692
## 
## sigma^2 estimated as 1.224e+09:  log likelihood=-4323.92
## AIC=8659.84   AICc=8660.07   BIC=8683.22
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 356.6522 34692.86 26926.26 -69.75817 96.22966 0.7273065
##                     ACF1
## Training set 0.005368986

Le modèle incluant les deux régresseurs est supérieur (AIC plus faible). On remarque aussi que le budget de marketing offline a peu d’effet sur les ventes, avec un coefficient de seulement 2.64, comparé à un coefficient de 43.10 pour le budget de marketing online. Une autre chose intéressante est que le AIC du modèle incluant les régresseurs est beaucoup plus faible que celui n’incluant pas les régresseurs, ce qui indique que nous devrions être en mesure de faire des prédictions plus précises.

Créons maintenant nos prédictions qui incluent le budget marketing prévu.

#Il faut d'abord créer des tableaux avec des scénarios d'augmentation de budgets en fonction des budgets du mois précédent.

Budget_same <- Marketing_spend %>% 
                filter(X1 <= (ceiling_date(as.Date(sysdate %m+% months(-1)), "month") - days(1)) & X1 >= as.Date(sysdate %m+% months(-1)))

Budget_higher_online <- Marketing_spend %>% 
                          filter(X1 <= (ceiling_date(as.Date(sysdate %m+% months(-1)), "month") - days(1)) & X1 >= as.Date(sysdate %m+% months(-1))) %>%
                          mutate(`Online Spend` = `Online Spend` * 1.25)

Budget_higher_offline <- Marketing_spend %>% 
                          filter(X1 <= (ceiling_date(as.Date(sysdate %m+% months(-1)), "month") - days(1)) & X1 >= as.Date(sysdate %m+% months(-1))) %>%
                          mutate(`Offline Spend` = `Offline Spend` * 1.25)

#On peut maintenant créer trois prédictions avec nos trois scénarios
fcast_same <- forecast(model_spend_both, xreg = cbind(Budget_same$`Online Spend`, Budget_same$`Offline Spend`), h = days_in_month(sysdate))

fcast_higher_online <- forecast(model_spend_both, xreg = cbind(Budget_higher_online$`Online Spend`, Budget_higher_online$`Offline Spend`), h = days_in_month(sysdate))

fcast_higher_offline <- forecast(model_spend_both, xreg = cbind(Budget_higher_offline$`Online Spend`, Budget_higher_offline$`Offline Spend`), h = days_in_month(sysdate))

#On jette un oeil à nos prédictions. Laquelle semble la plus intéressante? 
plot(fcast_same)
plot(fcast_higher_online)
plot(fcast_higher_offline)

Maintenant que nous avons nos prédictions avec nos trois scénarios, nous allons visualiser celles-ci. En premier lieu, nous allons refaire notre estimation précédente des ventes du mois avec notre nouveau modèle plus précis.

#On crée notre tableau de données à visualiser
df_same <- data.frame(Bas = sum(fcast_same$lower[,1]), Moyen = sum(fcast_same$mean), `Élevé` = sum(fcast_same$upper[,1]))
df_long_same <- gather(df_same, key = "Estimation", value = "value")

#On crée notre visualisation
ggplot(data = df_long_same, aes(x = Estimation, y = value, fill = Estimation, label = dollar(value))) +
  geom_bar(stat="identity", position = "dodge") +
  geom_text(hjust = 1.1, size = 5, color = "#ffffff") +
  coord_flip() +
  theme_tufte() +
  scale_y_continuous(labels = dollar) +
  scale_x_discrete(limits = c("Élevé", "Moyen", "Bas")) +
  scale_fill_manual(values = c("#abd9e9", "#abd9e9", "#2c7bb6")) +
  labs(title = (paste("Estimation des ventes pour le mois de", format(as.Date(sysdate), "%B %Y"), sep = " ")),
       subtitle = "Budget de marketing identique au mois précédent", 
       caption = "Régression linéaire du budget marketing sur les ventes avec erreurs ARIMA\nNiveau de confiance 80%") +
  theme(
    axis.title.x = element_blank(),
    axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
    axis.title.y = element_blank(),
    axis.text.y  = element_text(family = c("Arial", "Arial Black", "Arial"), colour="#484848", size=12), 
    plot.title   = element_text(family = "Arial", colour="#484848", size=16),
    plot.subtitle = element_text(family = "Arial", colour="#484848", size=10),
    plot.caption = element_text(family = "Arial", colour="#484848", size=8),
    legend.position = "none"
) 

Déjà beaucoup plus précis! Maintenant, nous allons créer une visualisation qu démontre les trois scénarios dans un seul graphique, afin qu’il soit évident de la décision à prendre.

#On crée notre tableau de données à visualiser
df_budget <- data.frame(Budget = c("Identique", "25% de plus en ligne", "25% de plus hors ligne"), 
                        Moyen = c(sum(fcast_same$mean), sum(fcast_higher_online$mean), sum(fcast_higher_offline$mean)))

#On crée notre visualisation
ggplot(data = df_budget, aes(x = Budget, y = Moyen, fill = Budget, label = dollar(Moyen))) +
  geom_bar(stat="identity", position = "dodge") +
  geom_text(hjust = 1.1, size = 5, color = "#ffffff") +
  coord_flip() +
  theme_tufte() +
  scale_y_continuous(labels = dollar) +
  scale_fill_manual(values = c("#2c7bb6", "#abd9e9", "#abd9e9")) +
  scale_x_discrete(limits = c("25% de plus en ligne", "25% de plus hors ligne", "Identique")) +
  labs(title = (paste("Estimation des ventes pour le mois de", format(as.Date(sysdate), "%B %Y"), sep = " ")),
       subtitle = "En fonction d'une augmentation du budget marketing en ligne ou hors ligne", 
       caption = "Prédictions calculées à partir de la moyenne du modèle de régression\n linéaire du budget marketing sur les ventes avec erreurs ARIMA") +
  theme(
    axis.title.x = element_blank(),
    axis.text.x  = element_text(family = "Arial", colour="#484848", size=12),
    axis.title.y = element_blank(),
    axis.text.y  = element_text(family = c("Arial Black", "Arial", "Arial"), colour="#484848", size=12), 
    plot.title   = element_text(family = "Arial", colour="#484848", size=16),
    plot.subtitle = element_text(family = "Arial", colour="#484848", size=10),
    plot.caption = element_text(family = "Arial", colour="#484848", size=8),
    legend.position = "none"
) 

Démarrage de l’instance Google Compute Engine

Maintenant que nous avons tous les rapports que nous désirons envoyer chaque mois, il nous reste seulement deux étapes. La première est de créer une instance de RStudio dans Google Compute Engine, et la deuxième est de créer un script dans cette instance qui enverra notre rapport chaque mois. Commençons par créer notre instance de RStudio dans Google Compute Engine avec la librairie googleComputeEngineR.

La première étape est de créer un compte Google Compute Engine. Il faut ajouter une carte de crédit, mais l’utilisation d’un serveur f1-micro est gratuit. C’est amplement assez performant pour la tâche qu’on va lui demander une fois par mois.

Ensuite, il faut créer notre projet, puis aller chercher les détails de notre “Service Account” en format JSON: API Manager -> Credentials -> Create credentials -> Service account key -> Key type = JSON. On ajoute ce fichier au dossier de travail de notre projet. À noter que c’est ce fichier JSON qui contient les informations d’accès au compte GCE: il convient donc de bien le protéger.

Ensuite, il faut créer un fichier .Renviron dans notre dossier de travail (avec le bloc-note) qui sera chargé automatiquement lorsque la librairie sera activée. Ce fichier doit contenir les éléments suivants:

GCE_AUTH_FILE = "C:/My First Project-168c949fa5ac.json" #L'emplaçement du fichier JSON créé précédemment. 
GCE_DEFAULT_PROJECT_ID = "mineral-highway-236314" #Le project ID du "My First Projet" créé par Google lors de la création du compte, ou un autre projet si vous préférez. 
GCE_DEFAULT_ZONE = "us-east1-b" #L'emplaçement du serveur, qui devrait être celui-ci si vous vous connectez à partir de la cote est. 

Ensuite, le code pour créer la nouvelle instance GCE est excessivement simple.

library(googleComputeEngineR)

vm <- gce_vm(template = "rstudio",
             name = "autoemailer", #Nom qu'on désire donner à l'instance
             username = "waq", #Nom d'utilisateur pour RStudio
             password = "qwertyiscool", #Mot de passe pour RStudio
             predefined_type = "n1-standard-1") #Type de serveur

Le f1-micro est le seul serveur gratuit, mais j’ai plutôt utilisé un n1-standard-1 à environ 25$/mois car les librairies utilisées pour l’exemple dépassaient la capacité de mémoire du petit f1-micro.

Préparation de l’instance de machine virtuelle

Il faut d’abord installer les librairies qui ne sont pas incluses dans l’image de base. Mais avant cela, il faut installer java ainsi que rJava via le terminal de notre instance (Linux Debian 8) car ils sont une dépendance de la librairie mailR que nous utiliserons pour l’envoi de courriel.

sudo apt-get install default-jdk
sudo apt-get install r-cran-rjava
sudo R CMD javareconf

Maintenant, nous pouvons installer les librairies:

install.packages("cronR")
install.packages("ggthemes")
install.packages("extrafont")
install.packages("miniUI")
install.packages("forecast")
install.packages("xts")
install.packages("mailR")
install.packages("rJava")
install.packages("shiny")
install.packages("shinyFiles")

Et les activer:

library(rJava)
library(cronR)
library(miniUI)
library(mailR)
library(readr)
library(tidyverse)
library(lubridate)
library(ggthemes)
library(scales)
library(extrafont)
library(forecast)
library(xts)
library(shiny)
library(shinyFiles)

Ensuite, si on veut que les dates soient formattées correctement, il faut créer la locale fr_FR.UTF-8, encore une fois dans le terminal, avec l’application à cet effet:

sudo dpkg-reconfigure locales

Et créer un fichier .Renviron avec les locales qui seront chargées au start-up de R:

LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="fr_FR.UTF-8"
LC_NUMERIC="fr_FR.UTF-8"
LC_TIME="fr_FR.UTF-8"
LC_COLLATE="fr_FR.UTF-8"
LC_MONETARY="fr_FR.UTF-8"
LC_MESSAGES="fr_FR.UTF-8"
LC_PAPER="fr_FR.UTF-8"
LC_NAME="fr_FR.UTF-8"
LC_ADDRESS="fr_FR.UTF-8"
LC_TELEPHONE="fr_FR.UTF-8"
LC_MEASUREMENT="fr_FR.UTF-8"
LC_IDENTIFICATION="fr_FR.UTF-8"
LC_ALL=fr_FR.UTF-8

Et enfin, installer et activer le service cron dans le terminal

sudo apt-get install cron
sudo cron start

Création du script d’envoi de courriel automatique

J’ai d’abord créé une version abrégée du fichier actuel en format R Markdown, contenant uniquement les graphiques ainsi qu’une courte explication de ceux-ci. J’ai téléversé ce fichier sur mon instance GCE directement via RStudio. Ensuite, il faut téléverser tous les documents que nous avons utilisés jusqu’ici. Le plus simple est de les copier via un fichier .zip qui sera automatiquement décompressé à l’arrivée.

Une fois que c’est fait, voici le script à utiliser:

#Créer le rapport en html à partir du fichier .rmd
rapport <- rmarkdown::render("Rapport_Mensuel.Rmd")

#Envoyer le courriel
mailR::send.mail(from = "[email protected]",
          to = "[email protected]",
          subject = "Rapport de performance du mois",
          body = "Rapport_Mensuel.html",
          html = TRUE,
          inline = TRUE,
          smtp = list(host.name = "smtp.gmail.com", port = 465, user.name = "[email protected]", passwd = "xxxxxxxx", ssl = TRUE),
          authenticate = TRUE,
          send = TRUE)

La toute dernière étape est de sélectionner ce script dans l’onglet Tools –> Addins –> cronR afin de sélectionner la fréquence désirée!

Voila, vous reçevrez maintenant vos rapports dans votre boite courriel, toujours mis à jour, sans lever le petit doigt!

Un hybride entre le mealkit et l’épicerie en ligne

Je lisais tantôt un article sur la perte nette des Marchés Goodfood. C’est certainement impressionnant d’avoir plus que doublé ses abonnés en un an (45 000 à 126 000!), mais combien de ceux-ci sont de réels abonnés que ne font pas que surfer les promotions? Il faut se questionner sur le modèle d’affaires même d’une entreprise avec une perte nette de 4,9M sur un revenu de 29,6M – c’est-à-dire une perte de 20%. Ça fait toute une pente à remonter avant d’être profitable!

Un KPI que je serais vraiment curieux de savoir pour les Marchés Goodfood (et tous les autres compagnies de mealkits, d’ailleurs!) est le LTV/CAC, qui permet de voir le ratio entre la valeur à vie d’un client et son coût d’acquisition. Avec ses campagnes d’acquisition très agressives, je parie que celle des Marchés Goodfood est inférieure à 1. On conseille généralement de garder ce ratio entre 2 et 4.  Ça prendra des poches profondes afin de faire vivre cette entreprise jusqu’à la rentabilité, qui ne pourra être atteinte que par une baisse importante des coûts d’acquisition, suivie d’une baisse de la croissance des abonnés, et évidemment d’investisseurs mécontents!

Je ne suis pas un fan du modèle d’affaire de l’offre de service actuelle des compagnies de mealkits. Je trouve que c’est beaucoup de main d’œuvre peu productive, remplir des petits pots de vinaigre balsamique et emballer des oignons verts individuellement dans des sacs de plastique. Toutefois, quand on cherche plus loin, la vraie valeur des compagnies de mealkits, qui explique évidemment leur popularité, n’est pas le produit lui-même, c’est le convenience. Le côté pratique de ne pas avoir à aller à l’épicerie aussi souvent, mais surtout le fait de ne pas avoir à choisir des recettes sur des sites web fragmentées pour ensuite les écrire sur un papier qu’on va perdre, pour ensuite oublier la moitié des choses qu’on voulait acheter à l’épicerie.

Les gros joueurs d’épicerie n’ont pas encore vu que c’était là, la véritable valeur de ces entreprises. Un seul joueur s’en approche, IGA, avec sa plate-forme de recettes en ligne qui permet de cocher les ingrédients manquants sur chaque recette qu’on sélectionne pour les mettre automatiquement dans le panier d’épicerie. Toutefois, deux problèmes majeurs ne permettent pas à cette plate-forme de rayonner :

  1. La fonctionnalité elle-même ne fonctionne pas : quand on essaie d’ajouter plusieurs produits au panier, seul le premier est ajouté à titre de recherche qu’on doit ensuite choisir. Exemple, s’il faut acheter des pommes, il faut choisir le type de pommes. C’est trop de friction : il faudrait que le produit optimal pour la recette soit déjà programmé dans la recette, par exemple 2lbs de pommes Fuji ajoutées directement dans le panier d’achats.
  2. La plate-forme fonctionne seulement en achat simple plutôt qu’en abonnement, ce qui fait qu’on doit réacquérir les utilisateurs constamment.

Il n’en faudrait pas beaucoup pour qu’IGA (ou un autre gros joueur) lance sa propre plateforme de mealkits personnalisée, et différenciée des joueurs actuels :

  1. Une plate-forme où on s’engage à acheter de manière hebdomadaire, avec un paiement récurrent donnant droit à la livraison gratuite, qui pourrait se transformer en carte-cadeau si la balance n’est pas utilisée et qu’on décide d’arrêter notre abonnement.  
  2. Une dizaine de recettes différentes proposées chaque semaine parmis lesquelles on peut en sélectionner le nombre de notre choix.
  3. Un système permettant de sélectionner uniquement les ingrédients qu’on a besoin pour chaque recette, sans ceux qu’on a déjà : on oublie les oignons verts emballés individuellement alors qu’on en a déjà pleins dans le frigo!
  4. Un système de suggestion des recettes de la semaine en fonction de ce qu’on a acheté la semaine précédente : si on a acheté 5 lbs de patates la semaine passée, il doit en rester cette semaine, et le système pourrait mettre en valeur une recette comportant cet ingrédient qui est peut-être restant.

Il s’agirait là d’une excellente manière de fidéliser une clientèle jeune et dynamique, attirée par la facilité des services de mealkits, à un prix bien plus raisonnable par le modèle d’affaires même. Ajoutez à ça un vrai centre de distribution automatisé à la Tesco en Angleterre, les seuls joueurs qui font vraiment bien l’épicerie en ligne, et vous avez là un vrai modèle d’affaire qui ferait de vrais profits. Parions qu’avec ça, la part de marché abysmale de 1.3% de ventes en ligne de l’épicerie au Canada[1] augmenterais rapidement!

Quelqu’un écoute chez IGA, Métro, Sobeys?

[1] Données Nielsen – Q3 2018


UPDATE #1

J’ai reçu plusieurs messages par rapport à ce billet. Voici une petite précision que j’ai envoyée à Judith Fetzer chez Cookit (mes prefs!)…

Salut Judith! Mon article n’a jamais été écrit comme un attaque mais plutôt comme une idée de plus-value à ajouter à l’offre existante des compagnies de mealkits. D’ailleurs, j’ai modifié le wording pour changer “Modèle d’affaires” par “Offre actuelle”. Le modèle d’affaire lui-même est excellent, c’est l’offre qui pourrait être encore meilleure. Don’t get me wrong, tous ceux travaillant dans cette industrie sont au top de leur game, et ca doit être un défi marketing hallucinant à réaliser. Vous êtes fort probablement les leaders au niveau de la finesse du marketing. Et c’est aussi un défi technique et logistique hallucinant. Je ne remet rien de ça en question! Ce que je trouve, c’est que c’est dommage d’avoir à reçevoir en petites quantités des ingrédients que j’ai probablement déjà, le fameux exemple du 10mL de vinaigre balsamique dans un cup de plastique. Si tu lis l’article jusqu’à la fin, je propose une manière de mélanger le concept du mealkit et de l’épicerie en ligne, en amenant le meilleur des deux offres avec aucunes de leurs faiblesses. C’est un défi technique à réaliser, mais considérant ce que vous avez déjà accompli, c’est clairement dans le domaine du possible. 

Maintenant, si on parle chiffres, ce qui me fait peur de cette industrie, c’est le churn hallucinant. En utilisant tes chiffres et en supposant une commande moyenne de 149$ (4 repas de 4 portions), on se retrouve avec un churn de 12% par semaine pour un LTV de 1200$. C’est énorme! Ça veut dire qu’en une année, sur 1000 inscrits, il reste… une personne. Où vont les 999 autres? Retournent-ils à l’épicerie traditionnelle ou à l’offre marketing alléchante du concurrent? C’est difficile à dire. Toutefois, nous sommes un petit marché, et l’acquisition de clients va frapper un mur un moment donné. Pour GoodFood, le sujet original de mon article, il faut souhaiter qu’ils deviennent profitables avant de frapper ce mur la, sinon le churn exponentiel va leur rentrer dedans. Si tu veux en parler plus en détails, je suis pas la pour basher. J’ai juste eu une idée qui ne s’applique pas à mon industrie et je voulais la partager, mais je l’ai peut-être fait un peu maladroitement 🙂

UPDATE #2

Cet article a vraiment explosé par rapport à l’analyse du churn, même si le sujet principal de l’article était plutôt une idée d’extension d’offre de service… Il faut quand même que je fasse un mea culpa! J’ai géré des services d’abonnements où les stats étaient faites mensuellement et ou le churn était très faible. Dans ces cas, assumer un churn linéaire peux fonctionner, c’est même une façon très conservatrice d’analyser ses données, et ca a le mérite d’être très simple à calculer.

Toutefois, dans le cas d’un churn plus élevé et de stats calculées de manière hebdomadaires, la game change complètement car à ce moment, la courbe exponentielle est trop élevée. J’ai eu la chance que Daniel McCarthy, Assistant Professeur en Marketing à Emory University’s Goizueta School of Business, vienne commenter sur mon billet LinkedIn. Il est une sommitée mondiale dans cette industrie! Il suggère de calculer le churn en deux segments, l’un a churn élevé et l’autre à churn faible, à l’aide de données secondaires de cartes de crédit provenant de Second Measure. Comme je n’ai pas accès à ce genre de données secondaires, j’ai pensé faire un modèle exponentiel double, avec un churn qui diminue chaque semaine pour la cohorte originale. M. McCarthy a spécifié que ce genre de modèle était un peu moins performant que celui à double segments de données secondaires, mais serait tout de même valide. Le modèle naif (sans données) avec une diminution du churn hebdomadaire de 5% semble mieux fitter avec les données fournies par Judith: LTV de 1200$, commande moyenne de 82$, pour un churn semaine 1 d’un peu oins de 7%, mais qui diminue rapidement ensuite, pour laisser environ 25% de utilisateurs à la fin d’un an qui devraient coller pour de bon, avec un churn de moins de 0.5% à la fin de l’année pour cette cohorte:

La logique est qu’un client qui a gardé le service pendant 1 an est clairement qualifié pour ce genre de service. C’est vraiment plus intuitif qu’un churn linéaire. C’est aussi plus complexe, mais vraiment nécéssaire pour une fréquence comme celle-ci. Ça donne une idée du défi d’analyse de données de cette industrie, ce n’est qu’une preuve de plus que ceux qui oeuvre dans ce domaine sont vraiment au top. Chapeau!

Quand j’aurai un peu de temps libre, je vais faire un modèle sur de vraies données pour voir si un modèle exponentiel double va avoir un bon fit, et ainsi éviter le besoin de données secondaires. Je ferai un autre article de blog sur le sujet! Si vous êtes curieux comme moi, allez lire les articles de M. McCarthy sur le sujet. C’est ce que je vais faire de ce pas!