Analyse de donnĂ©es et intelligence artificielle đŸ€–

Bienvenue dans ce nouveau projet oĂč nous utiliserons l’IA (plus prĂ©cisĂ©ment, les grands modĂšles linguistiques comme ChatGPT, Gemini et Claude) pour nous aider Ă  analyser des donnĂ©es ! Ces modĂšles sont remarquablement efficaces pour extraire, reformater, nettoyer et catĂ©goriser des donnĂ©es.

En mars 2025, j’ai assistĂ© Ă  une excellente session Ă  NICAR prĂ©sentĂ©e par Ben Welsh et Derek Willis. Ils nous ont montrĂ© comment catĂ©goriser des demandes de remboursement Ă  l’aide de ces outils fascinants.

Leur projet (en Python) a Ă©tĂ© une rĂ©vĂ©lation. J’ai donc dĂ©cidĂ© d’implĂ©menter de nouvelles mĂ©thodes dans la bibliothĂšque Simple Data Analysis (SDA) (donnez-lui une ⭐ !) pour tirer le meilleur parti des LLM. Je vais vous montrer comment utiliser ces mĂ©thodes avec une clĂ© API Google AI Studio, mais elles fonctionnent Ă©galement avec Vertex AI et Ollama.

Ben a gentiment acceptĂ© que je reproduise son tutoriel, mais cette fois en TypeScript ! Nous utiliserons Deno et VS Code. Consultez la leçon Installation si nĂ©cessaire. Cependant, vous pouvez le faire avec l’engin d’exĂ©cution JS/TS et l’éditeur de code de votre choix !

Si vous ĂȘtes bloquĂ©, il pourrait ĂȘtre utile de revoir les leçons prĂ©cĂ©dentes expliquant les bases de SDA :

C’est parti pour le code !

Vous voulez ĂȘtre prĂ©venu quand de nouvelles leçons sont publiĂ©es ? Abonnez-vous Ă  l'infolettre ✉ et donnez une ⭐ au cours sur GitHub pour me garder motivé ! Cliquez ici pour me contacter.

Configuration

Pour configurer tout ce dont nous avons besoin, utilisons setup-sda comme dans les leçons précédentes.

Créez un nouveau dossier, ouvrez-le avec VS Code et exécutez : deno -A jsr:@nshiab/setup-sda

Une capture d'écran de VS Code aprÚs l'exécution de setup-sda.

Obtenir une clé API

Dans cette leçon, nous allons utiliser les LLM de Google. Pour interagir avec eux via notre code, nous avons besoin d’une clĂ© API. Rendez-vous sur Google AI Studio, crĂ©ez un compte si vous n’en avez pas dĂ©jĂ  un, et cliquez sur Obtenir une clĂ© API.

Une capture d'écran de Google AI Studio.

Cliquez ensuite sur Créer une clé API.

Suivez les Ă©tapes suggĂ©rĂ©es. Si on vous demande d’utiliser un projet Google Cloud, sĂ©lectionnez Gemini si disponible ; sinon, crĂ©ez-en un nouveau.

Une capture d'écran de Google AI Studio.

Une fois que vous avez copié votre nouvelle clé API, créez un nouveau fichier .env dans votre projet et collez-la-y. Ajoutez également le modÚle que nous utiliserons, qui est gemini-2.0-flash-lite.

.env
AI_KEY=your_api_key_here
AI_MODEL=gemini-2.0-flash-lite

.env est un fichier que nous utilisons habituellement pour les variables d’environnement, qui sont souvent des identifiants qui ne doivent pas ĂȘtre partagĂ©s. Lorsque vous configurez tout avec setup-sda, ce fichier est automatiquement ajoutĂ© Ă  .gitignore, il ne sera donc pas commitĂ© par Git et ne sera pas poussĂ© sur GitHub.

Variables d'environnement dans VS Code.

Maintenant, il nous suffit de charger ces variables en mémoire.

Installez la bibliothĂšque @std/dotenv de l’équipe Deno en exĂ©cutant deno add jsr:@std/dotenv.

