Pandas Qui Apprend Sql

Si tu veux travailler dans la Data, que ce soit Data Analyst, Data Scientist ou Data Engineer, tu dois nécessairement maîtriser SQL et Pandas. Voici 30 requêtes SQL expliquées avec le code Pandas équivalent.

Petit retour dans le passé, dans un monde dominé par SQL depuis 1974, Pandas est apparu en 2008, offrant des fonctionnalités attrayantes telles que la visualisation intégrée et la manipulation flexible des données. Il est rapidement devenu l’outil de prédilection pour l’exploration des données, éclipsant SQL.

30 requêtes SQL expliquées avec le code équivalent Pandas

Mais ne vous y trompez pas, le langage SQL a encore de beaux jours devant lui. C’est le deuxième langage le plus demandé et le troisième qui connaît la plus forte croissance dans le domaine de la Data Science. Ainsi, même si Pandas vole la vedette, SQL reste une compétence essentielle pour tout data scientist.

Voyons à quel point il est facile d’apprendre SQL quand on connaît déjà Pandas.

Connexion à une base de données

La configuration d’un espace de travail SQL et la connexion à une base de données d’échantillons peuvent s’avérer très difficiles. Tout d’abord, tu dois installer ta version préférée de SQL (PostgreSQL, MySQL, etc.) et télécharger un IDE SQL. Faire cela ici nous éloignerait de l’objectif de l’article, c’est pourquoi nous utiliserons un raccourci.

Plus précisément, nous exécuterons directement des requêtes SQL dans un notebook Jupyter sans étapes supplémentaires. Tout ce que nous avons à faire est d’installer le paquetage ipython-sql à l’aide de pip :

pip install ipython-sql

Après l’installation, démarre une nouvelle session Jupyter et exécute cette commande dans le notebook :

%load_ext sql

et le tour est joué !

Pour illustrer le fonctionnement des instructions SQL de base, nous utiliserons la base de données Chinook SQLite, qui comporte 11 tables.

Pour charger l’ensemble de données et ses 11 tables en tant que variables distinctes dans notre environnement, nous pouvons exécuter :

%sql sqlite:///data/chinook.db

La déclaration commence par %sql, une commande magique en ligne qui indique à l’interpréteur du notebook que nous allons exécuter des commandes SQL. Elle est suivie par le chemin d’accès à la base de données Chinook téléchargée.

Les chemins valides doivent toujours commencer par le préfixe sqlite:/// pour les bases de données SQLite. Ci-dessus, nous nous connectons à la base de données stockée dans le dossier ‘data’ du répertoire courant. Si tu souhaites transmettre un chemin absolu, le préfixe doit commencer par quatre barres obliques – sqlite:////

Premier aperçu des tables

La première chose que nous faisons toujours dans Pandas est d’utiliser la fonction .head() pour jeter un premier coup d’œil aux données. Voyons comment faire cela en SQL :

%%sql

SELECT * FROM customers
LIMIT 5

Le premier mot-clé de la requête ci-dessus est SELECT. Il est équivalent à l’opérateur crochets dans Pandas, qui permet de sélectionner des colonnes spécifiques. Mais le mot-clé SELECT est suivi d’un * (astérisque). * est un opérateur SQL qui sélectionne tout (toutes les lignes et toutes les colonnes) dans une table spécifiée après le mot-clé FROM. LIMIT est utilisé pour limiter le nombre de résultats renvoyés. La requête ci-dessus est donc équivalente à la fonction df.head().

Si tu ne souhaites pas sélectionner toutes les colonnes, tu peux spécifier un ou plusieurs noms de colonnes après le mot-clé SELECT :

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
LIMIT 10

L’opération Pandas équivalente est la suivante :

tracks[['Name', 'Composer', 'UnitPrice']].head(10)

Un autre mot-clé utile en SQL est DISTINCT. L’ajout de ce mot-clé devant le nom d’une colonne permet d’obtenir des valeurs uniques :

%%sql

SELECT DISTINCT FirstName
FROM employees

-- équivalent à `.unique()`

Les commentaires en SQL sont écrits avec des doubles tirets.

Compter le nombre de lignes

Tout comme Pandas dispose d’un attribut .shape sur ses DataFrames, SQL dispose d’une fonction COUNT pour afficher le nombre de lignes d’une table :

%%sql

SELECT COUNT(*) FROM tracks

Une information plus utile serait de compter le nombre de valeurs uniques dans une colonne particulière. Nous pouvons le faire en ajoutant le mot-clé DISTINCT dans COUNT :

%%sql 

SELECT COUNT(DISTINCT FirstName) FROM employees

-- équivalent à `employees['FirstName'].nunique()`

Filtrer les résultats avec les clauses WHERE

Se contenter de regarder et de compter les lignes est plutôt maladroit. Voyons comment nous pouvons filtrer les lignes en fonction de conditions.

Tout d’abord, regardons les chansons qui coûtent plus d’un dollar :

%%sql

SELECT * FROM tracks
WHERE UnitPrice > 1.0
LIMIT 10

--tracks[tracks['UnitPrice' > 1.0]]

Les instructions conditionnelles sont écrites dans la clause WHERE, qui vient toujours après FROM et avant les mots-clés LIMIT. L’utilisation des conditionnelles est assez similaire à la façon dont nous le faisons dans Pandas, mais j’ose dire que la version SQL est plus lisible.

Tu peux également utiliser la fonction COUNT lorsque tu utilises des conditionnelles. Par exemple, voyons le nombre de chansons dont le prix est compris entre 1 et 10 dollars :

%%sql

SELECT COUNT(*) FROM tracks
WHERE UnitPrice > 1.0 AND UnitPrice < 10

-- tracks[(tracks['UnitPrice'] > 1) & (tracks['UnitPrice'] < 10)]

Comme tu peux le constater, la version SQL est beaucoup plus lisible.

Ci-dessus, nous avons enchaîné deux conditions à l’aide de l’opérateur booléen AND. D’autres opérateurs booléens (OR, NOT) sont utilisés de la même manière.

Voyons maintenant toutes les factures dont la ville de facturation est Paris ou Berlin :

%%sql

SELECT BillingAddress, BillingCity, Total
FROM invoices
WHERE BillingCity = 'Paris' OR BillingCity = 'Berlin'
LIMIT 5

--invoices[(invoices['BillingCity'] == 'Paris') |
--         (invoices['BillingCity'] == 'Berlin')]

L’opérateur d’égalité en SQL ne nécessite qu’un seul signe « = » (égal). L’opérateur d’inégalité est représenté par les opérateurs « != » ou « <> » :

%%sql

SELECT BillingAddress, BillingCity, Total
FROM invoices
WHERE BillingCity != 'Paris' AND BillingCity <> 'Berlin'
LIMIT 5

Filtrage plus facile avec BETWEEN et IN

Des conditionnelles similaires sont utilisées très souvent, et les écrire avec de simples booléens devient encombrant. Par exemple, Pandas possède la fonction .isin() qui vérifie si une valeur appartient à une liste de valeurs.

Si nous voulions sélectionner les factures de cinq villes, nous devrions écrire cinq conditions enchaînées. Heureusement, SQL prend en charge un opérateur IN similaire à .isin(), ce qui nous évite d’avoir à le faire :

%%sql

SELECT * 
  FROM invoices
 WHERE BillingCity IN ('Berlin', 'Paris', 'New York', 'Boston', 'London')
 LIMIT 5
    
--invoices[invoices['BillingCity'].isin(
--      ('Berlin', 'Paris', 'New York', 'Boston', 'London')
--)]

La liste des valeurs après IN doit être donnée sous la forme d’un tuple, et non d’une liste. Tu peux également neutraliser la condition à l’aide du mot-clé NOT :

%%sql

SELECT * 
  FROM invoices
 WHERE BillingCity NOT IN ('Berlin', 'Paris', 'New York', 'Boston', 'London')
 LIMIT 5
    
--invoices[~invoices['BillingCity'].isin(
--    ('Berlin', 'Paris', 'New York', 'Boston', 'London')
--)]

Une autre opération de filtrage courante sur les colonnes numériques consiste à sélectionner des valeurs à l’intérieur d’une plage. Pour cela, le mot-clé BETWEEN peut être utilisé, ce qui est équivalent à pd.Series.between() :

%%sql

SELECT BillingCity, Total
FROM invoices
WHERE Total BETWEEN 5 AND 15
LIMIT 5

--invoices[invoices['Total'].between(5, 15)]

Vérifier les éléments manquants

Toutes les sources de données comportent des valeurs manquantes, et les bases de données ne font pas exception. Tout comme il existe plusieurs façons d’explorer les valeurs manquantes dans Pandas, il existe des mots-clés spécifiques qui vérifient l’existence de valeurs nulles dans SQL. La requête ci-dessous compte le nombre de lignes avec des valeurs manquantes dans BillingState :

%%sql

SELECT COUNT(*) FROM
invoices
WHERE BillingState IS NULL

--invoices['BillingState'].isnull().sum()

Tu peux ajouter le mot-clé NOT entre IS et NULL pour écarter les valeurs manquantes d’une colonne particulière :

%%sql

SELECT InvoiceDate, BillingCountry 
FROM invoices
WHERE Total IS NOT NULL
LIMIT 10

Meilleure correspondance des chaînes de caractères avec LIKE

Dans la clause WHERE, nous avons filtré les colonnes sur la base de valeurs textuelles exactes. Mais il arrive souvent que l’on veuille filtrer des colonnes textuelles sur la base d’un motif (ou pattern). Dans Pandas (et Python pur), nous utiliserions des expressions régulières (regex) pour la recherche de motifs, qui sont très puissantes, mais les expressions régulières demandent du temps pour être maîtrisées.

SQL propose une alternative : le caractère générique « %« , qui permet de faire correspondre n’importe quel caractère 0 fois ou plus. Par exemple, la chaîne « gr% » correspond à « great », « groom », « greed », et « %ex% » correspond à tout texte contenant « ex » au milieu, etc. Voyons comment l’utiliser avec SQL :

%%sql

SELECT Name, Composer, UnitPrice 
  FROM tracks
 WHERE Name LIKE 'B%'
 LIMIT 5

--tracks[tracks['Name'].str.startswith('B')]

La requête ci-dessus permet de trouver toutes les chansons qui commencent par « B ». La chaîne qui contient le caractère générique doit venir après le mot-clé LIKE.

Trouvons maintenant toutes les chansons dont le titre contient le mot « beautiful » :

%%sql

SELECT Name, Composer, UnitPrice
  FROM tracks
 WHERE Name LIKE '%beautiful%'

--tracks[tracks['Name'].str.contains('beautiful')]

Tu pourrais également utiliser d’autres opérateurs booléens en plus de LIKE :

%%sql

SELECT Name, Composer, UnitPrice
  FROM tracks
 WHERE (Name LIKE 'F%') AND (UnitPrice > 1.0)

--tracks[(tracks['Name'].str.startswith('F')) & (tracks['UnitPrice'] > 1.0)]

Il existe de nombreux autres caractères génériques dans SQL qui ont des fonctionnalités différentes. Tu peux consulter la liste complète et leur utilisation ici.

Fonctions d’agrégation en SQL

Il est également possible d’effectuer des opérations arithmétiques de base sur les colonnes. Ces opérations sont appelées fonctions d’agrégation en SQL, et les plus courantes sont AVG, SUM, MIN, MAX. Leur fonction devrait être claire d’après leur nom :

%%sql 

SELECT SUM(Total), MAX(Total), 
       MIN(Total), AVG(Total)
FROM invoices

Les fonctions d’agrégation ne donnent qu’un seul résultat pour la colonne sur laquelle elles sont utilisées. Cela signifie que tu ne peux pas agréger une colonne et sélectionner d’autres colonnes non agrégées :

