Lea CSV en Spark con un esquema explícito

Big DataBeginner
Practicar Ahora

Introducción

Los registros de pedidos de la empresa minorista llegan ahora en un archivo CSV en lugar de estar escritos directamente en su lista de Python. Un archivo almacena texto como 2 y 10.00; un cálculo necesita saber si esos valores significan texto, una cantidad entera o un precio decimal. Leer los caracteres correctos es solo parte de leer correctamente los datos.

Comparará la carga predeterminada de cadenas de Spark, la inferencia automática de tipos y un esquema explícito. Después aplicará el mismo esquema a un segundo archivo con el mismo orden de campos. Los datos comerciales son deliberadamente válidos aquí para que pueda centrarse en el contrato entre columnas y valores. Un laboratorio posterior trata explícitamente las entradas ausentes y no válidas.

El entorno proporciona PySpark y dos archivos CSV locales de ejemplo independientes. Debe comprender la SparkSession, la visualización de DataFrames, el esquema y el conteo de filas del primer laboratorio, además de Python básico. No necesita pandas, Hadoop, Hive, instalación ni estado previo de la máquina virtual. Utilice Terminal.

Lea el encabezado y observe las columnas de cadenas

En este paso, relacionará las posiciones de campos CSV con la tabla que crea Spark. Comience examinando el archivo real:

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

La primera línea nombra siete columnas; las seis siguientes son pedidos. Cada fila representa un pedido con un tipo de producto. El archivo incluye estados paid, pending y refunded; este laboratorio los lee todos sin aplicar una regla de ventas.

spark.read crea un lector de DataFrames. option("header", True) indica al lector que trate la primera línea CSV como nombres de columnas, en lugar de como un registro de pedido. csv(input_path) indica el archivo local. Sin inferencia de tipos ni un esquema proporcionado, el lector CSV produce columnas de cadenas.

Guarde y ejecute este programa completo. El documento en línea PYTHON entre comillas conserva el código incluido y > escribe el archivo local. bigdata-python utiliza el entorno preparado de PySpark:

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

Espere seis filas y siete columnas de cadenas, incluidas quantity y unit_price. El 2 mostrado puede parecer numérico, pero printSchema identifica su tipo real como string. orderBy("order_id") ordena las filas mostradas por identificador para examinarlas de forma predecible; ordenar es independiente de asignar tipos.

header=True impidió que el encabezado se convirtiera en un séptimo registro. No indicó a Spark que los precios debían ser decimales exactos ni que las fechas debían admitir operaciones de fecha.

Compare la inferencia con un contrato explícito de datos

En este paso, declarará qué significa cada campo CSV. inferSchema=True pide a Spark que examine valores e infiera tipos. Puede resultar cómodo, pero el resultado depende de los datos observados; no es un acuerdo duradero sobre precios ni archivos futuros. En este ejemplo, la inferencia identifica unit_price como double, un número de punto flotante.

Utilice un esquema explícito para el contrato previsto. StructType es la colección ordenada de campos. Cada StructField contiene un nombre, un tipo y True para permitir valores ausentes. StringType contiene identificadores y texto de estado; IntegerType contiene cantidades; DecimalType(10, 2) contiene precios exactos con diez dígitos totales y dos fraccionarios; DateType contiene fechas del calendario. Los campos que admiten nulos permiten valores ausentes, pero no hacen automáticamente aceptable la ausencia de información comercial.

Sustituya el programa por esta versión. Imprime la descripción de tipos inferidos para compararlos y después lee el mismo archivo con su esquema explícito:

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

Busque el esquema explícito debajo del resumen inferido. quantity debería ser integer, unit_price decimal(10,2) y order_date date. Los seis registros deberían seguir presentes. La opción explícita dateFormat describe las fechas de entrada como año-mes-día, coincidiendo con valores como 2026-10-14.

El orden de campos del esquema coincide con el orden de campos de este CSV. Un esquema proporcionado no es una instrucción general para reorganizar cualquier archivo entrante por nombre de encabezado. Para esta entrega de orden conocido, el contrato deja claras las columnas y los tipos previstos. Un esquema tampoco sustituye una política de calidad de datos; más adelante aprenderá a tratar valores no válidos de forma segura.

Aplique el mismo esquema a una entrega nueva

En este paso, probará el contrato con un segundo archivo válido. Examínelo primero:

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

Tiene los mismos siete campos en el mismo orden, con dos pedidos nuevos. Prediga los cambios: dos filas comerciales en lugar de seis, mientras que cantidad, precio y fecha conservan sus tipos declarados.

La siguiente versión completa conserva el esquema explícito y cambia la ruta de entrada. La comparación de inferencia ya no es necesaria para esta lectura normal:

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

Espere o7 con cantidad 2, precio 12.50, estado paid y fecha 2026-10-16; o8 con cantidad 3, precio 4.00, estado pending y fecha 2026-10-17. El conteo debería ser 2. Compruebe los mismos tipos integer, decimal y date en printSchema.

El archivo original de seis pedidos permanece sin cambios. Un esquema reutilizable permite que una entrega nueva entre en la misma lógica de procesamiento sin redefinir qué significa una cantidad o un precio. A continuación, utilizará estas columnas con tipos para seleccionar pedidos y calcular el importe de cada uno.

Resumen

Leyó correctamente un encabezado CSV, comparó la carga de cadenas y la inferencia y declaró un contrato explícito para identificadores, cantidades, precios decimales y fechas. El mismo esquema interpretó correctamente una segunda entrega. A continuación, las expresiones de columnas con tipos le permitirán filtrar filas y calcular importes de pedidos.