web-dev-qa-db-fra.com

Remplacez les valeurs manquantes par la moyenne - Spark Dataframe

J'ai un Spark Dataframe avec quelques valeurs manquantes. Je voudrais effectuer une imputation simple en remplaçant les valeurs manquantes par la moyenne de cette colonne. Je suis très nouveau sur Spark, donc j'ai été du mal à mettre en œuvre cette logique. Voici ce que j'ai réussi à faire jusqu'à présent:

a) Pour ce faire pour une seule colonne (disons Col A), cette ligne de code semble fonctionner:

df.withColumn("new_Col", when($"ColA".isNull, df.select(mean("ColA"))
  .first()(0).asInstanceOf[Double])
  .otherwise($"ColA"))

b) Cependant, je n'ai pas pu comprendre comment procéder pour toutes les colonnes de mon dataframe. J'essayais la fonction Carte, mais je crois qu'elle parcourt chaque ligne d'une trame de données

c) Il y a une question similaire sur SO - ici . Et bien que j'aimais la solution (en utilisant des tableaux agrégés et la fusion), j'étais très désireux de savoir s'il y avait est un moyen de le faire en parcourant chaque colonne (je viens de R, donc parcourir chaque colonne en utilisant une fonction d'ordre supérieur comme lapply me semble plus naturel).

Merci!

12
Dataminer

Spark> = 2.2

Vous pouvez utiliser org.Apache.spark.ml.feature.Imputer (qui prend en charge les stratégies moyenne et médiane).

Scala:

import org.Apache.spark.ml.feature.Imputer

val imputer = new Imputer()
  .setInputCols(df.columns)
  .setOutputCols(df.columns.map(c => s"${c}_imputed"))
  .setStrategy("mean")

imputer.fit(df).transform(df)

Python:

from pyspark.ml.feature import Imputer

imputer = Imputer(
    inputCols=df.columns, 
    outputCols=["{}_imputed".format(c) for c in df.columns]
)
imputer.fit(df).transform(df)

Spark <2,2

Vous voilà:

import org.Apache.spark.sql.functions.mean

df.na.fill(df.columns.Zip(
  df.select(df.columns.map(mean(_)): _*).first.toSeq
).toMap)

df.columns.map(mean(_)): Array[Column] 

calcule une moyenne pour chaque colonne,

df.select(_: *).first.toSeq: Seq[Any]

collecte des valeurs agrégées et convertit la ligne en Seq[Any] (Je sais que ce n'est pas optimal mais c'est l'API avec laquelle nous devons travailler),

df.columns.Zip(_).toMap: Map[String,Any] 

crée aMap: Map[String, Any] qui correspond du nom de la colonne à sa moyenne, et enfin:

df.na.fill(_): DataFrame

remplit les valeurs manquantes en utilisant:

fill: Map[String, Any] => DataFrame 

de DataFrameNaFunctions.

Pour ingore NaN entrées, vous pouvez remplacer:

df.select(df.columns.map(mean(_)): _*).first.toSeq

avec:

import org.Apache.spark.sql.functions.{col, isnan, when}


df.select(df.columns.map(
  c => mean(when(!isnan(col(c)), col(c)))
): _*).first.toSeq
16
user6910411

Pour imputer la médiane (au lieu de la moyenne) dans PySpark <2,2

## filter numeric cols
num_cols = [col_type[0] for col_type in filter(lambda dtype: dtype[1] in {"bigint", "double", "int"}, df.dtypes)]
### Compute a dict with <col_name, median_value>
median_dict = dict()
for c in num_cols:
   median_dict[c] = df.stat.approxQuantile(c, [0.5], 0.001)[0]

Ensuite, appliquez na.fill

df_imputed = df.na.fill(median_dict)
3
noleto

Pour PySpark, voici le code que j'ai utilisé:

mean_dict = { col: 'mean' for col in df.columns }
col_avgs = df.agg( mean_dict ).collect()[0].asDict()
col_avgs = { k[4:-1]: v for k,v in col_avgs.iteritems() }
df.fillna( col_avgs ).show()

Les quatre étapes sont les suivantes:

  1. Créez le dictionnaire mean_dict Mappant les noms des colonnes à l'opération d'agrégation (moyenne)
  2. Calculez la moyenne de chaque colonne et enregistrez-la en tant que dictionnaire col_avgs
  3. Les noms de colonne dans col_avgs Commencent par avg( Et se terminent par ), Par ex. avg(col1). Retirez les parenthèses.
  4. Remplissez les colonnes de la trame de données avec les moyennes en utilisant col_avgs
2
Michael P