web-dev-qa-db-fra.com

Comment rendre Apache Spark reproductibles)

J'ai passé pas mal de temps à lire certaines questions avec les balises pyspark et spark-dataframe et très souvent, je trouve que les affiches ne fournissent pas suffisamment d'informations pour vraiment comprendre leur question. Je commente généralement en leur demandant de publier un MCVE mais parfois leur faire afficher des exemples de données d'entrée/sortie, c'est comme se tirer des dents. Par exemple: voir les commentaires sur cette question .

Peut-être qu'une partie du problème est que les gens ne savent tout simplement pas comment créer facilement un MCVE pour des cadres de données spark. Je pense qu'il serait utile d'avoir une version spark-dataframe de this pandas question comme guide qui peut être lié.

Alors, comment peut-on créer un bon exemple reproductible?

55
pault

Fournissez de petits échantillons de données, qui peuvent être facilement recréés.

À tout le moins, les affiches devraient fournir quelques lignes et colonnes sur leur trame de données et leur code qui peuvent être utilisées pour les créer facilement. Par simple, je veux dire couper-coller. Faites-le aussi petit que possible pour démontrer votre problème.


J'ai la trame de données suivante:

+-----+---+-----+----------+
|index|  X|label|      date|
+-----+---+-----+----------+
|    1|  1|    A|2017-01-01|
|    2|  3|    B|2017-01-02|
|    3|  5|    A|2017-01-03|
|    4|  7|    B|2017-01-04|
+-----+---+-----+----------+

qui peut être créé avec ce code:

df = sqlCtx.createDataFrame(
    [
        (1, 1, 'A', '2017-01-01'),
        (2, 3, 'B', '2017-01-02'),
        (3, 5, 'A', '2017-01-03'),
        (4, 7, 'B', '2017-01-04')
    ],
    ('index', 'X', 'label', 'date')
)

Affiche la sortie souhaitée.

Posez votre question spécifique et montrez-nous la sortie souhaitée.


Comment puis-je créer une nouvelle colonne'is_divisible' qui a de la valeur'yes' si le jour du mois du'date' plus 7 jours est divisible par la valeur dans la colonne'X', et'no' sinon?

Sortie souhaitée:

+-----+---+-----+----------+------------+
|index|  X|label|      date|is_divisible|
+-----+---+-----+----------+------------+
|    1|  1|    A|2017-01-01|         yes|
|    2|  3|    B|2017-01-02|         yes|
|    3|  5|    A|2017-01-03|         yes|
|    4|  7|    B|2017-01-04|          no|
+-----+---+-----+----------+------------+

Expliquez comment obtenir votre sortie.

Expliquez en détail comment vous obtenez la sortie souhaitée. Cela aide à montrer un exemple de calcul.


Par exemple dans la ligne 1, le X = 1 et la date = 2017-01-01. L'ajout de 7 jours à ce jour donne le 01-01-2017. Le jour du mois est 8 et comme 8 est divisible par 1, la réponse est 'oui'.

De même, pour la dernière ligne X = 7 et la date = 2017-01-04. L'ajout de 7 à la date donne 11 comme jour du mois. Puisque 11% 7 n'est pas 0, la réponse est "non".


Partagez votre code existant.

Montrez-nous ce que vous avez fait ou essayé, y compris tout * du code même si cela ne fonctionne pas. Dites-nous où vous êtes bloqué et si vous recevez une erreur, veuillez inclure le message d'erreur.

(* Vous pouvez omettre le code pour créer le contexte spark, mais vous devez inclure toutes les importations.)


Je sais comment ajouter une nouvelle colonne qui estdate plus 7 jours mais j'ai du mal à obtenir le jour du mois sous forme d'entier.

from pyspark.sql import functions as f
df.withColumn("next_week", f.date_add("date", 7))

Inclure les versions, les importations et utiliser la coloration syntaxique


Pour les messages d'optimisation des performances, incluez le plan d'exécution

  • Tous les détails dans cette réponse écrit par ser8371915 .
  • Il aide à utiliser des noms standardisés pour les contextes.

Analyse spark fichiers de sortie

  • Max fourni du code utile dans cette réponse pour aider à analyser Spark fichiers de sortie dans un DataFrame.

Autres notes.

50
pault

L'optimisation des performances

Si la question est liée au réglage des performances, veuillez inclure les informations suivantes.

Plan d'exécution

Il est préférable d'inclure plan d'exécution étend. En Python:

df.explain(True) 

À Scala:

df.explain(true)

ou plan d'exécution étendu avec statistiques. En Python:

print(df._jdf.queryExecution().stringWithStats())

à Scala:

df.queryExecution.stringWithStats

