web-dev-qa-db-fra.com

renommer les colonnes pour les agrégats pyspark dataframes

J'analyse certaines données avec pyspark dataframes. Supposons que j'ai une image de données df que je suis en train d'agréger 

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

Cela me donnera: 

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

L'agrégation fonctionne parfaitement mais je n'aime pas le nouveau nom de colonne "SUM (money # 2L)". Existe-t-il un moyen simple de renommer cette colonne en quelque chose de lisible par l'homme à partir de la méthode .agg? Peut-être que quelque chose de plus semblable à ce que l'on ferait dans dplyr

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

Bien que je préfère toujours la syntaxe dplyr , cet extrait de code fera: 

import pyspark.sql.functions as sf

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

Cela devient verbeux. 

81
cantdutchthis

withColumnRenamed devrait faire l'affaire. Voici le lien vers l'API pyspark.sql .

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

J'ai créé pour cela une petite fonction d'assistance qui pourrait aider certaines personnes.

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

Un exemple: 

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']

Faire au moins un peu pour empêcher les gens de taper autant. 

4
Aaron Gonzales
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])

Le code ci-dessus peut supprimer tout ce qui est en dehors de "()". Par exemple, "sum (foo)" sera renommé "foo".

0
Huiguorou

C'est simple comme:

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

Utilisez .as in agg pour nommer la nouvelle ligne créée.

0
kapil kumar

Considérant que vous avez un dictionnaire columns_and_operations et que, après l’agrégation, vous souhaitez renommer sans coder en dur, voici un moyen plus simple:

from functools import reduce

columns_and_operations = {
        "rank": "mean",
        "*": "count",
        "rate": "mean", 
        "price": "mean", 
         "clicks": "mean"}

df = df.groupBy("group").agg(columns_and_operations)

old_names = ["{}({})".format(v, k) for k, v in columns_and_operations.items()]
new_names = list(columns_and_operations.keys())

df = reduce(lambda df, i: df.withColumnRenamed(old_names[i],
                                               new_names[i]),
            range(len(old_names)),
            df)
0