一、源数据

本章所分析的数据来自于一家英国的零售电商平台在01/12/2010至09/12/2011期间的交易数据(https://www.kaggle.com/datasets/carrie1/ecommerce-data),每条记录由8个属性组成,具体的含义如下表所示:

字段名称 类型 含义 举例
InvoiceNo string 订单编号(退货订单以C开头) 536365
StockCode string 产品代码 85123A
Description string 产品描述 WHITE METAL LANTERN
Quantity integer 购买数量(负数表示退货) 6
InvoiceDate string 订单日期和时间 12/1/2010 8:26
UnitPrice double 单价(英镑) 3.39
CustomerID integer 客户编号 17850
Country string 国家名称 United Kingdom

二、练习题

0. 数据预处理

我们将原始文件存放在HDFS的/SparkLearning目录下,然后从HDFS中读取数据到DataFrame格式。由于顾客编号CustomID和商品描述Description均存在部分缺失,所以需要先进行数据清洗,过滤掉有缺失值的记录。

// 加载Spark会话
val spark = SparkSession
  .builder()
  .appName("E-Commerce")
  .getOrCreate()
import spark.implicits._

// 读取源数据
val raw_data = spark.read
  .option("header", "true")
  .option("inferSchema", "true")
  .csv("hdfs:///SparkLearning/E_Commerce_Data.csv")
// 过滤掉有缺失值的记录
val df = raw_data
  .filter($"CustomerID" =!= 0)
  .filter($"Description" =!= "")

1. 客户数最多的10个国家

val res = df
  .select($"CustomerID", $"Country")
  .distinct()
  .groupBy($"Country")
  .count()
  .withColumnRenamed("count", "CustomerNum")
  .sort($"CustomerNum".desc)
  .limit(10)

2. 销量最高的10个国家

val res = df
  .select($"Quantity", $"Country")
  .groupBy($"Country")
  .sum("Quantity")
  .withColumnRenamed("sum(Quantity)", "totalQuantity")
  .sort($"totalQuantity".desc)
  .limit(10)

3. 各个国家的总销售额分布情况

val res = df
  .select($"Country", ($"Quantity" * $"UnitPrice").as("Revenue"))
  .groupBy($"Country")
  .sum("Revenue")
  .withColumnRenamed("sum(Revenue)", "totalRevenue")
  .orderBy($"totalRevenue".desc)

4. 销量最高的10个商品

val res = df
  .select($"StockCode", $"Description", $"Quantity")
  .groupBy($"StockCode", $"Description")
  .sum("Quantity")
  .withColumnRenamed("sum(Quantity)", "totalQuantity")
  .orderBy($"totalQuantity".desc)
  .limit(10)

5. 商品描述的热门关键词Top300

val res = df
  .select($"Description")
  .flatMap(row => row.getString(0).split(" "))
  .map(word => word.toLowerCase)
  .filter(word => word.nonEmpty)
  .toDF("keyword")
  .groupBy($"keyword")
  .count()
  .orderBy($"count".desc)
  .limit(300)

6. 退货订单数最多的10个国家

val res = df
  .filter($"Quantity" < 0)
  .select($"Country", $"InvoiceNo")
  .distinct()
  .groupBy($"Country")
  .count()
  .orderBy($"count".desc)
  .limit(10)

7. 月销售额随时间的变化趋势

val res = df
  .select(
    date_format(to_timestamp($"InvoiceDate", "M/d/yyyy H:mm"), "yyyy-MM").as("month"),
    ($"Quantity" * $"UnitPrice").as("revenue")
  )
  .groupBy($"month")
  .sum("revenue")
  .withColumnRenamed("sum(revenue)", "totalRevenue")
  .orderBy($"month")

8. 日销量随时间的变化趋势

val res = df
  .select(
    to_date(to_timestamp($"InvoiceDate", "M/d/yyyy H:mm")).as("date"),
    $"Quantity"
  )
  .groupBy($"date")
  .sum("Quantity")
  .withColumnRenamed("sum(Quantity)", "totalQuantity")
  .orderBy($"date")

9. 各国的购买订单量和退货订单量的关系

val res = df
  .select("Country", "InvoiceNo")
  .distinct()
  .selectExpr(
    "Country",
    "IF(InvoiceNo NOT REGEXP '^C', 1, 0) AS purchase",
    "IF(InvoiceNo REGEXP '^C', 1, 0) AS refund"
  )
  .groupBy("Country")
  .agg(
    Map(
      "purchase" -> "sum",
      "refund" -> "sum"
    )
  )

10. 商品的平均单价与销量的关系

val res = df
  .select("StockCode", "UnitPrice", "Quantity")
  .groupBy("StockCode")
  .agg(
    (sum_distinct($"UnitPrice") / count_distinct($"UnitPrice")).as("avgPrice"),
    sum($"Quantity").as("totalQuantity")
  )
  .select("StockCode", "avgPrice", "totalQuantity")

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