J'utilise PySpark et j'ai une base de données Spark avec un tas de colonnes numériques. Je veux ajouter une colonne qui est la somme de toutes les autres colonnes.
Supposons que mon cadre de données comporte des colonnes "a", "b" et "c". Je sais que je peux le faire:
df.withColumn('total_col', df.a + df.b + df.c)
Le problème est que je ne veux pas taper chaque colonne individuellement et les ajouter, surtout si j'ai beaucoup de colonnes. Je veux pouvoir le faire automatiquement ou en spécifiant une liste de noms de colonnes que je veux ajouter. Y a-t-il une autre façon de faire cela?
Ce n'était pas évident. Je ne vois aucune somme basée sur les lignes des colonnes définies dans l'API spark Dataframes.
Cela peut être fait de manière assez simple:
newdf = df.withColumn('total', sum(df[col] for col in df.columns))
df.columns
est fourni par pyspark sous forme de liste de chaînes donnant tous les noms de colonnes du cadre de données Spark. Pour une somme différente, vous pouvez également fournir une autre liste de noms de colonnes.
Je n’ai pas essayé cela comme première solution car je n’étais pas certain de la manière dont cela se comporterait. Mais ça marche.
C'est trop compliqué, mais ça marche aussi.
Tu peux le faire:
df.columns
pour obtenir une liste des noms des colonnesAvec réduire de python, quelques informations sur le fonctionnement de la surcharge d'opérateur et le code pyspark des colonnes ici qui devient:
def column_add(a,b):
return a.__add__(b)
newdf = df.withColumn('total_col',
reduce(column_add, ( df[col] for col in df.columns ) ))
Notez que ceci est une réduction python, pas une réduction spark RDD, et le terme de parenthèse dans le deuxième paramètre à réduire requiert la parenthèse car il s'agit d'une expression de générateur de liste.
Testé, ça marche!
$ pyspark
>>> df = sc.parallelize([{'a': 1, 'b':2, 'c':3}, {'a':8, 'b':5, 'c':6}, {'a':3, 'b':1, 'c':0}]).toDF().cache()
>>> df
DataFrame[a: bigint, b: bigint, c: bigint]
>>> df.columns
['a', 'b', 'c']
>>> def column_add(a,b):
... return a.__add__(b)
...
>>> df.withColumn('total', reduce(column_add, ( df[col] for col in df.columns ) )).collect()
[Row(a=1, b=2, c=3, total=6), Row(a=8, b=5, c=6, total=19), Row(a=3, b=1, c=0, total=4)]
Mon problème était semblable au précédent (un peu plus complexe) car je devais ajouter des sommes de colonnes consécutives en tant que nouvelles colonnes dans le cadre de données PySpark. Cette approche utilise le code de la version 1 de Paul ci-dessus:
import pyspark
from pyspark.sql import SparkSession
import pandas as pd
spark = SparkSession.builder.appName('addColAsCumulativeSUM').getOrCreate()
df=spark.createDataFrame(data=[(1,2,3),(4,5,6),(3,2,1)\
,(6,1,-4),(0,2,-2),(6,4,1)\
,(4,5,2),(5,-3,-5),(6,4,-1)]\
,schema=['x1','x2','x3'])
df.show()
+---+---+---+
| x1| x2| x3|
+---+---+---+
| 1| 2| 3|
| 4| 5| 6|
| 3| 2| 1|
| 6| 1| -4|
| 0| 2| -2|
| 6| 4| 1|
| 4| 5| 2|
| 5| -3| -5|
| 6| 4| -1|
+---+---+---+
colnames=df.columns
ajouter de nouvelles colonnes qui sont des sommes cumulatives (consécutives):
for i in range(0,len(colnames)):
colnameLst= colnames[0:i+1]
colname = 'cm'+ str(i+1)
df = df.withColumn(colname, sum(df[col] for col in colnameLst))
df.show ()
+---+---+---+---+---+---+
| x1| x2| x3|cm1|cm2|cm3|
+---+---+---+---+---+---+
| 1| 2| 3| 1| 3| 6|
| 4| 5| 6| 4| 9| 15|
| 3| 2| 1| 3| 5| 6|
| 6| 1| -4| 6| 7| 3|
| 0| 2| -2| 0| 2| 0|
| 6| 4| 1| 6| 10| 11|
| 4| 5| 2| 4| 9| 11|
| 5| -3| -5| 5| 2| -3|
| 6| 4| -1| 6| 10| 9|
+---+---+---+---+---+---+
Les colonnes 'somme cumulative' ajoutées sont les suivantes:
cm1 = x1
cm2 = x1 + x2
cm3 = x1 + x2 + x3
La solution
newdf = df.withColumn('total', sum(df[col] for col in df.columns))
posté par @Paul travaille. Néanmoins, j'ai eu l'erreur, comme beaucoup d'autres que j'ai vu,
TypeError: 'Column' object is not callable
Après un certain temps, j'ai trouvé le problème (du moins dans mon cas). Le problème est que j’avais déjà importé certaines fonctions de pyspark avec la ligne
from pyspark.sql.functions import udf, col, count, sum, when, avg, mean, min
la ligne a donc importé la commande sum
pyspark alors que df.withColumn('total', sum(df[col] for col in df.columns))
est censé utiliser la fonction python sum
normale.
Vous pouvez supprimer la référence de la fonction pyspark avec del sum
.
Sinon, dans mon cas, j'ai modifié l'importation en
import pyspark.sql.functions as F
et ensuite référencé les fonctions en tant que F.sum
.
La façon la plus simple de le faire est d’utiliser la fonction expr
from pyspark.sql.functions import *
data = data.withColumn('total', expr("col1 + col2 + col3 + col4"))