Spark Scala数据帧中的列拆分

Spark Scala数据帧中的列拆分,scala,apache-spark,Scala,Apache Spark,我随身带着下面的数据框- scala> val df1=Seq( | ("1_10","2_20","3_30"), | ("7_70","8_80","9_90") | )toDF("c1","c2","c3") scala>df1.show +----+----+----+ |

我随身带着下面的数据框-

scala> val df1=Seq(
     | ("1_10","2_20","3_30"),
     | ("7_70","8_80","9_90")
     | )toDF("c1","c2","c3")
scala>df1.show

+----+----+----+
|  c1|  c2|  c3|
+----+----+----+
|1_10|2_20|3_30|
|7_70|8_80|9_90|
+----+----+----+
如何根据分隔符\将其拆分为不同的列

预期产量-

+----+----+----+----+----+----+
|  c1|  c2|  c3|c1_1|c2_1|c3_1|
+----+----+----+----+----+----+
|1   |2   |3   |  10|  20|  30|
|7   |8   |9   |  70|  80|  90|
+----+----+----+----+----+----+

另外,我在DF中有50多个列。提前感谢。

您可以使用拆分方法

split(col("c1"), '_')
这将返回ArrayTypeStringType 然后可以使用.getItemindex方法访问项目。 也就是说,如果分割后元素数量稳定,如果不是这样,那么如果分割后数组中不存在索引值,则会有一些空值

代码示例:

df.select(
  split(col("c1"), "_").alias("c1_items"),
  split(col("c2"), "_").alias("c2_items"),
  split(col("c3"), "_").alias("c3_items"),
).select(
  col("c1_items").getItem(0).alias("c1"),
  col("c1_items").getItem(1).alias("c1_1"),
  col("c2_items").getItem(0).alias("c2"),
  col("c2_items").getItem(1).alias("c2_1"),
  col("c3_items").getItem(0).alias("c3"),
  col("c3_items").getItem(1).alias("c3_1")
)
因为您需要对50多列执行此操作,所以我可能会建议以这种方式将其包装在一个方法中,用于单个column+withColumn语句

def splitMyCol(df: Dataset[_], name: String) = {
  df.withColumn(
    s"${name}_items", split(col("name"), "_")
  ).withColumn(
    name, col(s"${name}_items).getItem(0)
  ).withColumn(
    s"${name}_1", col(s"${name}_items).getItem(1)
  ).drop(s"${name}_items")
}
注意:我假设您不需要维护项目,因此我将其删除。另外,由于两个变量之间的名称中的uu是s字符串,您需要将第一个变量包装在{}中,而第二个变量实际上不需要{}包装,并且$s就足够了

您可以通过以下方式将其包装到折叠方法中:

val result = columnsToExpand.foldLeft(df)(
  (acc, next) => splitMyCol(acc, next)
)

您可以使用拆分方法

split(col("c1"), '_')
这将返回ArrayTypeStringType 然后可以使用.getItemindex方法访问项目。 也就是说,如果分割后元素数量稳定,如果不是这样,那么如果分割后数组中不存在索引值,则会有一些空值

代码示例:

df.select(
  split(col("c1"), "_").alias("c1_items"),
  split(col("c2"), "_").alias("c2_items"),
  split(col("c3"), "_").alias("c3_items"),
).select(
  col("c1_items").getItem(0).alias("c1"),
  col("c1_items").getItem(1).alias("c1_1"),
  col("c2_items").getItem(0).alias("c2"),
  col("c2_items").getItem(1).alias("c2_1"),
  col("c3_items").getItem(0).alias("c3"),
  col("c3_items").getItem(1).alias("c3_1")
)
因为您需要对50多列执行此操作,所以我可能会建议以这种方式将其包装在一个方法中,用于单个column+withColumn语句

def splitMyCol(df: Dataset[_], name: String) = {
  df.withColumn(
    s"${name}_items", split(col("name"), "_")
  ).withColumn(
    name, col(s"${name}_items).getItem(0)
  ).withColumn(
    s"${name}_1", col(s"${name}_items).getItem(1)
  ).drop(s"${name}_items")
}
注意:我假设您不需要维护项目,因此我将其删除。另外,由于两个变量之间的名称中的uu是s字符串,您需要将第一个变量包装在{}中,而第二个变量实际上不需要{}包装,并且$s就足够了

您可以通过以下方式将其包装到折叠方法中:

val result = columnsToExpand.foldLeft(df)(
  (acc, next) => splitMyCol(acc, next)
)
Pypark解决方案:

import pyspark.sql.functions as F
df1=sqlContext.createDataFrame([("1_10","2_20","3_30"),("7_70","8_80","9_90")]).toDF("c1","c2","c3")
expr = [F.split(coln,"_") for coln in df1.columns]
df2=df1.select(*expr)
#%%
df3= df2.withColumn("clctn",F.flatten(F.array(df2.columns)))
#%%  assuming all columns will have data in the same format x_y
arr_size = len(df1.columns)*2
df_fin= df3.select([F.expr("clctn["+str(x)+"]").alias("c"+str(x/2)+'_'+str(x%2)) for x in range(arr_size)])
结果:

+----+----+----+----+----+----+
|c0_0|c0_1|c1_0|c1_1|c2_0|c2_1|
+----+----+----+----+----+----+
|   1|  10|   2|  20|   3|  30|
|   7|  70|   8|  80|   9|  90|
+----+----+----+----+----+----+
Pypark解决方案:

import pyspark.sql.functions as F
df1=sqlContext.createDataFrame([("1_10","2_20","3_30"),("7_70","8_80","9_90")]).toDF("c1","c2","c3")
expr = [F.split(coln,"_") for coln in df1.columns]
df2=df1.select(*expr)
#%%
df3= df2.withColumn("clctn",F.flatten(F.array(df2.columns)))
#%%  assuming all columns will have data in the same format x_y
arr_size = len(df1.columns)*2
df_fin= df3.select([F.expr("clctn["+str(x)+"]").alias("c"+str(x/2)+'_'+str(x%2)) for x in range(arr_size)])
结果:

+----+----+----+----+----+----+
|c0_0|c0_1|c1_0|c1_1|c2_0|c2_1|
+----+----+----+----+----+----+
|   1|  10|   2|  20|   3|  30|
|   7|  70|   8|  80|   9|  90|
+----+----+----+----+----+----+
下面是foldLeft的好用法。拆分每列并为每个拆分的值创建一个新列

如果您需要的列名称与您想要的完全相同,那么您需要过滤以_1结尾的列,并使用foldLeft再次重命名它们

输出:

+----+----+----+----+----+----+
|c1_1|c1_2|c2_1|c2_2|c3_1|c3_2|
+----+----+----+----+----+----+
|1   |10  |2   |20  |3   |30  |
|7   |70  |8   |80  |9   |90  |
+----+----+----+----+----+----+
下面是foldLeft的好用法。拆分每列并为每个拆分的值创建一个新列

如果您需要的列名称与您想要的完全相同,那么您需要过滤以_1结尾的列,并使用foldLeft再次重命名它们

输出:

+----+----+----+----+----+----+
|c1_1|c1_2|c2_1|c2_2|c3_1|c3_2|
+----+----+----+----+----+----+
|1   |10  |2   |20  |3   |30  |
|7   |70  |8   |80  |9   |90  |
+----+----+----+----+----+----+
你可以这样做

var df=Seq(("1_10","2_20","3_30"),("7_70","8_80","9_90")).toDF("c1","c2","c3")

  for (cl <- df.columns) {
    df=df.withColumn(cl+"_temp",split(df.col(cl),"_")(0))
    df=df.withColumn(cl+"_"+cl.substring(1),split(df.col(cl),"_")(1))
    df=df.withColumn(cl,df.col(cl+"_temp")).drop(cl+"_temp")
  }
  df.show(false)
}

//Sample output
    +---+---+---+----+----+----+
    |c1 |c2 |c3 |c1_1|c2_2|c3_3|
    +---+---+---+----+----+----+
    |1  |2  |3  |10  |20  |30  |
    |7  |8  |9  |70  |80  |90  |
    +---+---+---+----+----+----+
你可以这样做

var df=Seq(("1_10","2_20","3_30"),("7_70","8_80","9_90")).toDF("c1","c2","c3")

  for (cl <- df.columns) {
    df=df.withColumn(cl+"_temp",split(df.col(cl),"_")(0))
    df=df.withColumn(cl+"_"+cl.substring(1),split(df.col(cl),"_")(1))
    df=df.withColumn(cl,df.col(cl+"_temp")).drop(cl+"_temp")
  }
  df.show(false)
}

//Sample output
    +---+---+---+----+----+----+
    |c1 |c2 |c3 |c1_1|c2_2|c3_3|
    +---+---+---+----+----+----+
    |1  |2  |3  |10  |20  |30  |
    |7  |8  |9  |70  |80  |90  |
    +---+---+---+----+----+----+
尝试使用select而不是foldLeft以获得更好的性能。因为foldLeft可能比select花费更长的时间

结果

尝试使用select而不是foldLeft以获得更好的性能。因为foldLeft可能比select花费更长的时间

结果


谢谢你的回答。谢谢你的回答。谢谢你的回答更新了答案以获得更好的列名。检查一下,如果有帮助,请将答案标记为已接受。事实上,我正在寻找Scala实现。但这对python很有帮助。啊,好的。。您认为可以将相同的概念直接转移到scala。祝你好运:-谢谢你的回答更新了答案,以便有一个更好的列名。检查一下,如果有帮助,请将答案标记为已接受。事实上,我正在寻找Scala实现。但这对python很有帮助。啊,好的。。您认为可以将相同的概念直接转移到scala。祝你好运:-谢谢你的回答:谢谢你的回答: