重命名pyspark数据框聚合的列


70

我正在使用pyspark数据帧分析一些数据,假设df我正在聚合一个数据帧:

(df.groupBy("group")
   .agg({"money":"sum"})
   .show(100))

这会给我:

group                SUM(money#2L)
A                    137461285853
B                    172185566943
C                    271179590646

聚合工作正常,但我不喜欢新的列名“ SUM(money#2L)”。是否有一种巧妙的方法可以将该列重命名为该方法可以人工读取的内容.agg?也许更类似于以下内容dplyr

df %>% group_by(group) %>% summarise(sum_money = sum(money))

Answers:


133

尽管我仍然更喜欢dplyr语法,但是此代码段可以做到:

import pyspark.sql.functions as sf

(df.groupBy("group")
   .agg(sf.sum('money').alias('money'))
   .show(100))

它变得冗长。


12
对于复制粘贴此alias部分但看不到影响的其他任何人,请注意括号。alias('string')存在agg,否则您将为整个DataFrame加上别名,而不仅仅是该列。
矩阵异常

64

withColumnRenamed应该可以。这是pyspark.sql API的链接。

df.groupBy("group")\
  .agg({"money":"sum"})\
  .withColumnRenamed("SUM(money)", "money")
  .show(100)

7

它很简单:

 val maxVideoLenPerItemDf = requiredItemsFiltered.groupBy("itemId").agg(max("playBackDuration").as("customVideoLength"))
maxVideoLenPerItemDf.show()

.as在agg中使用以命名创建的新行。


4
从PySpark 2.4.0开始,.as('new_name')应当为.alias('new_name')
RyanLeiTaiwan

6

为此,我做了一些辅助功能,可能会帮助一些人。

import re

from functools import partial

def rename_cols(agg_df, ignore_first_n=1):
    """changes the default spark aggregate names `avg(colname)` 
    to something a bit more useful. Pass an aggregated dataframe
    and the number of aggregation columns to ignore.
    """
    delimiters = "(", ")"
    split_pattern = '|'.join(map(re.escape, delimiters))
    splitter = partial(re.split, split_pattern)
    split_agg = lambda x: '_'.join(splitter(x))[0:-ignore_first_n]
    renamed = map(split_agg, agg_df.columns[ignore_first_n:])
    renamed = zip(agg_df.columns[ignore_first_n:], renamed)
    for old, new in renamed:
        agg_df = agg_df.withColumnRenamed(old, new)
    return agg_df

一个例子:

gb = (df.selectExpr("id", "rank", "rate", "price", "clicks")
 .groupby("id")
 .agg({"rank": "mean",
       "*": "count",
       "rate": "mean", 
       "price": "mean", 
       "clicks": "mean", 
       })
)

>>> gb.columns
['id',
 'avg(rate)',
 'count(1)',
 'avg(price)',
 'avg(rank)',
 'avg(clicks)']

>>> rename_cols(gb).columns
['id',
 'avg_rate',
 'count_1',
 'avg_price',
 'avg_rank',
 'avg_clicks']

这样做至少可以避免人们打字太多。


2
非常有用和及时。我正要问同样的问题。如果您可以在aggdict中指定一个新的列名(在我的意思是Spark内),那就太好了。
埃文·扎米尔

@EvanZamir谢谢!我可能会尝试为此做一个简单的PR。
binaryaaron

您可以通过简单地重命名df = df.toDF(*newColumnNames),从而newColumnNames保留DataFrame(df)的所有列名称:)
Markus

3
import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *

spark = SparkSession.builder.appName('test').getOrCreate()
data = [(1, "siva", 100), (2, "siva2", 200),(3, "siva3", 300),(4, "siva4", 400),(5, "siva5", 500)]
schema = ['id', 'name', 'sallary']

df = spark.createDataFrame(data, schema=schema)
df.show()
+---+-----+-------+
| id| name|sallary|
+---+-----+-------+
|  1| siva|    100|
|  2|siva2|    200|
|  3|siva3|    300|
|  4|siva4|    400|
|  5|siva5|    500|
+---+-----+-------+


**df.agg({"sallary": "max"}).withColumnRenamed('max(sallary)', 'max').show()**
+---+
|max|
+---+
|500|
+---+

2
df = df.groupby('Device_ID').agg(aggregate_methods)
for column in df.columns:
    start_index = column.find('(')
    end_index = column.find(')')
    if (start_index and end_index):
        df = df.withColumnRenamed(column, column[start_index+1:end_index])

上面的代码可以去除“()”之外的任何内容。例如,“ sum(foo)”将重命名为“ foo”。


2

虽然先前给出的答案很好,但我认为它们缺乏一种巧妙的方式来处理字典中的字典用法。 .agg()

如果您要使用一个dict,由于您有数百个列,它实际上也可能是动态生成的,因此可以使用以下命令而无需处理许多代码行:

# Your dictionary-version of using the .agg()-function
# Note: The provided logic could actually also be applied to a non-dictionary approach
df = df.groupBy("group")\
   .agg({
          "money":"sum"
        , "...":  "..."
    })

# Now do the renaming
newColumnNames = ["group", "money", "..."] # Provide the names for ALL columns of the new df
df = df.toDF(*newColumnNames)              # Do the renaming

当然,newColumnNames-list也可以动态生成。例如,如果仅将聚合中的列追加到您的表中,则df可以预先存储newColumnNames = df.columns,然后仅追加其他名称。
无论如何,请注意newColumnNames必须包含.toDF()数据框的所有列名称,而不仅仅是要重命名的所有列名称(因为由于Sparks不可变的RDD而创建了新的数据框)!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.