Informations sur le mode et le cluster

  • mode - local, client, `cluster.
  • Gestionnaire de cluster (le cas échéant) - aucun (mode local), autonome, YARN, Mesos, Kubernetes.
  • Informations de configuration de base (nombre de cœurs, mémoire de l'exécuteur).

Informations sur le timing

lent est relatif, en particulier lorsque vous portez une application non distribuée ou que vous vous attendez à une faible latence. Les horaires exacts pour différentes tâches et étapes peuvent être récupérés à partir de Spark UI (sc.uiWebUrl) jobs ou Spark REST UI.

Utiliser des noms standardisés pour les contextes

L'utilisation de noms établis pour chaque contexte nous permet de reproduire rapidement le problème.

  • sc - pour SparkContext.
  • sqlContext - pour SQLContext.
  • spark - pour SparkSession.

Fournissez les informations de type (Scala)

L'inférence de type puissante est l'une des fonctionnalités les plus utiles de Scala, mais il est difficile d'analyser le code hors contexte. Même si le type est évident dans le contexte, il est préférable d'annoter les variables. Préférer

val lines: RDD[String] = sc.textFile("path")
val words: RDD[String] = lines.flatMap(_.split(" "))

plus de

val lines = sc.textFile("path")
val words = lines.flatMap(_.split(" "))

Les outils couramment utilisés peuvent vous aider:

  • spark-Shell/Scala Shell

    utilisation :t

    scala> val rdd = sc.textFile("README.md")
    rdd: org.Apache.spark.rdd.RDD[String] = README.md MapPartitionsRDD[1] at textFile at <console>:24
    
    scala> :t rdd
    org.Apache.spark.rdd.RDD[String]
    
  • InteliJ Idea

    Utilisation Alt + =

21
hi-zir

Bonne question et réponse; quelques suggestions supplémentaires:

Incluez votre Spark version

Spark évolue toujours, mais pas aussi rapidement qu'au temps de 1.x. C'est toujours (mais surtout si vous utilisez une version un peu plus ancienne) une bonne idée d'inclure votre version de travail. Personnellement, je commence toujours mes réponses par:

spark.version
# u'2.2.0'

ou

sc.version
# u'2.2.0'

Inclure votre Python, aussi, n'est jamais une mauvaise idée.


Inclure toutes vos importations

Si votre question ne concerne pas strictement Spark SQL & dataframes, par exemple si vous avez l'intention d'utiliser votre dataframe dans une opération d'apprentissage automatique, soyez explicite sur vos importations - voir cette question =, où les importations n'ont été ajoutées au PO qu'après un échange approfondi dans les commentaires (désormais supprimés) (et il s'est avéré que ces mauvaises importations étaient à l'origine du problème).

Pourquoi est-ce nécessaire? Parce que, par exemple, cette LDA

from pyspark.mllib.clustering import LDA

est différent de ce LDA:

from pyspark.ml.clustering import LDA

le premier provenant de l'ancienne API basée sur RDD (anciennement Spark MLlib)), tandis que le second de la nouvelle API basée sur des trames de données (Spark ML).


Inclure la mise en évidence du code

OK, j'avoue que c'est subjectif: je crois que les questions PySpark ne devraient pas être marquées comme python par défaut ; le fait est que la balise python donne automatiquement la surbrillance du code (et je crois que c'est une raison principale pour ceux qui l'utilisent pour les questions PySpark). Quoi qu'il en soit, si vous êtes d'accord et que vous souhaitez toujours un code Nice surligné, incluez simplement la directive de démarquage appropriée:

<!-- language-all: lang-python -->

quelque part dans votre message, avant votre premier extrait de code.

[MISE À JOUR: J'ai demandé mise en évidence automatique de la syntaxe pour les balises pyspark et sparkr - les votes positifs sont les bienvenus]

15
desertnaut

Cette petite fonction d'aide pourrait aider à analyser Spark fichiers de sortie dans DataFrame:

PySpark:

from pyspark.sql.functions import *

def read_spark_output(file_path):
    step1 = spark.read \
             .option("header","true") \
             .option("inferSchema","true") \
             .option("delimiter","|") \
             .option("parserLib","UNIVOCITY") \
             .option("ignoreLeadingWhiteSpace","true") \
             .option("ignoreTrailingWhiteSpace","true") \
             .option("comment","+") \
             .csv("file://{}".format(file_path))
    # select not-null columns
    step2 = t.select([c for c in t.columns if not c.startswith("_")])
    # deal with 'null' string in column
    return step2.select(*[when(~col(col_name).eqNullSafe("null"), col(col_name)).alias(col_name) for col_name in step2.columns])

Scala:

// read Spark Output Fixed width table:
def readSparkOutput(filePath: String): org.Apache.spark.sql.DataFrame = {
  val step1 = spark.read
    .option("header", "true")
    .option("inferSchema", "true")
    .option("delimiter", "|")
    .option("parserLib", "UNIVOCITY")
    .option("ignoreLeadingWhiteSpace", "true")
    .option("ignoreTrailingWhiteSpace", "true")
    .option("comment", "+")
    .csv(filePath)

  val step2 = step1.select(step1.columns.filterNot(_.startsWith("_c")).map(step1(_)): _*)

  val columns = step2.columns
  columns.foldLeft(step2)((acc, c) => acc.withColumn(c, when(col(c) =!= "null", col(c))))
}

Usage:

df = read_spark_output("file:///tmp/spark.out")

PS: Pour pyspark, eqNullSafe est disponible auprès de spark 2.3.

12
MaxU