%%sql

SELECT AVG(Total), BillingCity, BillingAddress
FROM invoices;

-- the result will be a single row because
-- of the presence of aggregate functions

Il est tout aussi facile de combiner des fonctions agrégées avec des conditions à l’aide de clauses WHERE :

%%sql

SELECT AVG(Total), BillingCity
FROM invoices
WHERE BillingCity = 'Paris'

-- invoices[invoices['BillingCity']]['Total'].mean()

Il est également possible d’utiliser des opérateurs arithmétiques tels que +, , *, / sur les colonnes et les nombres simples. Lorsqu’ils sont utilisés sur des colonnes, l’opération est effectuée par élément :

%%sql

SELECT SUM(Total) / COUNT(Total)
FROM invoices

-- recherche manuelle de la moyenne

Une chose à noter à propos des opérations arithmétiques : si tu effectues des opérations sur des nombres entiers uniquement, SQL pense que tu attends un nombre entier comme réponse :

%%sql

SELECT 10 / 3

Au lieu de renvoyer 3.33…, le résultat est 3. Pour obtenir un résultat flottant, tu dois utiliser au moins un nombre flottant dans la requête ou utiliser que des nombres flottants pour être sûr :

%%sql

SELECT 10.0 / 3.0

Sur la base de ces connaissances, calculons la durée moyenne d’une chanson en minutes :

%%sql

SELECT Milliseconds / 1000.0 / 60.0
FROM tracks
LIMIT 10

Si tu prêeetes attention à la colonne ci-dessus, son nom est écrit comme « la requête utilisée pour générer cette colonne« . En raison de ce comportement, l’utilisation de longs calculs, tels que la recherche de l’écart type ou de la variance d’une colonne, peut poser problème, car le nom de la colonne sera aussi grand que la requête elle-même.

Pour éviter cela, SQL permet l’aliasing, similaire à l’aliasing des instructions d’importation en Python. Par exemple :

%%sql

SELECT SUM(Total) as sum_total
FROM invoices

L’utilisation du mot-clé as après un seul élément dans une instruction SELECT indique à SQL qu’il s’agit d’un alias. Voici d’autres exemples :

%%sql

SELECT SUM(Total) as sum_total,
       MAX(Total) as max_total,
       MIN(Total) as min_total,
       SUM(Total) / COUNT(Total) as mean_total
FROM invoices

Tu peux utiliser l’alias tout aussi facilement pour les colonnes dont le nom est long.

Ordonner les résultats en SQL

Tout comme la méthode sort_values de Pandas, SQL prend en charge l’ordonnancement des colonnes via la clause ORDER BY. En passant le nom d’une colonne après la clause, les résultats sont triés par ordre croissant :

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer <> 'None'
ORDER BY Composer
LIMIT 10

-- tracks.sort_values('Compose')[['Name', 'Compose', 'UnitPrice']]

Nous classons la table tracks dans l’ordre croissant du nom du compositeur. Note que l’instruction ORDER BY doit toujours être placée après la clause WHERE. Il est également possible de passer deux colonnes ou plus à l’instruction ORDER BY :

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer <> 'None'
ORDER BY UnitPrice, Composer, Name
LIMIT 10

-- tracks.sort_values(['UnitPrice', 'Composer', 'Name'])\
-- [['Name', 'Compose', 'UnitPrice']]

Tu peux également inverser l’ordre en utilisant le mot-clé DESC après chaque nom de colonne :

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer != 'None'
ORDER BY UnitPrice DESC, Composer DESC, Name ASC
LIMIT 10

-- tracks.sort_values(['UnitPrice', 'Composer', 'Name'])\
-- [['Name', 'Compose', 'UnitPrice']]

La requête ci-dessus renvoie trois colonnes après avoir ordonné le prix unitaire et la composition par ordre décroissant et le nom par ordre croissant (ASC est un mot-clé par défaut).

Grouper en SQL

