web-dev-qa-db-fra.com

Ajout d'une colonne de lignes sur une liste de colonnes dans Spark Dataframe

J'ai un dataframe Spark avec plusieurs colonnes. Je veux ajouter une colonne à la structure de données qui représente la somme d'un certain nombre de colonnes. 

Par exemple, mes données ressemblent à ceci:

ID var1 var2 var3 var4 var5
a   5     7    9    12   13
b   6     4    3    20   17
c   4     9    4    6    9
d   1     2    6    8    1

Je veux une colonne ajoutée qui résume les lignes pour des colonnes spécifiques:

ID var1 var2 var3 var4 var5   sums
a   5     7    9    12   13    46
b   6     4    3    20   17    50
c   4     9    4    6    9     32
d   1     2    6    8    10    27

Je sais qu'il est possible d'ajouter des colonnes si vous connaissez les colonnes spécifiques à ajouter: 

val newdf = df.withColumn("sumofcolumns", df("var1") + df("var2"))

Mais est-il possible de passer une liste de noms de colonnes et de les additionner? Basé sur cette réponse, qui est fondamentalement ce que je veux mais utilise l’API de Python au lieu de scala ( Ajouter la somme des colonnes comme nouvelle colonne dans PySpark dataframe

//Select columns to sum
val columnstosum = ("var1", "var2","var3","var4","var5")

// Create new column called sumofcolumns which is sum of all columns listed in columnstosum
val newdf = df.withColumn("sumofcolumns", df.select(columstosum.head, columnstosum.tail: _*).sum)

La valeur d'erreur sum n'est pas membre d'org.Apache.spark.sql.DataFrame. Y a-t-il un moyen de faire la somme sur les colonnes?

Merci d'avance pour votre aide. 

16
Sarah

Vous devriez essayer ce qui suit:

import org.Apache.spark.sql.functions._

val sc: SparkContext = ...
val sqlContext = new SQLContext(sc)

import sqlContext.implicits._

val input = sc.parallelize(Seq(
  ("a", 5, 7, 9, 12, 13),
  ("b", 6, 4, 3, 20, 17),
  ("c", 4, 9, 4, 6 , 9),
  ("d", 1, 2, 6, 8 , 1)
)).toDF("ID", "var1", "var2", "var3", "var4", "var5")

val columnsToSum = List(col("var1"), col("var2"), col("var3"), col("var4"), col("var5"))

val output = input.withColumn("sums", columnsToSum.reduce(_ + _))

output.show()

Alors le résultat est:

+---+----+----+----+----+----+----+
| ID|var1|var2|var3|var4|var5|sums|
+---+----+----+----+----+----+----+
|  a|   5|   7|   9|  12|  13|  46|
|  b|   6|   4|   3|  20|  17|  50|
|  c|   4|   9|   4|   6|   9|  32|
|  d|   1|   2|   6|   8|   1|  18|
+---+----+----+----+----+----+----+
29
Paweł Jurczenko

Clair et simple:

import org.Apache.spark.sql.Column
import org.Apache.spark.sql.functions.{lit, col}

def sum_(cols: Column*) = cols.foldLeft(lit(0))(_ + _)

val columnstosum = Seq("var1", "var2", "var3", "var4", "var5").map(col _)
df.select(sum_(columnstosum: _*))

avec équivalent Python:

from functools import reduce
from operator import add
from pyspark.sql.functions import lit, col

def sum_(*cols):
    return reduce(add, cols, lit(0))

columnstosum = [col(x) for x in ["var1", "var2", "var3", "var4", "var5"]]
select("*", sum_(*columnstosum))

La valeur par défaut est NA si une valeur est manquante dans la ligne. Vous pouvez utiliser la fonction DataFrameNaFunctions.fill ou coalesce pour éviter cela.

8
zero323

Je suppose que vous avez une df dataframe. Ensuite, vous pouvez résumer tous les cols à l'exception de votre col d'identité. Ceci est utile lorsque vous avez plusieurs colonnes et que vous ne voulez pas mentionner manuellement les noms de toutes les colonnes, comme toutes les colonnes mentionnées ci-dessus. Ce post a la même réponse. 

val sumAll = df.columns.collect{ case x if x != "ID" => col(x) }.reduce(_ + _)
df.withColumn("sum", sumAll)
2
Abu Shoeb

Voici une solution élégante utilisant python:

NewDF = OldDF.withColumn('sums', sum(OldDF[col] for col in OldDF.columns[1:]))

Espérons que cela va influencer quelque chose de similaire dans Spark ... ça vous tente?.

0
Aerianis