【问题标题】:Rename pivoted and aggregated column in PySpark Dataframe重命名 PySpark Dataframe 中的旋转和聚合列
【发布时间】:2016-06-15 13:28:07
【问题描述】:

使用如下数据框:

from pyspark.sql.functions import avg, first

rdd = sc.parallelize(
    [
        (0, "A", 223,"201603", "PORT"), 
        (0, "A", 22,"201602", "PORT"), 
        (0, "A", 422,"201601", "DOCK"), 
        (1,"B", 3213,"201602", "DOCK"), 
        (1,"B", 3213,"201601", "PORT"), 
        (2,"C", 2321,"201601", "DOCK")
    ]
)
df_data = sqlContext.createDataFrame(rdd, ["id","type", "cost", "date", "ship"])

df_data.show()

我在它上面做了一个支点,

df_data.groupby(df_data.id, df_data.type).pivot("date").agg(avg("cost"), first("ship")).show()

+---+----+----------------+--------------------+----------------+--------------------+----------------+--------------------+
| id|type|201601_avg(cost)|201601_first(ship)()|201602_avg(cost)|201602_first(ship)()|201603_avg(cost)|201603_first(ship)()|
+---+----+----------------+--------------------+----------------+--------------------+----------------+--------------------+
|  2|   C|          2321.0|                DOCK|            null|                null|            null|                null|
|  0|   A|           422.0|                DOCK|            22.0|                PORT|           223.0|                PORT|
|  1|   B|          3213.0|                PORT|          3213.0|                DOCK|            null|                null|
+---+----+----------------+--------------------+----------------+--------------------+----------------+--------------------+

但是我得到了这些非常复杂的列名称。在聚合上应用alias 通常可以工作,但由于在这种情况下pivot 名称更糟:

+---+----+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+
| id|type|201601_(avg(cost),mode=Complete,isDistinct=false) AS cost#1619|201601_(first(ship)(),mode=Complete,isDistinct=false) AS ship#1620|201602_(avg(cost),mode=Complete,isDistinct=false) AS cost#1619|201602_(first(ship)(),mode=Complete,isDistinct=false) AS ship#1620|201603_(avg(cost),mode=Complete,isDistinct=false) AS cost#1619|201603_(first(ship)(),mode=Complete,isDistinct=false) AS ship#1620|
+---+----+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+
|  2|   C|                                                        2321.0|                                                              DOCK|                                                          null|                                                              null|                                                          null|                                                              null|
|  0|   A|                                                         422.0|                                                              DOCK|                                                          22.0|                                                              PORT|                                                         223.0|                                                              PORT|
|  1|   B|                                                        3213.0|                                                              PORT|                                                        3213.0|                                                              DOCK|                                                          null|                                                              null|
+---+----+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+--------------------------------------------------------------+------------------------------------------------------------------+ 

有没有办法在数据透视和聚合时动态重命名列名?

