В искре это было бы очень просто. Логика сокращения карты была бы полностью скрыта от вас.
// let's define some data
val df = Seq((0, "cat_a", "free", 2.5), (1, "cat_a", "free", 3.5), (2, "cat_a", "paid", 4.1),
(3, "cat_a", "paid", 4.5), (4, "cat_b", "free", 2.5), (5, "cat_b", "paid", 4.8))
.toDF("app", "cat", "type", "rating")
df.show
+---+-----+----+------+
|app| cat|type|rating|
+---+-----+----+------+
| 0|cat_a|free| 2.5|
| 1|cat_a|free| 3.5|
| 2|cat_a|paid| 4.1|
| 3|cat_a|paid| 4.5|
| 4|cat_b|free| 2.5|
| 5|cat_b|paid| 4.8|
+---+-----+----+------+
Тогда это так просто:
val result = df.groupBy("cat").pivot("type")
.agg(avg('rating))
.withColumn("ratio", 'free / 'paid)
result.show
+-----+----+----+------------------+
| cat|free|paid| ratio|
+-----+----+----+------------------+
|cat_b| 2.5| 4.8|0.5208333333333334|
|cat_a| 3.0| 4.3|0.6976744186046512|
+-----+----+----+------------------+
Примечание: если вы знаете, что этот тип может быть платным или бесплатным, вы можете использовать .pivot("type", Seq("paid", "free")
, что будет более эффективным.