我无法在DF以下转换:
| A | B | C |
| 1 | 2 | t1,t2,t3|
| 1 | 3 | t4,t5 |
| 2 | 2 | t1,t5 |
到DF以下:
| A | B | t1 | t2 | t3 | t4 | t5 |
| 1 | 2 | 1 | 1 | 1 | 0 | 0 |
| 1 | 3 | 0 | 0 | 0 | 1 | 1 |
| 2 | 2 | 1 | 0 | 0 | 0 | 1 |
在scala火花中。
列C
中的值以逗号分隔,我希望它使它们成为单独的列。完成此操作后,如果t1
,t2
等存在或不存在,我想在该特定行中标记0和1。
请在这里帮助。会非常感激的。
答案 0 :(得分:3)
这可以通过使用pivot
来完成。但是,首先在“ {”和“ explode
” C
列上拆分数据。
df.withColumn("C", explode(split($"C", ",")))
.groupBy("A", "B")
.pivot("C")
.count()
.na.fill(0)
结果:
+---+---+---+---+---+---+---+
| A| B| t1| t2| t3| t4| t5|
+---+---+---+---+---+---+---+
| 2| 2| 1| 0| 0| 0| 1|
| 1| 2| 1| 1| 1| 0| 0|
| 1| 3| 0| 0| 0| 1| 1|
+---+---+---+---+---+---+---+
以上解决方案假定所有行在A
和B
中具有唯一的值组合(由于使用groupBy
)。如果不是这种情况,则必须采取其他步骤:
添加一个具有唯一ID的新列,该列可以与A
中的B
和groupBy
一起使用,例如:
df.withColumn("D", monotonically_increasing_id)
可以在pivot
之后删除此列。
答案 1 :(得分:1)
首先,您需要从列C
中获取所有值,然后创建该列C
的集合作为值,该值将用作列名,请检查以下过程
创建数据框:
val df = spark.sparkContext.parallelize(Seq((1, 2, "t1,t2,t3"), (1, 3, "t4,t5"), (2,2,"t1,t5"))).toDF("A","B","C")
+---+---+--------+
| A| B| C|
+---+---+--------+
| 1| 2|t1,t2,t3|
| 1| 3| t4,t5|
| 2| 2| t1,t5|
+---+---+--------+
读取列值,该值需要数据框的标题:
val cols = df.select($"C")
val colnameAsHeader = cols.map(x=>x.getString(0).split(",")).collect.flatten.toSet
为检查条件创建UDF:
def checkCriteria(inputString: String) = {udf((inputColumn: String) => { if(inputColumn.split(",").contains(inputString)){1}else{0} })}
使用foldLeft设置标题:
colnameAsHeader.foldLeft(df)((df, name) => df.withColumn(name , lit(checkCriteria(name)($"C")))).drop("C").show
+---+---+---+---+---+---+---+
| A| B| t4| t5| t3| t2| t1|
+---+---+---+---+---+---+---+
| 1| 2| 0| 0| 1| 1| 1|
| 1| 3| 1| 1| 0| 0| 0|
| 2| 2| 0| 1| 0| 0| 1|
+---+---+---+---+---+---+---+
答案 2 :(得分:0)
另一种可能是利用Spark 2.4的spark sql函数代替udf,如下所示:
收集值列表中的所有值,然后使用array_contains方法测试列表中每个值是否存在于数组列中
val df1 = Seq(
("1", Array("t1", "t2")),
("2", Array("t1", "t3", "t5"))
).toDF("id", "arr")
import org.apache.spark.sql.functions.{col, lit, array_contains, when}
val values = df1.select("arr").collect().map(x => x.getList[Array[String]](0).toArray)
.flatMap(x => x)
.distinct
.map(_.toString)
val df2 = df1
.select(df1.columns.map(col(_)) ++
values.map(x => when(array_contains(col("arr"), x), 1).otherwise(0).alias(x)):_*)
df2.show()
+---+------------+---+---+---+---+
| id| arr| t1| t2| t3| t5|
+---+------------+---+---+---+---+
| 1| [t1, t2]| 1| 1| 0| 0|
| 2|[t1, t3, t5]| 1| 0| 1| 1|
+---+------------+---+---+---+---+