Из того, что я понимаю о том, что вы пытаетесь сделать, ваш пример неверен. Поля bcd Алисы составляют всего 145, а поля abc - 220. Таким образом, abc следует выбрать и для нее. Если я не прав, то я неправильно понял вашу проблему.
В любом случае, вам не нужен udf, чтобы делать то, что вы хотите. Давайте сгенерируем ваши данные:
val df = sc.parallelize(Seq(
("Bob", Array("av:27.0", "bcd:29.0", "abc:25.0")),
("Alice", Array("abc:95.0", "bcd:55.0")),
("Bob", Array("abc:95.0", "bcd:70.0")),
("Alice", Array("abc:125.0", "bcd:90.0"))) )
.toDF("name", "nt_set")
Тогда один из способов - это взорвать nt_set в столбец nt, который содержит только одну пару строка / значение.
df.withColumn("nt", explode('nt_set))
//then we split the string and the value
.withColumn("nt_string", split('nt, ":")(0))
.withColumn("nt_value", split('nt, ":")(1).cast("int"))
//then we sum the values by name and "string"
.groupBy("name", "nt_string")
.agg(sum('nt_value) as "nt_value")
/* then we build a struct with the value first to be able to select
the nt field with max value while keeping the corresponding string */
.withColumn("nt", struct('nt_value, 'nt_string))
.groupBy("name")
.agg(max('nt) as "nt")
// And we rebuild the "nt" column.
.withColumn("max_nt", concat_ws(":", $"nt.nt_string", $"nt.nt_value"))
.drop("nt").show(false)
+-----+-------+
|name |max_nt |
+-----+-------+
|Bob |abc:120|
|Alice|abc:220|
+-----+-------+