使用performant select操作重命名PySpark数据帧中的列

使用performant select操作重命名PySpark数据帧中的列,pyspark,Pyspark,还有关于如何重命名PySpark数据框中的列的其他线程,请参阅和。我认为现有的解决方案没有足够的性能或通用性(我有一个应该更好的解决方案,我被边缘案例错误卡住了)。让我们首先回顾一下当前解决方案的问题: 反复调用withColumnRename可能会遇到与多次调用withColumn相同的性能问题。看 依赖于模式推断,不一定保留列的nullable属性(在生产代码中应避免toDF)。我猜这种方法也很慢 ,但它不够通用,而且对于许多列来说,手动操作太多(例如,如果您尝试将2000个列名转换为sn

还有关于如何重命名PySpark数据框中的列的其他线程,请参阅和。我认为现有的解决方案没有足够的性能或通用性(我有一个应该更好的解决方案,我被边缘案例错误卡住了)。让我们首先回顾一下当前解决方案的问题:

  • 反复调用
    withColumnRename
    可能会遇到与多次调用
    withColumn
    相同的性能问题。看
  • 依赖于模式推断,不一定保留列的nullable属性(在生产代码中应避免toDF)。我猜这种方法也很慢
  • ,但它不够通用,而且对于许多列来说,手动操作太多(例如,如果您尝试将2000个列名转换为snake_大小写)
我创建了一个通用函数,适用于除包含点的列名以外的所有列类型:

导入pyspark.sql.F函数
def带有_列_重命名(乐趣):
定义(df):
cols=列表(映射)(
lambda col_name:F.col(col_name)。别名(fun(col_name)),
df.columns
))
返回df.select(*cols)
返回_
假设您有以下数据帧:

+-------------+-----------+
|i like cheese|yummy stuff|
+-------------+-----------+
|         jose|          a|
|           li|          b|
|          sam|          c|
+-------------+-----------+
+-------------+-----------+
|i.like.cheese|yummy.stuff|
+-------------+-----------+
|         jose|          a|
|           li|          b|
|          sam|          c|
+-------------+-----------+
下面介绍如何用下划线替换列名中的所有空格:

def空格到下划线:
返回s.replace(“,”)
transform(带_列_重命名(空格_到_下划线)).show()
除了列名包含点之外,该解决方案工作正常

假设您有以下数据帧:

+-------------+-----------+
|i like cheese|yummy stuff|
+-------------+-----------+
|         jose|          a|
|           li|          b|
|          sam|          c|
+-------------+-----------+
+-------------+-----------+
|i.like.cheese|yummy.stuff|
+-------------+-----------+
|         jose|          a|
|           li|          b|
|          sam|          c|
+-------------+-----------+
此代码将出错:

def dots_to_underscores(s):
    return s.replace(".", "_")

df.transform(quinn.with_columns_renamed(dots_to_underscores))
以下是错误消息:
pyspark.sql.utils.AnalysisException:“无法解析给定输入列的“
i.like.cheese
”[i.like.cheese,yummy.stuff];\n'Project['i.like.cheese AS i_like#cheese#242,'yummy.stuff AS yummy#243]\n+-LogicalRDD[i.like.cheese#231,yummy.stuff#232],false\n”


如何修改此解决方案以适用于带有点的列名?我还假设Catalyst优化器对多个
WithColumnRename
调用的优化问题与对多个
withColumn
调用的优化问题相同。让我知道Catalyst是否因为某些原因能够更好地处理多个列重命名的
调用。

尝试使用`转义

import pyspark.sql.functions as F

def with_columns_renamed(fun):
    def _(df):
        cols = list(map(
            lambda col_name: F.col("`{0}`".format(col_name)).alias(fun(col_name)),
            df.columns
        ))
        return df.select(*cols)
    return _
或者将列重命名后的
一起使用

from functools import reduce

reduce(lambda new_df, col: new_df.withColumnRenamed(col,col.replace('.','_')),df.columns,df)

你可以做一些像这样简单的事情

import pyspark.sql.functions as F

def with_columns_renamed(fun):
    def _(df):
        cols = list(map(
            lambda col_name: F.col('`' + col_name + '`').alias(fun(col_name)),
            df.columns
        ))
        return df.select(*cols)
    return _

@murtihash-使用
reduce
withColumnRenamed
是有效的,但我特别尝试避免这种方法。转义方法听起来很有希望,请随意用一段有效的代码段来写一个答案。我明白了,只要读一下那篇文章,这很有趣,这么多withColumn调用可能会导致瓶颈。您可以坚持使用“在地图中使用选择”进行转义