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。祝你好运:-谢谢你的回答:谢谢你的回答: