Scala 选择spark中每行的所有非空列名

Scala 选择spark中每行的所有非空列名,scala,dataframe,apache-spark,apache-spark-sql,Scala,Dataframe,Apache Spark,Apache Spark Sql,假设我有一个如下所示的数据帧: +-----+-----+-----+-----+ | A | B | C | D | +-----+-----+-----+-----+ | Y |null | Y |null | |null | N | N |null | | N | Y |null | N | +-----+-----+-----+-----+ val nonNullColsExpr = df.columns.map(c => when(c

假设我有一个如下所示的数据帧:

+-----+-----+-----+-----+
|  A  |  B  |  C  |  D  |
+-----+-----+-----+-----+
|  Y  |null |  Y  |null |
|null |  N  |  N  |null |
|  N  |  Y  |null |  N  |
+-----+-----+-----+-----+
val nonNullColsExpr = df.columns.map(c => when(col(c).isNotNull, lit(c)))

df.withColumn("non_null", array(nonNullColsExpr:_*))
  .withColumn("non_null", expr("array_join(non_null, ',')"))
  .show()

//+----+----+----+----+--------+
//|   A|   B|   C|   D|non_null|
//+----+----+----+----+--------+
//|   Y|null|   Y|null|     A,C|
//|null|   N|   N|null|     B,C|
//|   N|   Y|null|   N|   A,B,D|
//+----+----+----+----+--------+
我想添加一个新列,它是每行所有非空列名称的串联

因此,对于上面的示例,它将添加一个新列

+--------+
|non-null|
+--------+
| A, C   |
| B, C   |
| A, B, D|
+--------+
有什么简单的方法可以做到这一点吗?我试图寻找一个反合并,如果不是null,它将使用提供的名称。例如,如果存在反聚结:

df.withColumn("non-null", antiCoalesce("A", "A,") + antiCoalesce("B", "B,") + antiCoalesce("C", "C,") + antiCoalesce("D", "D"))

遗憾的是,没有这样的函数,但它演示了我所寻找的内容。

在SQL中,您可以使用
concat_ws()
来表达这一点:


这种方法怎么样?您可以使用新列
concat_ws
,这些新列包含列的名称(当列不为空时)

val df = Seq(("Y", null, "Y", null),
             (null, "N", "N", null),
             ("N", "Y", null, "N")).toDF("A", "B", "C", "D")

val columns = df.columns
columns.foldLeft(df) {(df, name) => df.withColumn(name + "2", when(!col(name).isNull, lit(name)))}
  .withColumn("non-null", concat_ws(",", columns.map(name => col(name + "2")): _*))
  .drop(columns.map(name => name + "2"): _*)
  .show(false)
那么


使用Spark 2.4+,您可以执行以下操作:

+-----+-----+-----+-----+
|  A  |  B  |  C  |  D  |
+-----+-----+-----+-----+
|  Y  |null |  Y  |null |
|null |  N  |  N  |null |
|  N  |  Y  |null |  N  |
+-----+-----+-----+-----+
val nonNullColsExpr = df.columns.map(c => when(col(c).isNotNull, lit(c)))

df.withColumn("non_null", array(nonNullColsExpr:_*))
  .withColumn("non_null", expr("array_join(non_null, ',')"))
  .show()

//+----+----+----+----+--------+
//|   A|   B|   C|   D|non_null|
//+----+----+----+----+--------+
//|   Y|null|   Y|null|     A,C|
//|null|   N|   N|null|     B,C|
//|   N|   Y|null|   N|   A,B,D|
//+----+----+----+----+--------+

首先,我们创建一个数组列,其中包含满足条件
isNotNull
的列名,不满足条件的列名将为null。然后使用
分隔符(
array\u join
在加入元素时过滤空值)将元素连接起来

这确实有效,但速度非常慢。还有更有效的方法吗?@Geoffreysaunds。我不知道这为什么会“非常慢”。它只是对每一行进行相对简单的计算。我真的想不出任何更快的事情。我有一个实现这样的事情,这真的需要很长的时间,所以我没有考虑它。我应该把它贴出来,作为我写问题时尝试的解决方案。我可以发布我在上面的问题,但它几乎完全是你在这里发布的。完美。哇,这真是跳出框框思考。太棒了我很抱歉问,但我已经挣扎了一整天。。。如何将此表达式转换为
pyspark
?@GuidoCioni在pyspark中,您可以这样做:
non\u null\u cols=[when(col(c).isNotNull,lit(c))for c in df.columns]
然后使用它:
df.withColumn(“non\u null”,array(*non\u null\u cols))
。其余部分与Scala中的相同。我可以这样使用它:ds.withColumn(“missing_L”,F.array(*[F.when(F.isnull(c),F.lit(c)),ds.columns中的c为F.when(F.isnull(c),F.lit(c))。withColumn(“missing_L”,F.expr(“array_join(missing_L',,”))))。withColumn(“missing___L”,F.regexp(替换(F.coll)(“missing_L”),“on object”,”)。选择(“msn”、“缺失”)