web-dev-qa-db-fra.com

Spark ajoute une nouvelle colonne à dataframe avec la valeur de la ligne précédente

Je me demande comment je peux réaliser ce qui suit dans Spark (Pyspark)

Dataframe initial:

+--+---+
|id|num|
+--+---+
|4 |9.0|
+--+---+
|3 |7.0|
+--+---+
|2 |3.0|
+--+---+
|1 |5.0|
+--+---+

Dataframe résultant:

+--+---+-------+
|id|num|new_Col|
+--+---+-------+
|4 |9.0|  7.0  |
+--+---+-------+
|3 |7.0|  3.0  |
+--+---+-------+
|2 |3.0|  5.0  |
+--+---+-------+

Je parviens généralement à "ajouter" de nouvelles colonnes à un cadre de données en utilisant quelque chose comme: df.withColumn("new_Col", df.num * 10)

Cependant, je n'ai aucune idée de la manière dont je peux réaliser ce "décalage de lignes" pour la nouvelle colonne, de sorte que la nouvelle colonne ait la valeur d'un champ de la ligne précédente (comme dans l'exemple). Je n'ai également rien trouvé dans la documentation de l'API sur la manière d'accéder à une certaine ligne d'un DF par index.

Toute aide serait appréciée.

26
Kito

Vous pouvez utiliser la fonction de fenêtre lag comme suit 

from pyspark.sql.functions import lag, col
from pyspark.sql.window import Window

df = sc.parallelize([(4, 9.0), (3, 7.0), (2, 3.0), (1, 5.0)]).toDF(["id", "num"])
w = Window().partitionBy().orderBy(col("id"))
df.select("*", lag("num").over(w).alias("new_col")).na.drop().show()

## +---+---+-------+
## | id|num|new_col|
## +---+---+-------|
## |  2|3.0|    5.0|
## |  3|7.0|    3.0|
## |  4|9.0|    7.0|
## +---+---+-------+

mais il y a quelques points importants:

  1. si vous avez besoin d'une opération globale (non partitionnée par une autre colonne/colonnes), cela est extrêmement inefficace.
  2. vous avez besoin d'un moyen naturel pour commander vos données. 

Alors que le second problème n’est presque jamais un problème, le premier peut être un facteur décisif. Si tel est le cas, convertissez simplement votre DataFrame en RDD et calculez lag manuellement. Voir par exemple:

Autres liens utiles:

30
zero323
   val df = sc.parallelize(Seq((4, 9.0), (3, 7.0), (2, 3.0), (1, 5.0))).toDF("id", "num")
df.show
+---+---+
| id|num|
+---+---+
|  4|9.0|
|  3|7.0|
|  2|3.0|
|  1|5.0|
+---+---+
df.withColumn("new_column", lag("num", 1, 0).over(w)).show
+---+---+----------+
| id|num|new_column|
+---+---+----------+
|  1|5.0|       0.0|
|  2|3.0|       5.0|
|  3|7.0|       3.0|
|  4|9.0|       7.0|
+---+---+----------+
0
mputha