Lire un CSV Spark avec un schéma explicite

Big DataBeginner
Pratiquer maintenant

Introduction

Les commandes du commerçant arrivent maintenant dans un fichier CSV au lieu d’être écrites directement dans votre liste Python. Un fichier stocke du texte comme 2 et 10.00 ; un calcul doit savoir si ces valeurs représentent du texte, une quantité entière ou un prix décimal. Lire les bons caractères n’est qu’une partie d’une lecture correcte des données.

Vous comparerez le chargement en chaînes par défaut de Spark, l’inférence automatique des types et un schéma explicite. Vous appliquerez ensuite le même schéma à un second fichier ayant le même ordre de champs. Les données métier sont volontairement valides ici pour vous permettre de vous concentrer sur le contrat entre colonnes et valeurs. Un lab ultérieur traitera explicitement les entrées manquantes et invalides.

L’environnement fournit PySpark et deux fichiers CSV locaux d’exercice indépendants. Vous devez comprendre la SparkSession, l’affichage du DataFrame, le schéma et le nombre de lignes du premier lab, ainsi que les bases de Python. Aucun pandas, Hadoop, Hive, installation ou état de VM précédent n’est nécessaire. Utilisez le Terminal.

Lire l’en-tête et observer les colonnes de chaînes

Dans cette étape, vous relierez les positions des champs CSV à la table créée par Spark. Commencez par examiner le fichier réel :

cd /home/labex/project
cat /home/labex/project/data/orders.csv

La première ligne nomme sept colonnes ; les six suivantes sont des commandes. Chaque ligne représente une commande avec un type de produit. Le fichier inclut les statuts payé, en attente et remboursé ; ce lab les lit tous sans appliquer de règle de ventes.

spark.read crée un lecteur de DataFrame. option("header", True) indique au lecteur de traiter la première ligne CSV comme les noms de colonnes plutôt que comme un enregistrement de commande. csv(input_path) nomme le fichier local. Sans inférence de types ni schéma fourni, le lecteur CSV produit des colonnes de chaînes.

Enregistrez et exécutez ce programme complet. Le here-document PYTHON entre apostrophes préserve le texte source inclus, et > écrit le fichier local. bigdata-python utilise l’environnement PySpark préparé :

cat > /home/labex/project/read_orders.py <<'PYTHON'
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("ReadOrderCSV").master("local[2]").getOrCreate()
spark.sparkContext.setLogLevel("ERROR")

input_path = "/home/labex/project/data/orders.csv"
df = spark.read.option("header", True).csv(input_path)
df.printSchema()
df.orderBy("order_id").show(truncate=False)
print("Rows:", df.count())
spark.stop()
PYTHON
bigdata-python /home/labex/project/read_orders.py > /home/labex/project/output/string-orders.txt
cat /home/labex/project/output/string-orders.txt

Vous devez obtenir six lignes et sept colonnes de chaînes, dont quantity et unit_price. Le 2 affiché peut sembler numérique, mais printSchema indique son véritable type string. orderBy("order_id") trie les lignes affichées par identifiant pour un examen prévisible ; le tri est distinct de l’attribution des types.

header=True a empêché l’en-tête de devenir un septième enregistrement. Il n’a pas indiqué à Spark que les prix doivent être des décimaux exacts ni que les dates doivent permettre des opérations de date.

Comparer l’inférence à un contrat de données explicite

Dans cette étape, vous déclarerez le sens de chaque champ CSV. inferSchema=True demande à Spark d’examiner les valeurs et d’en déduire les types. Cela peut être pratique, mais le résultat dépend des données observées ; ce n’est pas un accord durable sur les prix ou les fichiers futurs. Dans cet exemple, l’inférence identifie unit_price comme un double, un nombre à virgule flottante.

Utilisez un schéma explicite pour le contrat prévu. StructType est la collection ordonnée de champs. Chaque StructField contient un nom, un type et True pour autoriser les valeurs manquantes. StringType contient les identifiants et les textes de statut ; IntegerType les quantités ; DecimalType(10, 2) les prix exacts avec dix chiffres au total et deux après la virgule ; DateType les dates du calendrier. Des champs nullable autorisent les valeurs manquantes, mais ne rendent pas automatiquement acceptables des informations métier absentes.

Remplacez le programme par cette version. Elle affiche la description des types déduits pour comparaison, puis lit le même fichier avec votre schéma explicite :

cat > /home/labex/project/read_orders.py <<'PYTHON'
from pyspark.sql.types import StructType, StructField, StringType, IntegerType, DecimalType, DateType
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("ReadOrderCSV").master("local[2]").getOrCreate()
spark.sparkContext.setLogLevel("ERROR")

