ETL

提取、转换、加载(ETL)是一个三阶段的计算过程,其中数据从输入源提取、转换(包括清理、聚合、导出新值)并加载到输出数据容器中。 在大数据和AI流行的今天,ETL有了更多的用武之地。

下面是来自wikipedia的一个关于ETL的一幅图很好的说明了ETL的主要功能image.png

https://en.wikipedia.org/wiki/Extract,_transform,_load

我有一系列的csv,每个csv是某个基金的信息。 这个文章中,我计划利用ETL把一系列的csv合并为parquet文件,为之后的各种应用提供一个数据源。这个ETL主要功能包括:
1: 加载一系列csv
2:文件的名字是FundPrice<基金id>.csv,所以添加一列把fundid加到数据中
2: 因为csv导入后,查看它的schema 发现类型与数据不太匹配,定义了schema把csv各列数据转换为适当的类型
3:根据csv数据中最近一周数据来计算并添加每周价格平均价格列(一种常见的聚合)
4:保存为parquet文件。因为我的数据比较小,parquet文件建议文件大小在128M到1G之间,我把数据保存到了一个文件中。

import os

from pyspark.sql import SparkSession
from pyspark.sql.functions import input_file_name, regexp_extract, round
from pyspark.sql.window import Window
from pyspark.sql.functions import avg, col
from pyspark.sql.types import StructType, StructField, DateType, DecimalType, StringType, IntegerType

if __name__ == "__main__":
    spark = SparkSession \
        .builder \
        .master("local[3]") \
        .appName("sample2") \
        .getOrCreate()

    csvDF = spark.read \
        .format("csv") \
        .option("header", "true") \
        .load("data/fund/FundPrice*.csv") \
        .withColumn("fundid", regexp_extract(input_file_name(), r"FundPrice(\d+)\.csv", 1))

    # Define a window specification to calculate the average of the last 5 days
    window_spec = Window.partitionBy("fundid").orderBy("date").rowsBetween(-4, 0)

    # Add a new column for the average accumulate price, rounded to 3 decimal places
    csvDF = csvDF.withColumn("avgAccumulatePrice", round(avg(col("accumulativePrice")).over(window_spec), 3))

    print(csvDF.count())
    csvDF.show()
    print(csvDF.schema.json())

    # Define the schema for the Parquet file
    parquet_schema = StructType([
        StructField("Date", DateType(), True),
        StructField("fundid", StringType(), True),
        StructField("accumulativePrice", DecimalType(10, 3), True),
        StructField("avgAccumulatePrice", DecimalType(10, 3), True),
        StructField("isbonus", IntegerType(), True),  # Added as IntegerType
        StructField("isCash", IntegerType(), True),    # Added as IntegerType
        StructField("price", DecimalType(10, 3), True),  # Added as DecimalType
        StructField("bonusPerStock", DecimalType(10, 3), True)  # Added as DecimalType
    ])

    # Apply the schema when saving as Parquet
    csvDF = csvDF.select(
        col("Date").cast(DateType()),
        col("fundid"),
        col("accumulativePrice").cast(DecimalType(10, 3)),
        col("avgAccumulatePrice").cast(DecimalType(10, 3)),
        col("isbonus").cast(IntegerType()),  # Cast to IntegerType
        col("isCash").cast(IntegerType()),    # Cast to IntegerType
        col("price").cast(DecimalType(10, 3)),  # Cast to DecimalType
        col("bonusPerStock").cast(DecimalType(10, 3))  # Cast to DecimalType
    )

    # coalesce(1) is used to save it as one file
    csvDF.coalesce(1).write \
        .format("parquet") \
        .mode("overwrite") \
        .option("path", "data/fund/parquet/") \
        .save()
    
    parquetDF = spark.read \
        .format("parquet") \
        .load("data/fund/parquet/*.parquet")
    parquetDF.show(5)
    print(parquetDF.schema.json())

    spark.stop()

下面是运行结果
image.png

代码: https://gitee.com/yanghang1977/pyspark/blob/master/Etl.py


愚公爬山
1 声望0 粉丝

引用和评论

0 条评论