【问题讨论】:

    标签: python apache-spark pyspark apache-spark-sql


    【解决方案1】:

    一个简单的正则表达式就可以解决问题:

    import re
    
    def clean_names(df):
        p = re.compile("^(\w+?)_([a-z]+)\((\w+)\)(?:\(\))?")
        return df.toDF(*[p.sub(r"\1_\3", c) for c in df.columns])
    
    pivoted = df_data.groupby(...).pivot(...).agg(...)
    
    clean_names(pivoted).printSchema()
    ## root
    ##  |-- id: long (nullable = true)
    ##  |-- type: string (nullable = true)
    ##  |-- 201601_cost: double (nullable = true)
    ##  |-- 201601_ship: string (nullable = true)
    ##  |-- 201602_cost: double (nullable = true)
    ##  |-- 201602_ship: string (nullable = true)
    ##  |-- 201603_cost: double (nullable = true)
    ##  |-- 201603_ship: string (nullable = true)
    

    如果您想保留函数名称,请将替换模式更改为例如\1_\2_\3

    【讨论】:

    • 当你有很多列时,更重要的是,当你事先不知道列名时,例如由于某些 ETL 操作,您必须使用@zero323 方法
    【解决方案2】:

    一种简单的方法是在聚合函数之后使用别名。 我从您创建的 df_data spark dataFrame 开始。

    df_data.groupby(df_data.id, df_data.type).pivot("date").agg(avg("cost").alias("avg_cost"), first("ship").alias("first_ship")).show()
    +---+----+---------------+-----------------+---------------+-----------------+---------------+-----------------+
    | id|type|201601_avg_cost|201601_first_ship|201602_avg_cost|201602_first_ship|201603_avg_cost|201603_first_ship|
    +---+----+---------------+-----------------+---------------+-----------------+---------------+-----------------+
    |  1|   B|         3213.0|             PORT|         3213.0|             DOCK|           null|             null|
    |  2|   C|         2321.0|             DOCK|           null|             null|           null|             null|
    |  0|   A|          422.0|             DOCK|           22.0|             PORT|          223.0|             PORT|
    +---+----+---------------+-----------------+---------------+-----------------+---------------+-----------------+
    

    列名将采用“original_column_name_aliased_column_name”的形式。对于您的情况,original_column_name 将为 201601,aliased_column_name 将为 avg_cost,列名称为 201601_avg_cost(由下划线“_”链接)。

    【讨论】:

    • 这仅在您有超过 1 个聚合时才有效。就我而言,我选择了该列并在枢轴之前对其进行了转换。 IE。 df_data.select(($"date" + "_avg_cost").as("date"), $"cost")).pivot("date").agg(avg("cost"))
    • 如何在aggalias 时将此下划线更改为空格?
    【解决方案3】:

    您可以直接为聚合设置别名:

    pivoted = df_data \
        .groupby(df_data.id, df_data.type) \
        .pivot("date") \
        .agg(
           avg('cost').alias('cost'),
           first("ship").alias('ship')
        )
    
    pivoted.printSchema()
    ##root
    ##|-- id: long (nullable = true)
    ##|-- type: string (nullable = true)
    ##|-- 201601_cost: double (nullable = true)
    ##|-- 201601_ship: string (nullable = true)
    ##|-- 201602_cost: double (nullable = true)
    ##|-- 201602_ship: string (nullable = true)
    ##|-- 201603_cost: double (nullable = true)
    ##|-- 201603_ship: string (nullable = true)
    

    【讨论】:

    • 我认为这应该是公认的答案。
    【解决方案4】:

    为此编写了一个简单快速的函数。享受! :)

    # This function efficiently rename pivot tables' urgly names
    def rename_pivot_cols(rename_df, remove_agg):
        """change spark pivot table's default ugly column names at ease.
            Option 1: remove_agg = True: `2_sum(sum_amt)` --> `sum_amt_2`.
            Option 2: remove_agg = False: `2_sum(sum_amt)` --> `sum_sum_amt_2`
        """
        for column in rename_df.columns:
            if remove_agg == True:
                start_index = column.find('(')
                end_index = column.find(')')
                if (start_index > 0 and end_index > 0):
                    rename_df = rename_df.withColumnRenamed(column, column[start_index+1:end_index]+'_'+column[:1])
            else:
                new_column = column.replace('(','_').replace(')','')
                rename_df = rename_df.withColumnRenamed(column, new_column[2:]+'_'+new_column[:1])   
        return rename_df
    

    【讨论】:

      【解决方案5】:

      从 zero323 的修改版本,适用于 spark 2.4

      import re
      
      def clean_names(df):
          p = re.compile("^(\w+?)_([a-z]+)\((\w+)(,\s\w+)\)(:\s\w+)?")
          return df.toDF(*[p.sub(r"\1_\3", c) for c in df.columns])
      

      当前列名类似于0_first(is_flashsale, false): int

      【讨论】:

        猜你喜欢
        • 2015-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-09
        • 1970-01-01
        • 2016-09-23
        • 2016-12-23
        • 2017-05-30
        相关资源
        最近更新 更多