Ensuite, ajoutez import "@std/dotenv/load"; en haut de votre fichier sda/main.ts. Maintenant, chaque fois que vous exécuterez ce code, Deno chargera automatiquement les variables de votre fichier .env !

Et nous sommes prĂȘts Ă  dĂ©coller ! 🚀

Une capture d'écran montrant comment charger les variables d'environnement.

À propos de cette API


Avant d’exĂ©cuter du code, il y a quelque chose que je veux vous montrer.

Dans Google AI Studio, aprÚs avoir cliqué sur Obtenir une clé API, vous avez accÚs à votre Facturation de votre plan API. Sélectionnez le modÚle que nous allons utiliser : Gemini 2.0 Flash Lite.

LĂ , vous verrez que vous avez un compte gratuit qui vous permet de faire 30 requĂȘtes par minute avec ce modĂšle (cela a peut-ĂȘtre changĂ© depuis que j’ai Ă©crit ceci).

Et vous remarquerez Ă©galement que
 les donnĂ©es que vous allez envoyer seront utilisĂ©es par Google ! Alors, soyez trĂšs prudent : n’envoyez pas de donnĂ©es sensibles via cette API !

Comme vous le savez peut-ĂȘtre dĂ©jĂ , rien n’est vraiment gratuit dans la vie
 đŸ„Č

Si vous avez un compte pro ou entreprise, ils dĂ©clarent qu’ils n’utiliseront pas vos donnĂ©es pour amĂ©liorer leurs produits. Mais si vous avez besoin d’une confidentialitĂ© totale, je vous montrerai Ă  la fin comment utiliser Ollama pour exĂ©cuter des modĂšles localement sur votre machine.

Facturation du plan Google AI.

Les données

L’objectif du projet est de catĂ©goriser des demandes de remboursement. Nous allons utiliser les donnĂ©es CSV du tutoriel de Ben. Elles sont hĂ©bergĂ©es sur GitHub et contiennent 250 lignes.

Ben a manuellement catĂ©gorisĂ© la colonne payee. Nous demanderons Ă  notre modĂšle de faire le mĂȘme exercice, puis nous comparerons les rĂ©sultats humains et ceux de l’IA.

Les données d'exemple.

Pour récupérer les données, nous créons une nouvelle table data et mettons en cache les résultats de loadData, qui récupÚre les données de GitHub. Comme les données ne changeront pas, la mise en cache est logique. Elles seront stockées localement dans le dossier .sda-cache. Lorsque vous configurez tout avec setup-sda, ce dossier caché est ajouté à votre .gitignore.

Nous affichons également la table dans le terminal.

Pour exĂ©cuter ce code et le surveiller, exĂ©cutez deno task sda dans votre terminal. À chaque fois que nous allons modifier le code et le sauvegarder, il sera automatiquement exĂ©cutĂ©. Pratique!

sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB();
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.logTable();
 
await sdb.done();

Chargement des données depuis GitHub.

💡

Si vous voulez supprimer le cache, vous pouvez supprimer manuellement .sda-cache ou exécuter deno task clean dans votre terminal.

Envoi de données au modÚle

Pour envoyer des donnĂ©es et des instructions au modĂšle, nous devons utiliser la mĂ©thode aiRowByRow sur notre table. Si vous ĂȘtes curieux, vous trouverez la documentation ici.

Son utilisation est assez simple :

  • Tout d’abord, passez la colonne contenant les donnĂ©es que vous souhaitez envoyer Ă  l’IA (ici, c’est payee).
  • Ensuite, indiquez le nom de la nouvelle colonne qui sera créée pour stocker les rĂ©sultats de l’IA (ici, nous la nommons categoryAI).
  • Et enfin, tapez votre prompt. Ici, nous demandons au LLM de catĂ©goriser le payee.

Le quatriùme argument est facultatif. C’est un objet avec quelques options :

  • rateLimitPerMinute garantira que nous respectons notre quota d’API. Si nous envoyons des requĂȘtes trop rapidement, la mĂ©thode attendra la durĂ©e nĂ©cessaire pour Ă©viter que les requĂȘtes ne soient rejetĂ©es.
  • verbose affichera des informations supplĂ©mentaires dans le terminal pendant le traitement des donnĂ©es.
sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB();
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.logTable();
 
await sdb.done();

Si vous exécutez ce code, vous verrez votre prompt ainsi que des instructions supplémentaires ajoutées par la méthode.

L’option verbose ajoute des informations supplĂ©mentaires utiles. Les tokens reprĂ©sentent la quantitĂ© d’informations envoyĂ©es et reçues par l’IA. Sur cette base, la mĂ©thode vous donne une estimation du coĂ»t de la requĂȘte si vous avez un compte avec paiement Ă  l’utilisation.

Actuellement, les donnĂ©es sont classifiĂ©es par le modĂšle d’IA, mais
 c’est un peu lent.

Parce que nous avons 250 lignes de donnĂ©es, et que nous ne pouvons envoyer que 30 requĂȘtes par minute, classer l’ensemble des donnĂ©es prendra
 plus de 8 minutes !

Nous pouvons sûrement accélérer cela !

Envoi d'une ligne Ă  la fois.

Accélérer le processus

Lots

Au lieu d’envoyer une ligne à la fois, nous pourrions en envoyer un lot !

Dans le code ci-dessous, nous utilisons l’option batchSize pour envoyer 10 lignes Ă  la fois. Maintenant, le traitement de l’ensemble des donnĂ©es prend moins d’une minute !

C’est gĂ©nial, mais nous pouvons pousser cela un peu plus loin


sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB();
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    batchSize: 10,
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.logTable();
 
await sdb.done();

Envoi de lots de données.

Concurrence

L’une des nombreuses fonctionnalitĂ©s formidables de TypeScript et JavaScript est la concurrence. Au lieu de faire une seule requĂȘte Ă  l’API Ă  la fois, vous en envoyez plusieurs simultanĂ©ment et attendez ensuite les rĂ©sultats en parallĂšle.

Tirer parti de cette fonctionnalitĂ© du langage peut accĂ©lĂ©rer l’exĂ©cution de notre code !

Puisque nous avons 250 lignes de donnĂ©es, nous pourrions envoyer des lots de 17 lignes avec 15 requĂȘtes concurrentes pour traiter toutes nos donnĂ©es en moins de
 2 secondes !

Ces 15 requĂȘtes nous maintiennent en dessous de la limite de quota de 30, ce qui est important.

Un conseil : pour exĂ©cuter ce code, assurez-vous de n’avoir pas envoyĂ© de requĂȘtes depuis au moins une minute ; sinon, vous pourriez atteindre la limite de quota du plan gratuit.

sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB({ logDuration: true });
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    batchSize: 17,
    concurrent: 15,
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.logTable();
 
await sdb.done();

Envoi de requĂȘtes concurrentes.

Mise en cache

Il y a une derniĂšre astuce que nous pouvons utiliser pour rendre notre code plus efficace : le mise en cache.

Nous pourrions stocker les rĂ©sultats envoyĂ©s par l’IA dans des fichiers localement. Si nous rĂ©exĂ©cutons notre code et que le prompt/les donnĂ©es n’ont pas changĂ©, nous pourrions utiliser ce que nous avons stockĂ© sur notre machine au lieu d’attendre Ă  nouveau le modĂšle d’IA.

C’est exactement ce que l’option cache vous permet de faire.

sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB({ logDuration: true });
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    batchSize: 17,
    concurrent: 15,
    cache: true,
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.logTable();
 
await sdb.done();

Si vous exĂ©cutez ce code, vous remarquerez un nouveau dossier cachĂ© .journalism-cache. Les fichiers qu’il contient sont des fichiers JSON stockant les rĂ©ponses de l’IA. Il fonctionne comme le dossier .sda-cache prĂ©cĂ©dent, mais il est créé par la fonction askAI de ma bibliothĂšque journalism, que la mĂ©thode aiRowByRow utilise pour interagir avec le modĂšle d’IA.

Lors de la premiĂšre exĂ©cution, les requĂȘtes sont faites et le script prend autant de temps d’exĂ©cution qu’auparavant.

Mise en cache des données.

Mais si vous rĂ©exĂ©cutez ce script (CTRL + S dans main.ts devrait faire l’affaire), il est maintenant beaucoup plus rapide ! Il ne prend que quelques millisecondes ! Au lieu de faire les requĂȘtes, le code rĂ©cupĂšre les donnĂ©es stockĂ©es sur votre machine.

Bien sĂ»r, si vous modifiez le prompt ou les donnĂ©es envoyĂ©es au modĂšle, la mĂ©thode saura qu’elle doit faire de nouvelles requĂȘtes et stocker de nouveaux rĂ©sultats. Mais si vous ne touchez pas aux arguments aiRowByRow modifiant les requĂȘtes, vous pouvez travailler en toute sĂ©curitĂ© sur d’autres choses sans Ă©puiser vos quotas d’API et Ă  pleine vitesse !

Récupération depuis le cache.

💡

Si vous voulez supprimer le cache, vous pouvez supprimer manuellement .journalism-cache ou exécuter deno task clean dans votre terminal.

Tester les résultats

Tests

Bien qu’ils s’amĂ©liorent Ă  un rythme incroyable, les LLM ne comprennent pas toujours correctement vos instructions. Et lorsque vous faites des dizaines, des centaines ou des milliers de requĂȘtes, il y a toujours une chance qu’ils renvoient quelque chose d’inattendu.

C’est pourquoi les tests sont importants.

Dans le code ci-dessous, nous utilisons un test (une simple fonction) pour vérifier si la catégorie renvoyée pour chaque entrée correspond aux catégories attendues.

S’il y a une catĂ©gorie inattendue, nous lançons une erreur, et avec l’option retry, nous spĂ©cifions que nous voulons rĂ©essayer cinq fois avant d’abandonner et de tout arrĂȘter.

sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB({ logDuration: true });
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    batchSize: 17,
    concurrent: 15,
    cache: true,
    test: (dataPoint) => {
      if (typeof dataPoint !== "string") {
        throw new Error("Not a string");
      }
      if (!["Restaurant", "Bar", "Hotel", "Other"].includes(dataPoint)) {
        throw new Error("Unexpected category");
      }
    },
    retry: 5,
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.logTable();
 
await sdb.done();

De mon cĂŽtĂ©, aucune erreur n’a Ă©tĂ© gĂ©nĂ©rĂ©e. Ça a l’air bon !

Test des résultats.

Précision

Puisque nous avons testé les résultats, nous savons que nous avons les bonnes catégories, mais ont-elles été correctement attribuées ?

Il est temps de vérifier la précision du modÚle !

Puisque Ben a dĂ©jĂ  classĂ© les entreprises dans la colonne category et que nous avons mis les rĂ©sultats de l’IA dans la colonne categoryAI, utilisons quelques mĂ©thodes de SDA pour ce faire !

PremiĂšrement, crĂ©ons une nouvelle colonne pour vĂ©rifier si l’IA a retournĂ© la bonne rĂ©ponse, en supposant que Ben avait raison.

En utilisant cette colonne, nous pouvons ensuite enregistrer toutes les occurrences de rĂ©ponses diffĂ©rentes entre Ben et l’IA.

Nous pouvons Ă©galement calculer la proportion de rĂ©ponses correctes de l’IA.

sda/main.ts
import "@std/dotenv/load";
import { SimpleDB } from "@nshiab/simple-data-analysis";
 
const sdb = new SimpleDB({ logDuration: true });
 
const data = sdb.newTable("data");
await data.cache(async () => {
  await data.loadData(
    "https://raw.githubusercontent.com/palewire/first-llm-classifier/refs/heads/main/_notebooks/sample.csv",
  );
});
 
await data.aiRowByRow(
  "payee",
  "categoryAI",
  `Categorize the payee into one of the following categories: Restaurant, Bar, Hotel or Other. `,
  {
    batchSize: 17,
    concurrent: 15,
    cache: true,
    test: (dataPoint) => {
      if (typeof dataPoint !== "string") {
        throw new Error("Not a string");
      }
      if (!["Restaurant", "Bar", "Hotel", "Other"].includes(dataPoint)) {
        throw new Error("Unexpected category");
      }
    },
    retry: 5,
    rateLimitPerMinute: 30,
    verbose: true,
  },
);
 
await data.addColumn("correctCategory", "boolean", `category === categoryAI`);
await data.logTable({
  conditions: `correctCategory === false`,
  nbRowsToLog: "all",
});
 
await data.summarize({
  categories: "correctCategory",
});
await data.proportionsVertical("count", "perc");
await data.logTable();
 
await sdb.done();

Et
 voici le résultat !

Si nous vĂ©rifions les lignes avec des rĂ©ponses diffĂ©rentes entre Ben et l’IA, nous pouvons remarquer que
 Ben a fait quelques erreurs !

Par exemple, El POLLO INKA et ELLA DINNING ROOM devraient ĂȘtre des restaurants. Et FAIRMONT BATTERY WHARF devrait ĂȘtre un hĂŽtel. (Si vous ne voyez pas ces erreurs, c’est peut-ĂȘtre parce que Ben les a corrigĂ©es !)

Pour le reste, comme nous n’avons pas les adresses, nous accorderons Ă  Ben le bĂ©nĂ©fice du doute.

Cela signifie que le modÚle a atteint un taux de précision supérieur à 94 % ! Pas mal compte tenu de la simplicité de nos instructions et de la rapidité de traitement.

Selon ce sur quoi vous travaillez, peut-ĂȘtre que ce niveau de prĂ©cision est suffisant si vous le divulguez dans votre publication. Une chose est certaine : le faire Ă  la main aurait Ă©tĂ© beaucoup plus lent (ou tout simplement impossible s’il y avait des millions de lignes), et vous auriez Ă©galement fait des erreurs !

Si vous n’ĂȘtes pas satisfait de la prĂ©cision, vous pouvez affiner votre prompt, donner des exemples dans vos instructions, prĂ©traiter les donnĂ©es ou simplement passer Ă  un autre modĂšle. Mais au moins, en vĂ©rifiant la prĂ©cision, vous ne ferez pas de changements Ă  l’aveugle ! Vous saurez quand les choses s’amĂ©liorent et quand elles s’aggravent.

Calcul du taux de précision.

Conclusion

Ici, nous avons classifié des données avec un modÚle Gemini. Mais vous pouvez faire bien plus que cela, comme nettoyer, formater et extraire des données avec des LLM !

Et en utilisant la méthode aiRowByRow de SDA, vous pouvez le faire facilement, en quelques lignes de code, à grande échelle !

Si vous avez des donnĂ©es sensibles que vous ne voulez pas envoyer Ă  Google ou Ă  un autre fournisseur d’IA, vous pouvez Ă©galement utiliser Ollama. Il suffit de le dĂ©marrer et de passer les variables suivantes dans votre fichier .env. SDA basculera automatiquement vers l’utilisation du modĂšle local installĂ© sur votre machine pour traiter les donnĂ©es.

sda/main.ts
OLLAMA=true
AI_MODEL=gemma3:4b

Parfois, les modĂšles open-source lĂ©gers ont plus de difficultĂ© Ă  renvoyer directement des listes (ce qui est la rĂ©ponse attendue pour aiRowByRow). Si c’est le cas, j’ai implĂ©mentĂ© l’option clean qui vous permet de modifier directement la rĂ©ponse du LLM, par exemple en la restructurant en une liste.

Bien qu’il s’agisse d’une technologie exceptionnelle, n’oubliez pas que les rĂ©sultats ne sont pas garantis avec les LLM. Et, dans mes projets, plus les lots sont grands, plus les rĂ©sultats ont tendance Ă  ĂȘtre mauvais


Mais malgré tout, ils peuvent considérablement accélérer vos recherches et vos analyses. Ils sont incroyablement rapides et faciles à utiliser.

Alors, si vous les essayez dans l’un de vos projets, faites-le moi savoir !

Et merci encore à Ben Welsh et Derek Willis qui ont créé la version initiale de ce tutoriel.

Vous avez aimé ? Vous voulez ĂȘtre prĂ©venu quand de nouvelles leçons sont publiĂ©es ? Abonnez-vous Ă  l'infolettre ✉ et donnez une ⭐ au cours sur GitHub pour me garder motivé ! Contactez-moi si vous avez des questions.