order_schema = StructType([
    StructField("order_id", StringType(), True),
    StructField("customer_id", StringType(), True),
    StructField("product_id", StringType(), True),
    StructField("quantity", IntegerType(), True),
    StructField("unit_price", DecimalType(10, 2), True),
    StructField("order_status", StringType(), True),
    StructField("order_date", DateType(), True),
])

input_path = "/home/labex/project/data/orders.csv"
inferred = spark.read.option("header", True).option("inferSchema", True).csv(input_path)
print("Inferred schema:", inferred.schema.simpleString())
df = (
    spark.read
    .option("header", True)
    .option("dateFormat", "yyyy-MM-dd")
    .schema(order_schema)
    .csv(input_path)
)
print("Explicit schema:")
df.printSchema()
df.orderBy("order_id").show(truncate=False)
print("Rows:", df.count())
spark.stop()
PYTHON
bigdata-python /home/labex/project/read_orders.py > /home/labex/project/output/typed-orders.txt
cat /home/labex/project/output/typed-orders.txt

Trouvez le schéma explicite sous le résumé de l’inférence. quantity doit être integer, unit_price decimal(10,2) et order_date date. Les six enregistrements doivent toujours être présents. L’option explicite dateFormat décrit les dates d’entrée au format année-mois-jour, correspondant à des valeurs comme 2026-10-14.

L’ordre des champs du schéma correspond à celui de ce CSV. Fournir un schéma n’est pas une instruction générale de réorganiser n’importe quel fichier entrant par nom d’en-tête. Pour cette livraison dont l’ordre est connu, le contrat rend clairs les colonnes et les types prévus. Un schéma ne remplace pas non plus une politique de qualité des données ; vous apprendrez plus tard à traiter les valeurs invalides de façon sûre.

Appliquer le même schéma à une nouvelle livraison

Dans cette étape, vous testerez le contrat sur un second fichier valide. Examinez-le d’abord :

cat /home/labex/project/data/new-orders.csv

Il possède les sept mêmes champs dans le même ordre, avec deux nouvelles commandes. Prévoyez les changements : deux lignes métier au lieu de six, tandis que quantité, prix et date conservent leurs types déclarés.

La version complète suivante conserve le schéma explicite et change le chemin d’entrée. La comparaison d’inférence n’est plus nécessaire pour cette lecture habituelle :

cat > /home/labex/project/read_orders.py <<'PYTHON'
from pyspark.sql.types import StructType, StructField, StringType, IntegerType, DecimalType, DateType
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("ReadOrderCSV").master("local[2]").getOrCreate()
spark.sparkContext.setLogLevel("ERROR")

order_schema = StructType([
    StructField("order_id", StringType(), True),
    StructField("customer_id", StringType(), True),
    StructField("product_id", StringType(), True),
    StructField("quantity", IntegerType(), True),
    StructField("unit_price", DecimalType(10, 2), True),
    StructField("order_status", StringType(), True),
    StructField("order_date", DateType(), True),
])

input_path = "/home/labex/project/data/new-orders.csv"
df = (
    spark.read
    .option("header", True)
    .option("dateFormat", "yyyy-MM-dd")
    .schema(order_schema)
    .csv(input_path)
)
print("Explicit schema:")
df.printSchema()
df.orderBy("order_id").show(truncate=False)
print("Rows:", df.count())
spark.stop()
PYTHON
bigdata-python /home/labex/project/read_orders.py > /home/labex/project/output/new-typed-orders.txt
cat /home/labex/project/output/new-typed-orders.txt

Vous devez obtenir o7 avec la quantité 2, le prix 12.50, le statut payé et la date 2026-10-16 ; o8 avec la quantité 3, le prix 4.00, le statut en attente et la date 2026-10-17. Le nombre de lignes doit être 2. Vérifiez les mêmes types integer, decimal et date dans printSchema.

Le fichier original de six commandes reste inchangé. Un schéma réutilisable permet à une nouvelle livraison d’entrer dans la même logique de traitement sans redéfinir le sens d’une quantité ou d’un prix. Vous utiliserez ensuite ces colonnes typées pour sélectionner les commandes et calculer le montant de chacune.

Résumé

Vous avez lu correctement un en-tête CSV, comparé le chargement en chaînes à l’inférence et déclaré un contrat explicite pour les identifiants, les quantités, les prix décimaux et les dates. Le même schéma a correctement interprété une seconde livraison. Des expressions de colonnes typées vous permettront ensuite de filtrer les lignes et de calculer les montants des commandes.