web-dev-qa-db-fra.com

Mise à jour d'une colonne dataframe dans spark

En regardant la nouvelle api spark dataframe, il est difficile de savoir s'il est possible de modifier les colonnes dataframe.

Comment pourrais-je modifier une valeur dans la ligne x colonne y d'une trame de données?

Dans pandas ce serait df.ix[x,y] = new_value

Éditer: en consolidant ce qui a été dit ci-dessous, vous ne pouvez pas modifier le cadre de données existant car il est immuable, mais vous pouvez renvoyer un nouveau cadre de données avec les modifications souhaitées.

Si vous souhaitez simplement remplacer une valeur dans une colonne en fonction d'une condition, comme np.where:

from pyspark.sql import functions as F

update_func = (F.when(F.col('update_col') == replace_val, new_value)
                .otherwise(F.col('update_col')))
df = df.withColumn('new_column_name', update_func)

Si vous souhaitez effectuer une opération sur une colonne et créer une nouvelle colonne qui est ajoutée à la structure de données:

import pyspark.sql.functions as F
import pyspark.sql.types as T

def my_func(col):
    do stuff to column here
    return transformed_value

# if we assume that my_func returns a string
my_udf = F.UserDefinedFunction(my_func, T.StringType())

df = df.withColumn('new_column_name', my_udf('update_col'))

Si vous souhaitez que la nouvelle colonne porte le même nom que l'ancienne, vous pouvez ajouter l'étape supplémentaire suivante:

df = df.drop('update_col').withColumnRenamed('new_column_name', 'update_col')
61
Luke

Bien que vous ne puissiez pas modifier une colonne en tant que telle, vous pouvez agir sur une colonne et renvoyer un nouveau DataFrame reflétant ce changement. Pour cela, vous devez d'abord créer une UserDefinedFunction mettant en œuvre l'opération à appliquer, puis appliquer cette fonction de manière sélective à la colonne ciblée uniquement. En Python:

from pyspark.sql.functions import UserDefinedFunction
from pyspark.sql.types import StringType

name = 'target_column'
udf = UserDefinedFunction(lambda x: 'new_value', StringType())
new_df = old_df.select(*[udf(column).alias(name) if column == name else column for column in old_df.columns])

new_df a maintenant le même schéma que old_df (en supposant que old_df.target_column était de type StringType également) mais toutes les valeurs de la colonne target_column seront new_value.

62
karlson

Généralement, lors de la mise à jour d'une colonne, nous voulons mapper une ancienne valeur sur une nouvelle valeur. Voici un moyen de faire cela dans pyspark sans UDF:

# update df[update_col], mapping old_value --> new_value
from pyspark.sql import functions as F
df = df.withColumn(update_col,
    F.when(df[update_col]==old_value,new_value).
    otherwise(df[update_col])).
38
Paul

DataFrames sont basés sur des RDD. Les RDD sont des structures immuables et ne permettent pas de mettre à jour des éléments sur site. Pour modifier les valeurs, vous devez créer un nouveau DataFrame en transformant celui-ci à l'aide d'opérations DSL ou RDD de type SQL, telles que map.

Un jeu de diapositives hautement recommandé: Présentation de DataFrames dans Spark pour la science des données à grande échelle .

13
maasg

Tout comme maasg indique que vous pouvez créer un nouveau DataFrame à partir du résultat d'une carte appliquée à l'ancien DataFrame. Un exemple pour un DataFrame df donné avec deux lignes:

val newDf = sqlContext.createDataFrame(df.map(row => 
  Row(row.getInt(0) + SOMETHING, applySomeDef(row.getAs[Double]("y")), df.schema)

Notez que si les types de colonnes changent, vous devez lui donner un schéma correct au lieu de df.schema. Consultez l'API de org.Apache.spark.sql.Row pour connaître les méthodes disponibles: https://spark.Apache.org/docs/latest/api/Java/org/Apache/spark/sql/Row.html

[Mise à jour] Ou en utilisant des FDU dans Scala:

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

val toLong = udf[Long, String] (_.toLong)

val modifiedDf = df.withColumn("modifiedColumnName", toLong(df("columnName"))).drop("columnName")

et si le nom de la colonne doit rester le même, vous pouvez le renommer:

modifiedDf.withColumnRenamed("modifiedColumnName", "columnName")
11
radek1st