L’une des fonctions les plus puissantes de Pandas est le groupby. Tu peux l’utiliser pour transformer une table en pratiquement n’importe quelle forme. Sa cousine très proche en SQL – la clause GROUP BY – peut être utilisée pour obtenir la même fonctionnalité. Par exemple, la requête ci-dessous compte le nombre de chansons dans chaque genre :

%%sql

SELECT GenreID, COUNT(*) as genre_count
FROM tracks
GROUP BY GenreId
LIMIT 10

-- tracks.groupby('GenreId')['GenreID'].count()

La différence entre GROUP BY en SQL et groupby en Pandas est que SQL ne permet pas de sélectionner des colonnes qui n’ont pas été données dans la clause GROUP BY. Par exemple, l’ajout d’une colonne libre supplémentaire dans la requête ci-dessus génère une erreur :

%%sql

SELECT Name, GenreID, COUNT(*) as genre_count
FROM tracks
GROUP BY GenreId
syntax error at or near "GROUP"
LINE 1: GROUP BY GenreId;

Toutefois, tu peux choisir autant de colonnes que tu le souhaites dans l’instruction SELECT, à condition d’utiliser une fonction d’agrégation sur ces colonnes :

%%sql

SELECT GenreId, AlbumId, 
       COUNT(*) as genre_count,
       AVG(Milliseconds) / 1000.0 / 60.0 as avg_duration,
       AVG(UnitPrice) as avg_price
  FROM tracks
 GROUP BY GenreId, AlbumId
 LIMIT 10

La requête ci-dessus inclut presque tous les sujets que nous avons appris jusqu’à présent. Nous regroupons les données par ID d’album et ID de genre, et pour chaque groupe, nous calculons la durée et le prix moyens d’une chanson. Nous faisons également un usage efficace de l’aliasing.

Nous pouvons rendre la requête encore plus puissante en l’ordonnant en fonction de la durée moyenne et du nombre de genres :

%%sql

SELECT GenreId, AlbumId, 
       COUNT(*) as genre_count,
       AVG(Milliseconds) / 1000.0 / 60.0 as avg_duration,
       AVG(UnitPrice) as avg_price
  FROM tracks
 GROUP BY GenreId, AlbumId
 ORDER BY avg_duration DESC, genre_count DESC
 LIMIT 10

Attention à la façon dont nous utilisons les noms d’alias des fonctions d’agrégation dans la clause ORDER BY. Une fois que tu as donné un alias à une colonne ou au résultat d’une fonction d’agrégation, tu ne peux t’y référer que par leur alias pour le reste de la requête.

Utiliser des conditionnels avec HAVING

Par défaut, SQL n’autorise pas le filtrage conditionnel à l’aide de fonctions d’agrégation dans la clause WHERE. Par exemple, nous voulons sélectionner uniquement les genres dont le nombre de chansons est supérieur à 100. Essayons avec la clause WHERE :

%%sql

SELECT GenreId
FROM tracks
GROUP BY GenreId
WHERE COUNT(GenreId) > 10

syntax error at or near "WHERE"
LINE 1: where COUNT(GenreId) > 10

La manière correcte de filtrer les lignes sur la base des résultats des fonctions d’agrégation est d’utiliser la clause HAVING :

%%sql

SELECT GenreId
FROM tracks
GROUP BY GenreId
HAVING COUNT(GenreId) > 100

La clause HAVING est généralement utilisée avec GROUP BY. Lorsque tu souhaites filtrer des lignes à l’aide de fonctions d’agrégation, la clause HAVING est la meilleure solution !

Conclusion

Tu devrais maintenant avoir réalisé à quel point SQL peut être puissant et combien il est plus lisible que Pandas. Même si nous avons appris beaucoup de choses, nous n’avons fait qu’effleurer la surface.

Tu peux également consulter la première partie du post SQL vs. Pandas.

Pour des problèmes pratiques, je te recommande Data Lemur si tu te sens aventureux.

Publications similaires

0 Commentaires
Le plus récent
Le plus ancien Le plus populaire
Commentaires en ligne
Afficher tous les commentaires