You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySpark计算DataFrame数组列分组均值问题求助

问题:PySpark中对结构体数组按品牌分组计算均值并排序

我正从Python Pandas转用PySpark,现有一个DataFrame,其中sorted_zipped列是通过arrays_zip生成的结构体数组。需求为:按数组中每个结构体的第一个元素(品牌)分组,计算第二个元素的均值,生成新列且结果按均值降序排列。

尝试用UDF实现时遇到两个问题:

  • 在Databricks中无法在Worker节点使用SparkContext
  • 改用numpy的UDF触发TypeError: cannot perform reduce with flexible type错误

数据示例

+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|WeekOfYear|sorted_zipped                                                                                                                                                                                                                                                                                         |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|13-2023   |[{bmw, 0.99}, {vw, 0.98}, {chevy, 0.97}, {buick, 0.96}]                                                                                                                                                                                                                                               |
|14-2023   |[{chevy, 0.98}, {bmw, 0.98}, {bmw, 0.978}, {bmw, 0.976}, {vw, 0.975}, {bmw, 0.975}, {bmw, 0.97}, {buick, 0.967}, {vw, 0.964}, {vw, 0.96}, {nissan, 0.96}, {chevy, 0.952}, {nissan, 0.95}, {nissan, 0.95}, {lexus, 0.95}, {lexus, 0.94}, {lexus, 0.94}, {nissan, 0.935}, {buick, 0.93}, {chevy, 0.928}]
|15-2023   |[{chevy, 0.992}, {bmw, 0.987}, {nissan, 0.982}, {bmw, 0.982}, {buick, 0.978}, {lexus, 0.976}, {bmw, 0.975}, {bmw, 0.97}, {chevy, 0.967}, {vw, 0.964}, {lexus, 0.961}, {nissan, 0.96}, {vw, 0.952}, {nissan, 0.952}, {vw, 0.952}, {lexus, 0.943}]
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

预期输出(第3行)

[{chevy, 0.9795}, {bmw, 0.9784}, {buick, 0.978}, {nissan, 0.967}, {lexus, 0.96}, {vw, 0.956}]

尝试的错误代码

第一种UDF尝试(SparkContext访问问题)

def get_avg_1(x):
  rdd = parallelize(x)
  rdd2 = rdd.flatMap(lambda x: [(k, v) for (k, v) in x.items()]).collect()
  grouped_k = rdd2.groupByKey()
  avg_map = grouped_k.mapValues(lambda x: sum(x[1])/len(x[1])).collect()
  return avg_map

第二种numpy UDF尝试(类型错误)

import numpy as np
import json

schema = ArrayType(StructType([
  StructField("GroupedBrands", StringType(), True),
  StructField("GroupedWeights", FloatType(), True)
]))

array_mean = F.udf(lambda x: (x[0], np.mean(x[1]), schema))
mean_df = sdf.withColumn("mean_value", array_mean("sorted_zipped"))

mean_df.show()

正确解决方案(使用Spark内置函数,避免UDF)

Spark内置函数在分布式环境下更高效,且不会出现Worker节点权限或类型兼容问题,步骤如下:

from pyspark.sql import functions as F
from pyspark.sql.types import *

# 1. 展开结构体数组为单行记录
exploded_df = sdf.withColumn("struct_element", F.explode("sorted_zipped"))

# 2. 提取品牌和数值字段(注意:如果结构体字段是默认的_1、_2,替换成对应名称)
extracted_df = exploded_df.select(
    "WeekOfYear",
    F.col("struct_element._1").alias("brand"),  # 替换为你的品牌字段名
    F.col("struct_element._2").alias("weight")  # 替换为你的数值字段名
)

# 3. 按周+品牌分组,计算均值
grouped_df = extracted_df.groupBy("WeekOfYear", "brand")\
    .agg(F.round(F.avg("weight"), 4).alias("avg_weight"))

# 4. 按周聚合回结构体数组,并按均值降序排序
result_df = grouped_df.groupBy("WeekOfYear")\
    .agg(F.collect_list(
        F.struct(
            F.col("brand").alias("GroupedBrands"),
            F.col("avg_weight").alias("GroupedWeights")
        )
    ).alias("mean_value"))\
    .withColumn("mean_value", F.sort_array("mean_value", asc=False))

result_df.show(truncate=False)

关键说明

  • 字段名适配:如果你的结构体字段不是brand和weight,而是arrays_zip生成的默认名称(如_1、_2),需要替换代码中对应的字段名
  • 排序逻辑:sort_array函数直接对结构体数组按avg_weight降序排列,无需额外处理
  • 性能优势:完全使用Spark内置函数,避免UDF带来的序列化/反序列化开销

内容的提问来源于stack exchange,提问作者user1717931

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 13:50:41