Spark DSL 操作详解

Spark DSL 操作详解

Spark 的 DataFrame/Dataset API 提供了一套强大的领域特定语言(DSL),允许开发者以声明式方式处理数据。这些操作不仅语法简洁,还能被 Catalyst 优化器高效执行。以下是核心 DSL 操作详解:


🔍 1. 列选择:select()

作用:选择或转换指定列
特点:支持列表达式、别名、函数计算

# Python
from pyspark.sql import functions as F

df.select(
    "name",                            # 直接选择列
    F.col("age"),                      # 使用col函数
    (F.col("salary") * 1.1).alias("new_salary"),  # 计算+别名
    F.current_date().alias("today")    # 内置函数
)
// Scala
import org.apache.spark.sql.functions._

df.select(
  col("name"),
  $"age",                              // $是col的语法糖
  (col("salary") * 1.1).as("new_salary"),
  current_date().alias("today")
)

🔎 2. 数据过滤:filter() / where()

作用:按条件筛选行(两者等价)
特点:支持复杂逻辑表达式

# 过滤年龄>30且以"A"开头的名字
df.filter(
    (F.col("age") > 30) & 
    (F.col("name").startswith("A"))
)
// 等价写法
df.where($"age" > 30 && $"name".startsWith("A"))

📊 3. 分组聚合:groupBy() + agg()

作用:按列分组并进行聚合计算
特点:支持多种聚合函数

df.groupBy("department") \
  .agg(
      F.count("*").alias("emp_count"),
      F.avg("salary").alias("avg_salary"),
      F.max("age").alias("max_age")
  )
df.groupBy("department")
  .agg(
    count("*").as("emp_count"),
    avg("salary").as("avg_salary"),
    max("age").as("max_age")
  )

🔗 4. 数据连接:join()

作用:合并两个 DataFrame
连接类型

  • inner(默认)
  • left, right, full(outer)
  • cross, left_semi, left_anti
# 内连接
employees.join(departments, 
              employees.dept_id == departments.id,
              "inner")
          
# 左外连接
employees.join(departments, 
              "dept_id",  # 同名字段简写
              "left")
// 使用Seq指定多个连接键
employees.join(departments, 
              Seq("dept_id", "location_id"),
              "left_outer")

📈 5. 排序:orderBy() / sort()

作用:按指定列排序(两者等价)
特点:支持多列、升降序

df.orderBy(
    F.col("salary").desc(),  # 工资降序
    "age",                   # 年龄升序(默认)
    F.col("name").asc()      # 姓名升序
)
df.orderBy(
  col("salary").desc,
  $"age",                   // 升序
  asc("name")               // 显式升序
)

➕ 6. 添加/替换列:withColumn()

作用:添加新列或替换现有列
最佳实践:链式调用处理多列转换

df.withColumn("bonus", F.col("salary") * 0.1) \
  .withColumn("full_name", F.concat("first_name", F.lit(" "), "last_name")) \
  .withColumn("salary", F.col("salary") * 1.05)  # 替换现有列
df.withColumn("bonus", $"salary" * 0.1)
  .withColumn("full_name", concat($"first_name", lit(" "), $"last_name"))
  .withColumn("salary", $"salary" * 1.05)

❌ 7. 删除列:drop()

作用:移除指定列

df.drop("temp_column", "obsolete_column")
df.drop("temp_column", "obsolete_column")

🔄 其他关键操作

重命名列:withColumnRenamed()

df.withColumnRenamed("old_name", "new_name")

去重:distinct() / dropDuplicates()

df.select("department").distinct()  # 单列去重
df.dropDuplicates(["name", "age"])  # 多列组合去重

空值处理:na

df.na.fill(0)  # 所有数字列填0
df.na.fill({"salary": 5000, "age": 30})  # 指定列填充
df.na.drop()  # 删除含空值的行

类型转换:cast()

df.withColumn("age_str", F.col("age").cast("string"))

💡 DSL 最佳实践

  1. 链式操作:利用 DSL 的链式特性保持代码简洁

    (df.filter(F.col("age") > 18)
      .groupBy("city")
      .agg(F.avg("income").alias("avg_income"))
      .orderBy("avg_income", ascending=False)
    
  2. 优先内置函数:使用pyspark.sql.functionsorg.apache.spark.sql.functions中的函数而非 UDF,享受 Catalyst 优化

  3. 适时缓存:对重复使用的中间结果使用.cache()

    filtered_df = df.filter(F.col("year") == 2023).cache()
    
  4. 避免混用 API:在单个处理流程中保持使用 DSL 或 SQL 的一致性


⚡ DSL 与 SQL 对比

特性DSLSpark SQL
语法风格编程式(方法链)声明式(类SQL)
类型检查编译时(Scala)、运行时(Py)运行时
IDE支持自动补全、类型提示字符串无提示
复杂表达式较直观需写完整字符串
代码复用可封装为函数需拼接SQL字符串
执行计划优化相同(均由Catalyst优化)相同

大多数场景下,DSL 是首选,尤其在需要复杂编程逻辑时。SQL 更适合纯数据分析人员或简单查询。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值